wallBoundedParticle.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2017-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "wallBoundedParticle.H"
30 
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32 
34 (
35  sizeof(wallBoundedParticle) - sizeof(particle)
36 );
37 
38 
39 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
40 
42 {
43  if ((meshEdgeStart_ != -1) == (diagEdge_ != -1))
44  {
46  << "Particle:"
47  << info()
48  << "cannot both be on a mesh edge and a face-diagonal edge."
49  << " meshEdgeStart_:" << meshEdgeStart_
50  << " diagEdge_:" << diagEdge_
51  << abort(FatalError);
52  }
53 
54  const Foam::face& f = mesh().faces()[tetFace()];
55 
56  if (meshEdgeStart_ != -1)
57  {
58  return edge(f[meshEdgeStart_], f.nextLabel(meshEdgeStart_));
59  }
60  else
61  {
62  label faceBasePti = mesh().tetBasePtIs()[tetFace()];
63  if (faceBasePti == -1)
64  {
65  //FatalErrorInFunction
66  //WarningInFunction
67  // << "No base point for face " << tetFace() << ", " << f
68  // << ", produces a decomposition that has a minimum "
69  // << "volume greater than tolerance."
70  // //<< abort(FatalError);
71  // << endl;
72  faceBasePti = 0;
73  }
74 
75  label diagPti = (faceBasePti + diagEdge_)%f.size();
76 
77  return edge(f[faceBasePti], f[diagPti]);
78  }
79 }
80 
81 
83 (
84  const label& celli,
85  label& tetFacei,
86  label& tetPti,
87  const edge& e
88 )
89 {
90  const faceList& pFaces = mesh().faces();
91  const cellList& pCells = mesh().cells();
92 
93  const Foam::face& f = pFaces[tetFacei];
94 
95  const Foam::cell& thisCell = pCells[celli];
96 
97  for (const label facei : thisCell)
98  {
99  // Loop over all other faces of this cell and
100  // find the one that shares this edge
101 
102  if (tetFacei == facei)
103  {
104  continue;
105  }
106 
107  const Foam::face& otherFace = pFaces[facei];
108 
109  const auto edDir = otherFace.edgeDirection(e);
110 
111  if (edDir == 0)
112  {
113  continue;
114  }
115  else if (f == pFaces[facei])
116  {
117  // This is a necessary condition if using duplicate baffles
118  // (so coincident faces). We need to make sure we don't cross into
119  // the face with the same vertices since we might enter a tracking
120  // loop where it never exits. This test should be cheap
121  // for most meshes so can be left in for 'normal' meshes.
122  continue;
123  }
124  else
125  {
126  //Found edge on other face
127  tetFacei = facei;
128 
129  label eIndex = -1;
130 
131  if (edDir > 0)
132  {
133  // Edge is in the forward circulation of this face, so
134  // work with the start point of the edge
135  eIndex = otherFace.find(e.start());
136  }
137  else
138  {
139  // (edge-direction < 0) - edge is in the reverse
140  // circulation of this face, so work with the end
141  // point of the edge
142  eIndex = otherFace.find(e.end());
143  }
144 
145  label tetBasePtI = mesh().tetBasePtIs()[facei];
146 
147  if (tetBasePtI == -1)
148  {
149  //FatalErrorInFunction
150  //WarningInFunction
151  // << "No base point for face " << fI << ", " << f
152  // << ", produces a decomposition that has a minimum "
153  // << "volume greater than tolerance."
154  // //<< abort(FatalError);
155  // << endl;
156  tetBasePtI = 0;
157  }
158 
159  // Find eIndex relative to the base point on new face
160  eIndex -= tetBasePtI;
161 
162  if (neg(eIndex))
163  {
164  eIndex = (eIndex + otherFace.size()) % otherFace.size();
165  }
166 
167  if (eIndex == 0)
168  {
169  // The point is the base point, so this is first tet
170  // in the face circulation
171  tetPti = 1;
172  }
173  else if (eIndex == otherFace.size() - 1)
174  {
175  // The point is the last before the base point, so
176  // this is the last tet in the face circulation
177  tetPti = otherFace.size() - 2;
178  }
179  else
180  {
181  tetPti = eIndex;
182  }
184  break;
185  }
186  }
187 }
188 
189 
190 void Foam::wallBoundedParticle::crossEdgeConnectedFace(const edge& meshEdge)
191 {
192  // Update tetFace, tetPt
193  crossEdgeConnectedFace(cell(), tetFace(), tetPt(), meshEdge);
194 
195  // Update face to be same as tracking one
196  face() = tetFace();
197 
198  // And adapt meshEdgeStart_.
199  const Foam::face& f = mesh().faces()[tetFace()];
200  label fp = f.find(meshEdge[0]);
201 
202  if (f.nextLabel(fp) == meshEdge[1])
203  {
204  meshEdgeStart_ = fp;
205  }
206  else
207  {
208  label fpMin1 = f.rcIndex(fp);
209 
210  if (f[fpMin1] == meshEdge[1])
211  {
212  meshEdgeStart_ = fpMin1;
213  }
214  else
215  {
217  << "Problem :"
218  << " particle:"
219  << info()
220  << "face:" << tetFace()
221  << " verts:" << f
222  << " meshEdge:" << meshEdge
223  << abort(FatalError);
224  }
225  }
226 
227  diagEdge_ = -1;
228 
229  // Check that still on same mesh edge
230  const edge eNew(f[meshEdgeStart_], f.nextLabel(meshEdgeStart_));
231  if (eNew != meshEdge)
232  {
234  << "Problem" << abort(FatalError);
235  }
236 }
237 
238 
240 {
241  if (diagEdge_ == -1)
242  {
244  << "Particle:"
245  << info()
246  << "not on a diagonal edge" << abort(FatalError);
247  }
248  if (meshEdgeStart_ != -1)
249  {
251  << "Particle:"
252  << info()
253  << "meshEdgeStart_:" << meshEdgeStart_ << abort(FatalError);
254  }
255 
256  const Foam::face& f = mesh().faces()[tetFace()];
257 
258  // tetPti starts from 1, goes up to f.size()-2
259 
260  if (tetPt() == diagEdge_)
261  {
262  tetPt() = f.rcIndex(tetPt());
263  }
264  else
265  {
266  label nextTetPt = f.fcIndex(tetPt());
267  if (diagEdge_ == nextTetPt)
268  {
269  tetPt() = nextTetPt;
270  }
271  else
272  {
274  << "Particle:"
275  << info()
276  << "tetPt:" << tetPt()
277  << " diagEdge:" << diagEdge_ << abort(FatalError);
278  }
279  }
280 
281  meshEdgeStart_ = -1;
282 }
283 
284 
286 (
287  const vector& n,
288  const vector& endPosition,
289  label& minEdgei
290 )
291 {
292  // Track p from position to endPosition
293  const triFace tri(currentTetIndices().faceTriIs(mesh(), false));
294 
295  // Check which edge intersects the trajectory.
296  // Project trajectory onto triangle
297  minEdgei = -1;
298  scalar minS = 1; // end position
299 
300  edge currentE(-1, -1);
301  if (meshEdgeStart_ != -1 || diagEdge_ != -1)
302  {
303  currentE = currentEdge();
304  }
305 
306  // Determine path along line position+s*d to see where intersections are.
307  forAll(tri, i)
308  {
309  label j = tri.fcIndex(i);
310 
311  const point& pt0 = mesh().points()[tri[i]];
312  const point& pt1 = mesh().points()[tri[j]];
313 
314  if (edge(tri[i], tri[j]) == currentE)
315  {
316  // Do not check particle is on
317  continue;
318  }
319 
320  // Outwards pointing normal
321  vector edgeNormal = (pt1 - pt0)^n;
322  edgeNormal.normalise();
323 
324  // Determine whether position and end point on either side of edge.
325  scalar sEnd = (endPosition - pt0)&edgeNormal;
326  if (sEnd >= 0)
327  {
328  // endPos is outside triangle. localPosition_ should always be
329  // inside.
330  scalar sStart = (localPosition_ - pt0)&edgeNormal;
331  if (mag(sEnd - sStart) > VSMALL)
332  {
333  scalar s = sStart/(sStart - sEnd);
334 
335  if (s >= 0 && s < minS)
336  {
337  minS = s;
338  minEdgei = i;
339  }
340  }
341  }
342  }
343 
344  if (minEdgei != -1)
345  {
346  localPosition_ += minS*(endPosition - localPosition_);
347  }
348  else
349  {
350  // Did not hit any edge so tracked to the end position. Set position
351  // without any calculation to avoid truncation errors.
352  localPosition_ = endPosition;
353  minS = 1.0;
354  }
355 
356  // Project localPosition_ back onto plane of triangle
357  const point& triPt = mesh().points()[tri[0]];
358  localPosition_ -= ((localPosition_ - triPt)&n)*n;
359 
360  return minS;
361 }
362 
363 
365 (
366  const vector& n,
367  const point& endPosition
368 ) const
369 {
370  const triFace triVerts(currentTetIndices().faceTriIs(mesh(), false));
371  const edge currentE = currentEdge();
372 
373  if
374  (
375  currentE[0] == currentE[1]
376  || !triVerts.found(currentE[0])
377  || !triVerts.found(currentE[1])
378  )
379  {
381  << "Edge " << currentE << " not on triangle " << triVerts
382  << info()
383  << abort(FatalError);
384  }
385 
386 
387  const vector dir = endPosition - localPosition_;
388 
389  forAll(triVerts, i)
390  {
391  label j = triVerts.fcIndex(i);
392  const point& pt0 = mesh().points()[triVerts[i]];
393  const point& pt1 = mesh().points()[triVerts[j]];
394 
395  if (edge(triVerts[i], triVerts[j]) == currentE)
396  {
397  vector edgeNormal = (pt1-pt0)^n;
398  return (dir&edgeNormal) < 0;
399  }
400  }
401 
403  << "Problem" << abort(FatalError);
404 
405  return false;
406 }
407 
408 
409 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
410 
412 (
413  const polyMesh& mesh,
414  const point& position,
415  const label celli,
416  const label tetFacei,
417  const label tetPti,
418  const label meshEdgeStart,
419  const label diagEdge
420 )
421 :
422  particle(mesh, position, celli, tetFacei, tetPti, false),
423  localPosition_(position),
424  meshEdgeStart_(meshEdgeStart),
425  diagEdge_(diagEdge)
426 {}
427 
428 
430 (
431  const polyMesh& mesh,
432  Istream& is,
433  bool readFields,
434  bool newFormat
435 )
436 :
437  particle(mesh, is, readFields, newFormat)
438 {
439  if (readFields)
440  {
441  if (is.format() == IOstreamOption::ASCII)
442  {
444  }
445  if (!is.checkLabelSize<>() || !is.checkScalarSize<>())
446  {
447  // Non-native label or scalar size
448 
449  is.beginRawRead();
450 
451  readRawScalar(is, localPosition_.data(), vector::nComponents);
453  readRawLabel(is, &diagEdge_);
454 
455  is.endRawRead();
456  }
457  else
458  {
459  is.read(reinterpret_cast<char*>(&localPosition_), sizeofFields_);
460  }
461  }
462 
463  is.check(FUNCTION_NAME);
464 }
465 
466 
468 (
469  const wallBoundedParticle& p
470 )
471 :
472  particle(p),
473  localPosition_(p.localPosition_),
474  meshEdgeStart_(p.meshEdgeStart_),
475  diagEdge_(p.diagEdge_)
476 {}
477 
478 
479 // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
480 
481 Foam::Ostream& Foam::operator<<
482 (
483  Ostream& os,
484  const wallBoundedParticle& p
485 )
486 {
488  {
489  os << static_cast<const particle&>(p)
490  << token::SPACE << p.localPosition_
491  << token::SPACE << p.meshEdgeStart_
492  << token::SPACE << p.diagEdge_;
493  }
494  else
495  {
496  os << static_cast<const particle&>(p);
497  os.write
498  (
499  reinterpret_cast<const char*>(&p.localPosition_),
501  );
502  }
503 
504  return os;
505 }
506 
507 
508 Foam::Ostream& Foam::operator<<
509 (
510  Ostream& os,
511  const InfoProxy<wallBoundedParticle>& iproxy
512 )
513 {
514  const auto& p = *iproxy;
515 
516  tetPointRef tpr(p.currentTetIndices().tet(p.mesh()));
517 
518  os << " " << static_cast<const particle&>(p) << nl
519  << " tet:" << nl;
520  os << " ";
521  meshTools::writeOBJ(os, tpr.a());
522  os << " ";
523  meshTools::writeOBJ(os, tpr.b());
524  os << " ";
525  meshTools::writeOBJ(os, tpr.c());
526  os << " ";
527  meshTools::writeOBJ(os, tpr.d());
528  os << " l 1 2" << nl
529  << " l 1 3" << nl
530  << " l 1 4" << nl
531  << " l 2 3" << nl
532  << " l 2 4" << nl
533  << " l 3 4" << nl;
534  os << " ";
535  meshTools::writeOBJ(os, p.localPosition_);
536 
537  return os;
538 }
539 
540 
541 // ************************************************************************* //
label meshEdgeStart_
Particle is on mesh edge:
bool found(const T &val, label pos=0) const
Same as contains()
Definition: FixedList.H:747
label tetFace() const noexcept
Return current tet face particle is in.
Definition: particleI.H:134
std::enable_if< std::is_floating_point< T >::value, bool >::type checkScalarSize() const noexcept
Check if the scalar byte-size associated with the stream is the same as the given type...
Definition: IOstream.H:379
List< cell > cellList
List of cell.
Definition: cellListFwd.H:39
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
virtual Ostream & write(const char c) override
Write character.
Definition: OBJstream.C:69
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
virtual bool check(const char *operation) const
Check IOstream status for given operation.
Definition: IOstream.C:45
const labelIOList & tetBasePtIs() const
Return the tetBasePtIs.
Definition: polyMesh.C:899
tetrahedron< point, const point & > tetPointRef
A tetrahedron using referred points.
Definition: tetrahedron.H:72
An Istream is an abstract base class for all input systems (streams, files, token lists etc)...
Definition: Istream.H:57
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
"ascii" (normal default)
label fcIndex(const label i) const noexcept
Return the forward circular index, i.e. next index which returns to the first at the end of the list...
Definition: FixedListI.H:206
virtual bool endRawRead()=0
End of low-level raw binary read.
const cellList & cells() const
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition: meshTools.C:196
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept
The number of components associated with given valueTypeCode.
Definition: exprTraits.C:40
label otherFace(const primitiveMesh &mesh, const label celli, const label facei, const label edgeI)
Return face on cell using edgeI but not facei. Throws error.
Definition: meshTools.C:548
static const std::size_t sizeofFields_
Size in bytes of the fields.
static void readFields(CloudType &)
Read.
dimensionedScalar neg(const dimensionedScalar &ds)
Base particle class.
Definition: particle.H:69
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1078
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
label fcIndex(const label i) const noexcept
The forward circular index. The next index in the list which returns to the first at the end of the l...
Definition: UListI.H:97
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
virtual Istream & read(token &)=0
Return next token from stream.
dynamicFvMesh & mesh
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
Definition: edge.H:59
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const NameMatchPredicate &selectedFields, DynamicList< regIOobject *> &storedObjects)
Read the selected GeometricFields of the templated type and store on the objectRegistry.
bool isTriAlongTrack(const vector &n, const point &endPosition) const
Is current triangle in the track direction.
A triangular face using a FixedList of labels corresponding to mesh vertices.
Definition: triFace.H:65
face triFace(3)
Space [isspace].
Definition: token.H:131
point localPosition_
Particle position is updated locally as opposed to via track.
Vector< scalar > vector
Definition: vector.H:57
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1103
label diagEdge_
Particle is on diagonal edge:
errorManip< error > abort(error &err)
Definition: errorManip.H:139
label find(const T &val) const
Find index of the first occurrence of the value.
Definition: UList.C:173
Particle class that tracks on triangles of boundary faces. Use trackToEdge similar to trackToFace on ...
const Mesh & mesh() const noexcept
Return mesh.
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
OBJstream os(runTime.globalPath()/outputName)
#define FUNCTION_NAME
wallBoundedParticle(const polyMesh &c, const point &position, const label celli, const label tetFacei, const label tetPti, const label meshEdgeStart, const label diagEdge)
Construct from components.
void crossDiagonalEdge()
Cross diagonal edge into different triangle on same face,cell.
labelList f(nPoints)
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=cellModel::ref(cellModel::HEX);labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells].reset(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
label readRawLabel(Istream &is)
Read raw label from binary stream.
Definition: label.C:39
const polyMesh & mesh() const noexcept
Return the mesh database.
Definition: particleI.H:110
scalar trackFaceTri(const vector &n, const vector &endPosition, label &)
Track through single triangle.
vector point
Point is a vector.
Definition: point.H:37
A cell is defined as a list of faces with extra functionality.
Definition: cell.H:53
virtual bool beginRawRead()=0
Start of low-level raw binary read.
label n
std::enable_if< std::is_integral< T >::value, bool >::type checkLabelSize() const noexcept
Check if the label byte-size associated with the stream is the same as the given type.
Definition: IOstream.H:368
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
volScalarField & p
InfoProxy< wallBoundedParticle > info() const noexcept
Return info proxy, used to print particle information to a stream.
label rcIndex(const label i) const noexcept
The reverse circular index. The previous index in the list which returns to the last at the beginning...
Definition: UListI.H:104
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
edge currentEdge() const
Construct current edge.
streamFormat format() const noexcept
Get the current stream format.
void crossEdgeConnectedFace(const label &celli, label &tetFacei, label &tetPti, const edge &e)
Replacement for particle::crossEdgeConnectedFace that avoids bombing.