tracking.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2024 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "tracking.H"
27 #include "quadraticEqn.H"
28 #include "cubicEqn.H"
29 #include "indexedOctree.H"
30 #include "treeDataCell.H"
31 
32 #include "emptyPolyPatch.H"
33 #include "wedgePolyPatch.H"
34 #include "cyclicPolyPatch.H"
36 #include "processorPolyPatch.H"
37 
38 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
39 
40 namespace Foam
41 {
42 namespace tracking
43 {
44 
45 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
46 
47 // Tetrahedra
48 
49  //- Get the reverse transform associated with the current tet. The
50  // conversion is detA*y = (x - centre) & T. The variables x, y and
51  // centre have the same meaning as for the forward transform. T is
52  // the transposed inverse of the forward transform tensor, A,
53  // multiplied by its determinant, detA. This separation allows
54  // the barycentric tracking algorithm to function on inverted or
55  // degenerate tetrahedra.
57  (
58  const polyMesh& mesh,
59  const label celli,
60  const label facei,
61  const label faceTrii,
62  vector& centre,
63  scalar& detA,
65  );
66 
67  //- Get the reverse transformation associated with the current,
68  // moving, tet. This is of the same form as for the static case. As
69  // with the moving geometry, a function of the tracking fraction is
70  // returned for each component. The functions are higher order than
71  // for the forward transform; the determinant is cubic, and the
72  // tensor is quadratic.
74  (
75  const polyMesh& mesh,
76  const label celli,
77  const label facei,
78  const label faceTrii,
79  const scalar startStepFraction,
80  const scalar endStepFraction,
81  Pair<vector>& centre,
84  );
85 
86 
87 // Tracking
88 
89  //- The counter nTracksBehind is the number of tracks carried out that
90  // ended in a step fraction less than the maximum reached so far. Once
91  // this reaches maxNTracksBehind, tracking is abandoned for the current
92  // step.
93  //
94  // This is needed because when tetrahedra are inverted a straight
95  // trajectory can form a closed loop through regions of overlapping
96  // positive and negative space. Without this break clause, such loops can
97  // result in a valid track which never ends.
98  //
99  // Because the test is susceptible to round off error, a track of zero
100  // length will also increment the counter. As such, it is important that
101  // maxNTracksBehind is set large enough so that valid small tracks do not
102  // result in the track being abandoned. The largest number of valid small
103  // tracks that are likely to be performed sequentially is equal to the
104  // number of tetrahedra that can meet at a point. An estimate of this
105  // number is therefore used to set maxNTracksBehind.
106  static const label maxNTracksBehind = 48;
107 
108  //- See toTri. For a stationary mesh.
110  (
111  const polyMesh& mesh,
112  const vector& displacement,
113  const scalar fraction,
115  label& celli,
116  label& facei,
117  label& faceTrii,
118  scalar& stepFraction,
119  scalar& stepFractionBehind,
120  label& nTracksBehind,
121  const string& debugPrefix = NullObjectRef<string>()
122  );
123 
124  //- See toTri. Second order. For a stationary mesh.
126  (
127  const polyMesh& mesh,
128  const Pair<vector>& displacement,
129  const scalar fraction,
131  label& celli,
132  label& facei,
133  label& faceTrii,
134  scalar& stepFraction,
135  scalar& stepFractionBehind,
136  label& nTracksBehind,
137  const string& debugPrefix = NullObjectRef<string>()
138  );
139 
140  //- See toTri. For a moving mesh.
142  (
143  const polyMesh& mesh,
144  const vector& displacement,
145  const scalar fraction,
147  label& celli,
148  label& facei,
149  label& faceTrii,
150  scalar& stepFraction,
151  scalar& stepFractionBehind,
152  label& nTracksBehind,
153  const string& debugPrefix = NullObjectRef<string>()
154  );
155 
156  //- See toTri. Second order. For a moving mesh. Not implemented.
158  (
159  const polyMesh& mesh,
160  const Pair<vector>& displacement,
161  const scalar fraction,
163  label& celli,
164  label& facei,
165  label& faceTrii,
166  scalar& stepFraction,
167  scalar& stepFractionBehind,
168  label& nTracksBehind,
169  const string& debugPrefix = NullObjectRef<string>()
170  );
171 
172  //- Track along the displacement for a given fraction of the overall
173  // time-step. End when the track is complete or when a tet triangle is
174  // hit. Return the index of the tet triangle that was hit, or -1 if the
175  // end position was reached. Also return the proportion of the
176  // displacement still to be completed.
177  template<class Displacement>
179  (
180  const polyMesh& mesh,
181  const Displacement& displacement,
182  const scalar fraction,
184  label& celli,
185  label& facei,
186  label& faceTrii,
187  scalar& stepFraction,
188  scalar& stepFractionBehind,
189  label& nTracksBehind,
190  const string& debugPrefix = NullObjectRef<string>()
191  );
192 
193  //- Scale a second-order displacement
194  Pair<vector> operator*(const scalar f, const Pair<vector>& displacement);
195 
196 
197 // Transformations
198 
199  //- Reflection transform. Corrects the coordinates when the track moves
200  // between two tets which share a base vertex, but for which the other two
201  // non cell-centre vertices are reversed. All hits which retain the same
202  // face behave this way, as do face hits.
204 
205  //- Rotation transform. Corrects the coordinates when the track moves
206  // between two tets with different base vertices, but are otherwise
207  // similarly oriented. Hits which change the face within the cell make use
208  // of both this and the reflect transform.
209  void rotate(const bool reverse, barycentric& coordinates);
210 
211 
212 // Topology Changes
213 
214  //- Change face-triangle within a cell. Called after a tet-triangle is hit.
215  void changeFaceTri
216  (
217  const polyMesh& mesh,
218  const label tetTrii,
220  const label celli,
221  label& facei,
222  label& faceTrii
223  );
224 
225  //- Change face within a cell. Called (if necessary) by changeFaceTri.
226  void changeFace
227  (
228  const polyMesh& mesh,
229  const label tetTrii,
231  const label celli,
232  label& facei,
233  label& faceTrii
234  );
235 
236 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
237 
238 } // End namespace tracking
239 } // End namespace Foam
240 
241 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
242 
244 (
245  const polyMesh& mesh,
246  const label celli,
247  const label facei,
248  const label faceTrii,
249  vector& centre,
250  scalar& detA,
252 )
253 {
254  barycentricTensor A = stationaryTetTransform(mesh, celli, facei, faceTrii);
255 
256  const vector ab = A.b() - A.a();
257  const vector ac = A.c() - A.a();
258  const vector ad = A.d() - A.a();
259  const vector bc = A.c() - A.b();
260  const vector bd = A.d() - A.b();
261 
262  centre = A.a();
263 
264  detA = ab & (ac ^ ad);
265 
267  (
268  bd ^ bc,
269  ac ^ ad,
270  ad ^ ab,
271  ab ^ ac
272  );
273 }
274 
275 
277 (
278  const polyMesh& mesh,
279  const label celli,
280  const label facei,
281  const label faceTrii,
282  const scalar startStepFraction,
283  const scalar endStepFraction,
284  Pair<vector>& centre,
285  FixedList<scalar, 4>& detA,
287 )
288 {
291  (
292  mesh,
293  celli,
294  facei,
295  faceTrii,
296  startStepFraction,
297  endStepFraction
298  );
299 
300  const Pair<vector> ab(A[0].b() - A[0].a(), A[1].b() - A[1].a());
301  const Pair<vector> ac(A[0].c() - A[0].a(), A[1].c() - A[1].a());
302  const Pair<vector> ad(A[0].d() - A[0].a(), A[1].d() - A[1].a());
303  const Pair<vector> bc(A[0].c() - A[0].b(), A[1].c() - A[1].b());
304  const Pair<vector> bd(A[0].d() - A[0].b(), A[1].d() - A[1].b());
305 
306  centre[0] = A[0].a();
307  centre[1] = A[1].a();
308 
309  detA[0] = ab[0] & (ac[0] ^ ad[0]);
310  detA[1] =
311  (ab[1] & (ac[0] ^ ad[0]))
312  + (ab[0] & (ac[1] ^ ad[0]))
313  + (ab[0] & (ac[0] ^ ad[1]));
314  detA[2] =
315  (ab[0] & (ac[1] ^ ad[1]))
316  + (ab[1] & (ac[0] ^ ad[1]))
317  + (ab[1] & (ac[1] ^ ad[0]));
318  detA[3] = ab[1] & (ac[1] ^ ad[1]);
319 
320  T[0] = barycentricTensor
321  (
322  bd[0] ^ bc[0],
323  ac[0] ^ ad[0],
324  ad[0] ^ ab[0],
325  ab[0] ^ ac[0]
326  );
327  T[1] = barycentricTensor
328  (
329  (bd[0] ^ bc[1]) + (bd[1] ^ bc[0]),
330  (ac[0] ^ ad[1]) + (ac[1] ^ ad[0]),
331  (ad[0] ^ ab[1]) + (ad[1] ^ ab[0]),
332  (ab[0] ^ ac[1]) + (ab[1] ^ ac[0])
333  );
334  T[2] = barycentricTensor
335  (
336  bd[1] ^ bc[1],
337  ac[1] ^ ad[1],
338  ad[1] ^ ab[1],
339  ab[1] ^ ac[1]
340  );
341 }
342 
343 
345 (
346  const polyMesh& mesh,
347  const vector& displacement,
348  const scalar fraction,
350  label& celli,
351  label& facei,
352  label& faceTrii,
353  scalar& stepFraction,
354  scalar& stepFractionBehind,
355  label& nTracksBehind,
356  const string& debugPrefix
357 )
358 {
359  const bool debug = notNull(debugPrefix);
360  #define debugIndent string(debugPrefix.size(), ' ').c_str() << ": "
361 
362  const vector x0 =
363  position(mesh, coordinates, celli, facei, faceTrii, stepFraction);
364  const vector& x1 = displacement;
365  const barycentric& y0 = coordinates;
366 
367  DebugInfo
368  << debugPrefix.c_str() << ": Tracking from " << x0
369  << " along " << x1 << " to " << x0 + x1 << nl;
370 
371  // Get the tet geometry
372  vector centre;
373  scalar detA;
376  (
377  mesh,
378  celli,
379  facei,
380  faceTrii,
381  centre,
382  detA,
383  T
384  );
385 
386  if (debug)
387  {
388  vector o, b, v1, v2;
390  (
391  mesh,
392  celli,
393  facei,
394  faceTrii,
395  o,
396  b,
397  v1,
398  v2
399  );
400 
401  Info<< debugIndent << "Tet points o=" << o << ", b=" << b
402  << ", v1=" << v1 << ", v2=" << v2 << nl
403  << debugIndent << "Tet determinant = " << detA << nl
404  << debugIndent << "Start local coordinates = " << y0 << nl;
405  }
406 
407  // Calculate the local tracking displacement
408  barycentric Tx1(x1 & T);
409 
410  DebugInfo
411  << debugIndent << "Local displacement = " << Tx1 << "/" << detA << nl;
412 
413  // Calculate the hit fraction
414  label iH = -1;
415  scalar muH = detA > vSmall ? 1/detA : vGreat;
416  for (label i = 0; i < 4; ++ i)
417  {
418  if (Tx1[i] < - vSmall && Tx1[i] < - mag(detA)*small)
419  {
420  scalar mu = - y0[i]/Tx1[i];
421 
422  DebugInfo
423  << debugIndent << "Hit on tet face " << i
424  << " at local coordinate " << y0 + mu*Tx1 << ", "
425  << mu*detA*100 << "% of the " << "way along the track"
426  << nl;
427 
428  if (0 <= mu && mu < muH)
429  {
430  iH = i;
431  muH = mu;
432  }
433  }
434  }
435 
436  // If there has been no hit on a degenerate or inverted tet then the
437  // displacement must be within the round off error. Advance the step
438  // fraction without moving and return.
439  if (iH == -1 && muH == vGreat)
440  {
441  stepFraction += fraction;
442  return Tuple2<label, scalar>(-1, 0);
443  }
444 
445  // Set the new coordinates
446  barycentric yH = y0 + muH*Tx1;
447 
448  // Clamp to zero any negative coordinates generated by round-off error
449  for (label i = 0; i < 4; ++ i)
450  {
451  yH.replace(i, i == iH ? 0 : max(0, yH[i]));
452  }
453 
454  // Re-normalise if within the tet
455  if (iH == -1)
456  {
457  yH /= cmptSum(yH);
458  }
459 
460  // Set the new position
461  coordinates = yH;
462 
463  // Set the proportion of the track that has been completed
464  stepFraction += fraction*muH*detA;
465 
466  if (debug)
467  {
468  if (iH != -1)
469  {
470  Info<< debugIndent << "Track hit tet face " << iH << " first" << nl;
471  }
472  else
473  {
474  Info<< debugIndent << "Track hit no tet faces" << nl;
475  }
476 
477  const vector xH =
478  position(mesh, coordinates, celli, facei, faceTrii, stepFraction);
479 
480  Info<< debugIndent << "End local coordinates = " << yH << nl
481  << debugIndent << "End global coordinates = " << xH << nl
482  << debugIndent << "Tracking displacement = " << xH - x0 << nl
483  << debugIndent << muH*detA*100 << "% of the step from "
484  << stepFraction - fraction*muH*detA << " to "
485  << stepFraction - fraction*muH*detA + fraction
486  << " completed" << nl << endl;
487  }
488 
489  // Accumulate fraction behind
490  if (muH*detA < small || nTracksBehind > 0)
491  {
492  stepFractionBehind += (fraction != 0 ? fraction : 1)*muH*detA;
493 
494  if (stepFractionBehind > rootSmall)
495  {
496  stepFractionBehind = 0;
497  nTracksBehind = 0;
498  }
499  else
500  {
501  ++ nTracksBehind;
502  }
503  }
504 
505  #undef debugIndent
506 
507  return Tuple2<label, scalar>(iH, iH != -1 ? 1 - muH*detA : 0);
508 }
509 
510 
512 (
513  const polyMesh& mesh,
514  const Pair<vector>& displacement,
515  const scalar fraction,
517  label& celli,
518  label& facei,
519  label& faceTrii,
520  scalar& stepFraction,
521  scalar& stepFractionBehind,
522  label& nTracksBehind,
523  const string& debugPrefix
524 )
525 {
526  const bool debug = notNull(debugPrefix);
527  #define debugIndent string(debugPrefix.size(), ' ').c_str() << ": "
528 
529  const vector x0 =
530  position(mesh, coordinates, celli, facei, faceTrii, stepFraction);
531  const vector& x1 = displacement.first();
532  const vector& x2 = displacement.second();
533  const barycentric& y0 = coordinates;
534 
535  DebugInfo
536  << debugPrefix.c_str() << ": Tracking from " << x0
537  << " along " << x1 << ',' << x2 << " to " << x0 + x1 + x2 << nl;
538 
539  // Get the tet geometry
540  vector centre;
541  scalar detA;
544  (
545  mesh,
546  celli,
547  facei,
548  faceTrii,
549  centre,
550  detA,
551  T
552  );
553 
554  if (debug)
555  {
556  vector o, b, v1, v2;
558  (
559  mesh,
560  celli,
561  facei,
562  faceTrii,
563  o,
564  b,
565  v1,
566  v2
567  );
568 
569  Info<< debugIndent << "Tet points o=" << o << ", b=" << b
570  << ", v1=" << v1 << ", v2=" << v2 << nl
571  << debugIndent << "Tet determinant = " << detA << nl
572  << debugIndent << "Start local coordinates = " << y0 << nl;
573  }
574 
575  // Calculate the local tracking displacement and deltaDisplacement
576  barycentric Tx1(x1 & T);
577  barycentric Tx2(x2 & T);
578 
579  // Form hit equations
581  forAll(hitEqn, i)
582  {
583  hitEqn[i] = quadraticEqn(detA*Tx2[i], Tx1[i], y0[i]);
584  }
585 
586  if (debug)
587  {
588  for (label i = 0; i < 4; ++ i)
589  {
590  Info<< debugIndent << (i ? " " : "Hit equation ")
591  << i << " = " << hitEqn[i] << nl;
592  }
593  }
594 
595  // Calculate the hit fraction
596  label iH = -1;
597  scalar muH = detA > vSmall ? 1/detA : vGreat;
598  for (label i = 0; i < 4; ++ i)
599  {
600  const Roots<2> mu = hitEqn[i].roots();
601 
602  for (label j = 0; j < 2; ++ j)
603  {
604  if
605  (
606  mu.type(j) == rootType::real
607  && hitEqn[i].derivative(mu[j]) < - vSmall
608  && hitEqn[i].derivative(mu[j]) < - mag(detA)*small
609  )
610  {
611  if (debug)
612  {
613  const barycentric yH
614  (
615  hitEqn[0].value(mu[j]),
616  hitEqn[1].value(mu[j]),
617  hitEqn[2].value(mu[j]),
618  hitEqn[3].value(mu[j])
619  );
620 
621  DebugInfo
622  << debugIndent << "Hit on tet face " << i
623  << " at local coordinate " << yH << ", "
624  << mu*detA*100 << "% of the " << "way along the track"
625  << nl;
626  }
627 
628  if (0 <= mu[j] && mu[j] < muH)
629  {
630  iH = i;
631  muH = mu[j];
632  }
633  }
634  }
635  }
636 
637  // If there has been no hit on a degenerate or inverted tet then the
638  // displacement must be within the round off error. Advance the step
639  // fraction without moving and return.
640  if (iH == -1 && muH == vGreat)
641  {
642  stepFraction += fraction;
643  return Tuple2<label, scalar>(-1, 0);
644  }
645 
646  // Set the new coordinates
647  barycentric yH
648  (
649  hitEqn[0].value(muH),
650  hitEqn[1].value(muH),
651  hitEqn[2].value(muH),
652  hitEqn[3].value(muH)
653  );
654 
655  // Clamp to zero any negative coordinates generated by round-off error
656  for (label i = 0; i < 4; ++ i)
657  {
658  yH.replace(i, i == iH ? 0 : max(0, yH[i]));
659  }
660 
661  // Re-normalise if within the tet
662  if (iH == -1)
663  {
664  yH /= cmptSum(yH);
665  }
666 
667  // Set the new position
668  coordinates = yH;
669 
670  // Set the proportion of the track that has been completed
671  stepFraction += fraction*muH*detA;
672 
673  if (debug)
674  {
675  if (iH != -1)
676  {
677  Info<< debugIndent << "Track hit tet face " << iH << " first" << nl;
678  }
679  else
680  {
681  Info<< debugIndent << "Track hit no tet faces" << nl;
682  }
683 
684  const vector xH =
685  position(mesh, coordinates, celli, facei, faceTrii, stepFraction);
686 
687  Info<< debugIndent << "End local coordinates = " << yH << nl
688  << debugIndent << "End global coordinates = " << xH << nl
689  << debugIndent << "Tracking displacement = " << xH - x0 << nl
690  << debugIndent << muH*detA*100 << "% of the step from "
691  << stepFraction - fraction*muH*detA << " to "
692  << stepFraction - fraction*muH*detA + fraction
693  << " completed" << nl << endl;
694  }
695 
696  // Accumulate fraction behind
697  if (muH*detA < small || nTracksBehind > 0)
698  {
699  stepFractionBehind += (fraction != 0 ? fraction : 1)*muH*detA;
700 
701  if (stepFractionBehind > rootSmall)
702  {
703  stepFractionBehind = 0;
704  nTracksBehind = 0;
705  }
706  else
707  {
708  ++ nTracksBehind;
709  }
710  }
711 
712  #undef debugIndent
713 
714  return Tuple2<label, scalar>(iH, iH != -1 ? 1 - muH*detA : 0);
715 }
716 
717 
719 (
720  const polyMesh& mesh,
721  const vector& displacement,
722  const scalar fraction,
724  label& celli,
725  label& facei,
726  label& faceTrii,
727  scalar& stepFraction,
728  scalar& stepFractionBehind,
729  label& nTracksBehind,
730  const string& debugPrefix
731 )
732 {
733  const bool debug = notNull(debugPrefix);
734  #define debugIndent string(debugPrefix.size(), ' ').c_str() << ": "
735 
736  const vector x0 =
737  position(mesh, coordinates, celli, facei, faceTrii, stepFraction);
738  const vector& x1 = displacement;
739  const barycentric& y0 = coordinates;
740 
741  DebugInfo
742  << debugPrefix.c_str() << ": Tracking from " << x0
743  << " along " << x1 << " to " << x0 + x1 << nl;
744 
745  // Get the tet geometry
746  Pair<vector> centre;
750  (
751  mesh,
752  celli,
753  facei,
754  faceTrii,
755  stepFraction,
756  fraction,
757  centre,
758  detA,
759  T
760  );
761 
762  if (debug)
763  {
764  Pair<vector> o, b, v1, v2;
766  (
767  mesh,
768  celli,
769  facei,
770  faceTrii,
771  stepFraction,
772  fraction,
773  o,
774  b,
775  v1,
776  v2
777  );
778 
779  Info<< debugIndent << "Tet points o=" << o[0] << ", b=" << b[0]
780  << ", v1=" << v1[0] << ", v2=" << v2[0] << nl
781  << debugIndent << "Tet determinant = " << detA[0] << nl
782  << debugIndent << "Start local coordinates = " << y0[0] << nl;
783  }
784 
785  // Get the relative global position
786  const vector x0Rel = x0 - centre[0];
787  const vector x1Rel = x1 - centre[1];
788 
789  // Form the determinant and hit equations
790  cubicEqn detAEqn(sqr(detA[0])*detA[3], detA[0]*detA[2], detA[1], 1);
791  const barycentric yC(1, 0, 0, 0);
792  const barycentric hitEqnA =
793  ((x1Rel & T[2]) + detA[3]*yC)*sqr(detA[0]);
794  const barycentric hitEqnB =
795  ((x1Rel & T[1]) + (x0Rel & T[2]) + detA[2]*yC)*detA[0];
796  const barycentric hitEqnC =
797  ((x1Rel & T[0]) + (x0Rel & T[1]) + detA[1]*yC);
798  const barycentric hitEqnD = y0;
799  FixedList<cubicEqn, 4> hitEqn;
800  forAll(hitEqn, i)
801  {
802  hitEqn[i] = cubicEqn(hitEqnA[i], hitEqnB[i], hitEqnC[i], hitEqnD[i]);
803  }
804 
805  if (debug)
806  {
807  for (label i = 0; i < 4; ++ i)
808  {
809  Info<< debugIndent << (i ? " " : "Hit equation ")
810  << i << " = " << hitEqn[i] << nl;
811  }
812  Info<< debugIndent << " DetA equation = " << detA << nl;
813  }
814 
815  // Calculate the hit fraction
816  label iH = -1;
817  scalar muH = detA[0] > vSmall ? 1/detA[0] : vGreat;
818  for (label i = 0; i < 4; ++ i)
819  {
820  const Roots<3> mu = hitEqn[i].roots();
821 
822  for (label j = 0; j < 3; ++ j)
823  {
824  if
825  (
826  mu.type(j) == rootType::real
827  && hitEqn[i].derivative(mu[j]) < - vSmall
828  && hitEqn[i].derivative(mu[j]) < - mag(detA[0])*small
829  )
830  {
831  if (debug)
832  {
833  const barycentric yH
834  (
835  hitEqn[0].value(mu[j]),
836  hitEqn[1].value(mu[j]),
837  hitEqn[2].value(mu[j]),
838  hitEqn[3].value(mu[j])
839  );
840  const scalar detAH = detAEqn.value(mu[j]);
841 
842  Info<< debugIndent << "Hit on tet face " << i
843  << " at local coordinate "
844  << (mag(detAH) > vSmall ? name(yH/detAH) : "???")
845  << ", " << mu[j]*detA[0]*100 << "% of the "
846  << "way along the track" << nl;
847  }
848 
849  if (0 <= mu[j] && mu[j] < muH)
850  {
851  iH = i;
852  muH = mu[j];
853  }
854  }
855  }
856  }
857 
858  // If there has been no hit on a degenerate or inverted tet then the
859  // displacement must be within the round off error. Advance the step
860  // fraction without moving and return.
861  if (iH == -1 && muH == vGreat)
862  {
863  stepFraction += fraction;
864  return Tuple2<label, scalar>(-1, 0);
865  }
866 
867  // Set the new coordinates
868  barycentric yH
869  (
870  hitEqn[0].value(muH),
871  hitEqn[1].value(muH),
872  hitEqn[2].value(muH),
873  hitEqn[3].value(muH)
874  );
875  // !!! <-- This fails if the tet collapses onto the track, as detA tends
876  // to zero at the hit. In this instance, we can differentiate the hit and
877  // detA polynomials to find a limiting location, but this will not be on a
878  // triangle. We will then need to do a second track through the degenerate
879  // tet to find the final hit position. This second track is over zero
880  // distance and therefore can be of the static mesh type. This has not yet
881  // been implemented.
882  const scalar detAH = detAEqn.value(muH);
883  if (mag(detAH) < vSmall)
884  {
886  << "A moving tet collapsed onto a track. This is not supported. "
887  << "The mesh is too poor, or the motion too severe, for tracking "
888  << "to function." << exit(FatalError);
889  }
890  yH /= detAH;
891 
892  // Clamp to zero any negative coordinates generated by round-off error
893  for (label i = 0; i < 4; ++ i)
894  {
895  yH.replace(i, i == iH ? 0 : max(0, yH[i]));
896  }
897 
898  // Re-normalise if within the tet
899  if (iH == -1)
900  {
901  yH /= cmptSum(yH);
902  }
903 
904  // Set the new position and hit index
905  coordinates = yH;
906 
907  // Set the proportion of the track that has been completed
908  stepFraction += fraction*muH*detA[0];
909 
910  if (debug)
911  {
912  if (iH != -1)
913  {
914  Info<< debugIndent << "Track hit tet face " << iH << " first" << nl;
915  }
916  else
917  {
918  Info<< debugIndent << "Track hit no tet faces" << nl;
919  }
920 
921  const vector xH =
922  position(mesh, coordinates, celli, facei, faceTrii, stepFraction);
923 
924  Info<< debugIndent << "End local coordinates = " << yH << nl
925  << debugIndent << "End global coordinates = " << xH << nl
926  << debugIndent << "Tracking displacement = " << xH - x0 << nl
927  << debugIndent << muH*detA[0]*100 << "% of the step from "
928  << stepFraction - fraction*muH*detA[0] << " to "
929  << stepFraction - fraction*muH*detA[0] + fraction
930  << " completed" << nl << endl;
931  }
932 
933  // Accumulate fraction behind
934  if (muH*detA[0] < small || nTracksBehind > 0)
935  {
936  stepFractionBehind += (fraction != 0 ? fraction : 1)*muH*detA[0];
937 
938  if (stepFractionBehind > rootSmall)
939  {
940  stepFractionBehind = 0;
941  nTracksBehind = 0;
942  }
943  else
944  {
945  ++ nTracksBehind;
946  }
947  }
948 
949  #undef debugIndent
950 
951  return Tuple2<label, scalar>(iH, iH != -1 ? 1 - muH*detA[0] : 0);
952 }
953 
954 
956 (
957  const polyMesh& mesh,
958  const Pair<vector>& displacement,
959  const scalar fraction,
961  label& celli,
962  label& facei,
963  label& faceTrii,
964  scalar& stepFraction,
965  scalar& stepFractionBehind,
966  label& nTracksBehind,
967  const string& debugPrefix
968 )
969 {
971  << "Second-order tracking through moving meshes is not supported"
972  << exit(FatalError);
973 
974  return Tuple2<label, scalar>();
975 }
976 
977 
978 template<class Displacement>
980 (
981  const polyMesh& mesh,
982  const Displacement& displacement,
983  const scalar fraction,
985  label& celli,
986  label& facei,
987  label& faceTrii,
988  scalar& stepFraction,
989  scalar& stepFractionBehind,
990  label& nTracksBehind,
991  const string& debugPrefix
992 )
993 {
994  return
995  mesh.moving() && (stepFraction != 1 || fraction != 0)
996  ? toMovingTri
997  (
998  mesh, displacement, fraction,
999  coordinates, celli, facei, faceTrii, stepFraction,
1000  stepFractionBehind, nTracksBehind,
1001  debugPrefix
1002  )
1003  : toStationaryTri
1004  (
1005  mesh, displacement, fraction,
1006  coordinates, celli, facei, faceTrii, stepFraction,
1007  stepFractionBehind, nTracksBehind,
1008  debugPrefix
1009  );
1010 }
1011 
1012 
1013 Foam::Pair<Foam::vector> Foam::tracking::operator*
1014 (
1015  const scalar f,
1016  const Pair<vector>& displacement
1017 )
1018 {
1019  return
1020  Pair<vector>
1021  (
1022  f*displacement.first() + 2*f*(1 - f)*displacement.second(),
1023  f*f*displacement.second()
1024  );
1025 }
1026 
1027 
1029 {
1030  Swap(coordinates.c(), coordinates.d());
1031 }
1032 
1033 
1035 {
1036  if (!reverse)
1037  {
1038  scalar temp = coordinates.b();
1039  coordinates.b() = coordinates.c();
1040  coordinates.c() = coordinates.d();
1041  coordinates.d() = temp;
1042  }
1043  else
1044  {
1045  scalar temp = coordinates.d();
1046  coordinates.d() = coordinates.c();
1047  coordinates.c() = coordinates.b();
1048  coordinates.b() = temp;
1049  }
1050 }
1051 
1052 
1054 (
1055  const polyMesh& mesh,
1056  const label tetTrii,
1058  const label celli,
1059  label& facei,
1060  label& faceTrii
1061 )
1062 {
1063  const bool isOwner = mesh.faceOwner()[facei] == celli;
1064 
1065  const label firstTetPti = 1;
1066  const label lastTetPti = mesh.faces()[facei].size() - 2;
1067 
1068  if (tetTrii == 1)
1069  {
1070  changeFace(mesh, tetTrii, coordinates, celli, facei, faceTrii);
1071  }
1072  else if (tetTrii == 2)
1073  {
1074  if (isOwner)
1075  {
1076  if (faceTrii == lastTetPti)
1077  {
1078  changeFace(mesh, tetTrii, coordinates, celli, facei, faceTrii);
1079  }
1080  else
1081  {
1083  faceTrii += 1;
1084  }
1085  }
1086  else
1087  {
1088  if (faceTrii == firstTetPti)
1089  {
1090  changeFace(mesh, tetTrii, coordinates, celli, facei, faceTrii);
1091  }
1092  else
1093  {
1095  faceTrii -= 1;
1096  }
1097  }
1098  }
1099  else if (tetTrii == 3)
1100  {
1101  if (isOwner)
1102  {
1103  if (faceTrii == firstTetPti)
1104  {
1105  changeFace(mesh, tetTrii, coordinates, celli, facei, faceTrii);
1106  }
1107  else
1108  {
1110  faceTrii -= 1;
1111  }
1112  }
1113  else
1114  {
1115  if (faceTrii == lastTetPti)
1116  {
1117  changeFace(mesh, tetTrii, coordinates, celli, facei, faceTrii);
1118  }
1119  else
1120  {
1122  faceTrii += 1;
1123  }
1124  }
1125  }
1126  else
1127  {
1129  << "Changing tet without changing cell should only happen when the "
1130  << "track is on triangle 1, 2 or 3."
1131  << exit(FatalError);
1132  }
1133 }
1134 
1135 
1137 (
1138  const polyMesh& mesh,
1139  const label tetTrii,
1141  const label celli,
1142  label& facei,
1143  label& faceTrii
1144 )
1145 {
1146  // Get the old topology
1147  const triFace triOldIs(tetIndices(celli, facei, faceTrii).faceTriIs(mesh));
1148 
1149  // Get the shared edge and the pre-rotation
1150  edge sharedEdge;
1151  if (tetTrii == 1)
1152  {
1153  sharedEdge = edge(triOldIs[1], triOldIs[2]);
1154  }
1155  else if (tetTrii == 2)
1156  {
1157  sharedEdge = edge(triOldIs[2], triOldIs[0]);
1158  }
1159  else if (tetTrii == 3)
1160  {
1161  sharedEdge = edge(triOldIs[0], triOldIs[1]);
1162  }
1163  else
1164  {
1166  << "Changing face without changing cell should only happen when the"
1167  << " track is on triangle 1, 2 or 3."
1168  << exit(FatalError);
1169 
1170  sharedEdge = edge(-1, -1);
1171  }
1172 
1173  // Find the face in the same cell that shares the edge, and the
1174  // corresponding tetrahedra point
1175  faceTrii = -1;
1176  forAll(mesh.cells()[celli], cellFacei)
1177  {
1178  const label newFacei = mesh.cells()[celli][cellFacei];
1179  const class face& newFace = mesh.faces()[newFacei];
1180  const label newOwner = mesh.faceOwner()[newFacei];
1181 
1182  // Exclude the current face
1183  if (facei == newFacei)
1184  {
1185  continue;
1186  }
1187 
1188  // Loop over the edges, looking for the shared one
1189  const label edgeComp = newOwner == celli ? -1 : +1;
1190  label edgei = 0;
1191  for
1192  (
1193  ;
1194  edgei < newFace.size()
1195  && edge::compare(sharedEdge, newFace.faceEdge(edgei)) != edgeComp;
1196  ++ edgei
1197  );
1198 
1199  // If the face does not contain the edge, then move on to the next face
1200  if (edgei >= newFace.size())
1201  {
1202  continue;
1203  }
1204 
1205  // Make the edge index relative to the base point
1206  const label newBasei = max(0, mesh.tetBasePtIs()[newFacei]);
1207  edgei = (edgei - newBasei + newFace.size()) % newFace.size();
1208 
1209  // If the edge is next the base point (i.e., the index is 0 or n - 1),
1210  // then we swap it for the adjacent edge. This new edge is opposite the
1211  // base point, and defines the tet with the original edge in it.
1212  edgei = min(max(1, edgei), newFace.size() - 2);
1213 
1214  // Set the new face and tet point
1215  facei = newFacei;
1216  faceTrii = edgei;
1217 
1218  // Exit the loop now that the tet point has been found
1219  break;
1220  }
1221 
1222  if (faceTrii == -1)
1223  {
1225  << "The search for an edge-connected face and tet-point failed."
1226  << exit(FatalError);
1227  }
1228 
1229  // Pre-rotation puts the shared edge opposite the base of the tetrahedron
1230  if (sharedEdge.otherVertex(triOldIs[1]) == -1)
1231  {
1232  rotate(false, coordinates);
1233  }
1234  else if (sharedEdge.otherVertex(triOldIs[2]) == -1)
1235  {
1236  rotate(true, coordinates);
1237  }
1238 
1239  // Get the new topology
1240  const triFace triNewIs(tetIndices(celli, facei, faceTrii).faceTriIs(mesh));
1241 
1242  // Reflect to account for the change of triangle orientation on the new face
1244 
1245  // Post rotation puts the shared edge back in the correct location
1246  if (sharedEdge.otherVertex(triNewIs[1]) == -1)
1247  {
1248  rotate(true, coordinates);
1249  }
1250  else if (sharedEdge.otherVertex(triNewIs[2]) == -1)
1251  {
1252  rotate(false, coordinates);
1253  }
1254 }
1255 
1256 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1257 
1259 (
1260  const polyMesh& mesh,
1261  const point& position,
1262  const label celli,
1263  const label facei,
1264  const label faceTrii,
1265  const scalar stepFraction
1266 )
1267 {
1268  static const barycentric o(1, 0, 0, 0);
1269 
1270  if (mesh.moving() && stepFraction != 1)
1271  {
1272  Pair<vector> centre;
1273  FixedList<scalar, 4> detA;
1276  (
1277  mesh,
1278  celli, facei, faceTrii, stepFraction, 0,
1279  centre, detA, T
1280  );
1281 
1282  return o + ((position - centre[0]) & T[0]/detA[0]);
1283  }
1284  else
1285  {
1286  vector centre;
1287  scalar detA;
1290  (
1291  mesh,
1292  celli, facei, faceTrii,
1293  centre, detA, T
1294  );
1295 
1296  return o + ((position - centre) & T/detA);
1297  }
1298 }
1299 
1300 
1302 (
1303  const polyMesh& mesh,
1304  const barycentric& coordinates,
1305  const label celli,
1306  const label facei,
1307  const label faceTrii,
1308  const scalar stepFraction
1309 )
1310 {
1311  if (mesh.moving() && stepFraction != 1)
1312  {
1313  Pair<vector> centre, base, vertex1, vertex2;
1315  (
1316  mesh,
1317  celli,
1318  facei,
1319  faceTrii,
1320  stepFraction,
1321  1,
1322  centre,
1323  base,
1324  vertex1,
1325  vertex2
1326  );
1327 
1328  // Use the coordinates to interpolate the motion of the three face
1329  // vertices to the current coordinates
1330  return
1331  Pair<vector>
1332  (
1333  triPointRef(base[0], vertex1[0], vertex2[0]).normal(),
1334  coordinates.b()*base[1]
1335  + coordinates.c()*vertex1[1]
1336  + coordinates.d()*vertex2[1]
1337  );
1338  }
1339  else
1340  {
1341  vector centre, base, vertex1, vertex2;
1343  (
1344  mesh,
1345  celli,
1346  facei,
1347  faceTrii,
1348  centre,
1349  base,
1350  vertex1,
1351  vertex2
1352  );
1353 
1354  return
1355  Pair<vector>
1356  (
1357  triPointRef(base, vertex1, vertex2).normal(),
1358  Zero
1359  );
1360  }
1361 }
1362 
1363 
1364 template<class Displacement>
1366 (
1367  const polyMesh& mesh,
1368  const Displacement& displacement,
1369  const scalar fraction,
1371  label& celli,
1372  label& facei,
1373  label& faceTrii,
1374  scalar& stepFraction,
1375  scalar& stepFractionBehind,
1376  label& nTracksBehind,
1377  const string& debugPrefix
1378 )
1379 {
1380  scalar f = 1;
1381 
1382  // Loop the tets in the current cell until the track ends or a face is hit
1383  while (nTracksBehind < maxNTracksBehind)
1384  {
1385  const Tuple2<label, scalar> tetTriiAndF =
1386  toTri
1387  (
1388  mesh, f*displacement, f*fraction,
1389  coordinates, celli, facei, faceTrii, stepFraction,
1390  stepFractionBehind, nTracksBehind,
1391  debugPrefix
1392  );
1393 
1394  const label tetTrii = tetTriiAndF.first();
1395 
1396  f *= tetTriiAndF.second();
1397 
1398  if (tetTrii == -1)
1399  {
1400  // The track has completed within the current tet
1401  return Tuple2<bool, scalar>(false, 0);
1402  }
1403  else if (tetTrii == 0)
1404  {
1405  // The track has hit a face
1406  return Tuple2<bool, scalar>(true, f);
1407  }
1408  else
1409  {
1410  // Move to the next tet and continue the track
1412  (
1413  mesh, tetTrii,
1414  coordinates, celli, facei, faceTrii
1415  );
1416  }
1417  }
1418 
1419  // Warn if stuck, and incorrectly advance the step fraction to completion
1421  << "Track got stuck at "
1422  << position(mesh, coordinates, celli, facei, faceTrii, stepFraction)
1423  << endl;
1424 
1425  stepFraction += f*fraction;
1426 
1427  stepFractionBehind = 0;
1428  nTracksBehind = 0;
1429 
1430  return Tuple2<bool, scalar>(false, 0);
1431 }
1432 
1433 
1435 Foam::tracking::toFace<Foam::vector>
1436 (
1437  const polyMesh& mesh,
1438  const vector& displacement, const scalar fraction,
1439  barycentric& coordinates, label& celli, label& facei, label& faceTrii,
1440  scalar& stepFraction,
1441  scalar& stepFractionBehind, label& nTracksBehind,
1442  const string& debugPrefix
1443 );
1444 
1445 
1447 Foam::tracking::toFace<Foam::Pair<Foam::vector>>
1448 (
1449  const polyMesh& mesh,
1450  const Pair<vector>& displacement, const scalar fraction,
1451  barycentric& coordinates, label& celli, label& facei, label& faceTrii,
1452  scalar& stepFraction,
1453  scalar& stepFractionBehind, label& nTracksBehind,
1454  const string& debugPrefix
1455 );
1456 
1457 
1458 template<class Displacement>
1460 (
1461  const polyMesh& mesh,
1462  const Displacement& displacement,
1463  const scalar fraction,
1465  label& celli,
1466  label& facei,
1467  label& faceTrii,
1468  scalar& stepFraction,
1469  scalar& stepFractionBehind,
1470  label& nTracksBehind,
1471  const string& debugPrefix
1472 )
1473 {
1474  const Tuple2<bool, scalar> onFaceAndF =
1475  toFace
1476  (
1477  mesh, displacement, fraction,
1478  coordinates, celli, facei, faceTrii, stepFraction,
1479  stepFractionBehind, nTracksBehind,
1480  debugPrefix
1481  );
1482 
1483  const bool onInternalFace =
1484  onFaceAndF.first() && mesh.isInternalFace(facei);
1485 
1486  if (onInternalFace)
1487  {
1488  crossInternalFace(mesh, coordinates, celli, facei, faceTrii);
1489  }
1490 
1491  return Tuple2<bool, scalar>(onInternalFace, onFaceAndF.second());
1492 }
1493 
1494 
1496 Foam::tracking::toCell<Foam::vector>
1497 (
1498  const polyMesh& mesh,
1499  const vector& displacement, const scalar fraction,
1500  barycentric& coordinates, label& celli, label& facei, label& faceTrii,
1501  scalar& stepFraction,
1502  scalar& stepFractionBehind, label& nTracksBehind,
1503  const string& debugPrefix
1504 );
1505 
1506 
1508 Foam::tracking::toCell<Foam::Pair<Foam::vector>>
1509 (
1510  const polyMesh& mesh,
1511  const Pair<vector>& displacement, const scalar fraction,
1512  barycentric& coordinates, label& celli, label& facei, label& faceTrii,
1513  scalar& stepFraction,
1514  scalar& stepFractionBehind, label& nTracksBehind,
1515  const string& debugPrefix
1516 );
1517 
1518 
1519 template<class Displacement>
1521 (
1522  const polyMesh& mesh,
1523  const Displacement& displacement, const scalar fraction,
1525  label& celli,
1526  label& facei,
1527  label& faceTrii,
1528  scalar& stepFraction,
1529  scalar& stepFractionBehind,
1530  label& nTracksBehind,
1531  const string& debugPrefix
1532 )
1533 {
1534  scalar f = 1;
1535 
1536  // Loop multiple cells until the track ends or a boundary face is hit
1537  while (true)
1538  {
1539  const Tuple2<bool, scalar> onFaceAndF =
1540  toFace
1541  (
1542  mesh, f*displacement, f*fraction,
1543  coordinates, celli, facei, faceTrii, stepFraction,
1544  stepFractionBehind, nTracksBehind,
1545  debugPrefix
1546  );
1547 
1548  f *= onFaceAndF.second();
1549 
1550  const bool onInternalFace =
1551  onFaceAndF.first() && mesh.isInternalFace(facei);
1552 
1553  if (onInternalFace)
1554  {
1555  crossInternalFace(mesh, coordinates, celli, facei, faceTrii);
1556  }
1557  else
1558  {
1559  const bool onBoundaryFace =
1560  onFaceAndF.first() && !mesh.isInternalFace(facei);
1561 
1562  return Tuple2<bool, scalar>(onBoundaryFace, onBoundaryFace ? f : 0);
1563  }
1564  }
1565 }
1566 
1567 
1569 Foam::tracking::toBoundary<Foam::vector>
1570 (
1571  const polyMesh& mesh,
1572  const vector& displacement, const scalar fraction,
1573  barycentric& coordinates, label& celli, label& facei, label& faceTrii,
1574  scalar& stepFraction,
1575  scalar& stepFractionBehind, label& nTracksBehind,
1576  const string& debugPrefix
1577 );
1578 
1579 
1581 Foam::tracking::toBoundary<Foam::Pair<Foam::vector>>
1582 (
1583  const polyMesh& mesh,
1584  const Pair<vector>& displacement, const scalar fraction,
1585  barycentric& coordinates, label& celli, label& facei, label& faceTrii,
1586  scalar& stepFraction,
1587  scalar& stepFractionBehind, label& nTracksBehind,
1588  const string& debugPrefix
1589 );
1590 
1591 
1593 (
1594  const polyMesh& mesh,
1595  const point& position,
1597  label& celli,
1598  label& facei,
1599  label& faceTrii,
1600  const scalar stepFraction,
1601  const string& debugPrefix
1602 )
1603 {
1604  // Find the cell, if it has not been given
1605  if (celli < 0)
1606  {
1607  celli = mesh.cellTree().findInside(position);
1608  }
1609  if (celli < 0)
1610  {
1612  << "Cell not found for position " << position << "."
1613  << exit(FatalError);
1614  }
1615 
1616  // Track from the centre of the cell to the desired position
1617  const vector displacement = position - mesh.cellCentres()[celli];
1618 
1619  // Loop all cell tets to find the one containing the position. Track
1620  // through each tet from the cell centre. If a tet contains the position
1621  // then the track will end with a single toTri.
1622  const class cell& c = mesh.cells()[celli];
1623  scalar minF = vGreat;
1624  label minFacei = -1, minFaceTrii = -1;
1625  forAll(c, cellFacei)
1626  {
1627  const class face& f = mesh.faces()[c[cellFacei]];
1628  for (label tetPti = 1; tetPti < f.size() - 1; ++ tetPti)
1629  {
1630  coordinates = barycentric(1, 0, 0, 0);
1631  facei = c[cellFacei];
1632  faceTrii = tetPti;
1633  scalar stepFractionCopy = stepFraction, stepFractionBehind = 0;
1634  label nTracksBehind = 0;
1635 
1636  const Tuple2<label, scalar> tetTriiAndF =
1637  toTri
1638  (
1639  mesh, displacement, 0,
1640  coordinates, celli, facei, faceTrii, stepFractionCopy,
1641  stepFractionBehind, nTracksBehind,
1642  debugPrefix
1643  );
1644 
1645  if (tetTriiAndF.first() == -1)
1646  {
1647  return true;
1648  }
1649 
1650  if (tetTriiAndF.second() < minF)
1651  {
1652  minF = tetTriiAndF.second();
1653  minFacei = facei;
1654  minFaceTrii = faceTrii;
1655  }
1656  }
1657  }
1658 
1659  // The track must be (hopefully only slightly) outside the cell. Track into
1660  // the tet which got the furthest.
1661  coordinates = barycentric(1, 0, 0, 0);
1662  facei = minFacei;
1663  faceTrii = minFaceTrii;
1664  scalar stepFractionCopy = stepFraction, stepFractionBehind = 0;
1665  label nTracksBehind = 0;
1666  const Tuple2<bool, scalar> onBoundaryAndF =
1667  toBoundary
1668  (
1669  mesh, displacement, 0,
1670  coordinates, celli, facei, faceTrii, stepFractionCopy,
1671  stepFractionBehind, nTracksBehind,
1672  debugPrefix
1673  );
1674 
1675  // Return successful if in a cell
1676  return !onBoundaryAndF.first();
1677 }
1678 
1679 
1681 (
1682  const polyMesh& mesh,
1684  label& celli,
1685  label& facei,
1686  label& faceTrii
1687 )
1688 {
1689  // Set the cell to be the one on the other side of the face
1690  const label ownerCelli = mesh.faceOwner()[facei];
1691  const bool isOwner = celli == ownerCelli;
1692  celli = isOwner ? mesh.faceNeighbour()[facei] : ownerCelli;
1693 
1694  // Reflect to account for the change of triangle orientation in the new cell
1696 }
1697 
1698 
1700 (
1701  const wedgePolyPatch& inPatch,
1703  label& celli,
1704  label& facei,
1705  label& faceTrii,
1706  const scalar stepFraction
1707 )
1708 {
1709  const polyMesh& mesh = inPatch.boundaryMesh().mesh();
1710 
1711  // Move to the other side of the mesh. Note that we track here because the
1712  // tet indices aren't guaranteed to be consistent between pairs of wedge
1713  // patches, so we'd have to search for the opposite position to jump there,
1714  // and its quicker and more robust to track across the cell instead. We can
1715  // do a simplistic track here because we know we are just moving through a
1716  // cell; no other patch hits or transfers need to be considered.
1717  scalar stepFractionCopy = stepFraction, stepFractionBehind = 0;
1718  label nTracksBehind = 0;
1719  toBoundary
1720  (
1721  mesh,
1722  - mesh.bounds().mag()*inPatch.centreNormal(),
1723  0,
1724  coordinates,
1725  celli,
1726  facei,
1727  faceTrii,
1728  stepFractionCopy,
1729  stepFractionBehind,
1730  nTracksBehind
1731  );
1732 }
1733 
1734 
1736 (
1737  const cyclicPolyPatch& inPatch,
1739  label& celli,
1740  label& facei,
1741  label& faceTrii
1742 )
1743 {
1744  const polyMesh& mesh = inPatch.boundaryMesh().mesh();
1745 
1746  // Set the topology to the neighbouring face and cell
1747  facei = facei - inPatch.start() + inPatch.nbrPatch().start();
1748  celli = mesh.faceOwner()[facei];
1749 
1750  // Faces either side of a conformal coupled patch are numbered in opposite
1751  // directions as their normals both point away from their connected
1752  // cells. The tet point therefore counts in the opposite direction from
1753  // the base point.
1754  faceTrii = mesh.faces()[facei].size() - 1 - faceTrii;
1755 
1756  // Reflect to account for the change of tri orientation in the new cell
1758 }
1759 
1760 
1762 (
1763  const processorPolyPatch& inPatch,
1764  label& celli,
1765  label& facei
1766 )
1767 {
1768  // Break the topology. Convert the mesh face label to a patch-local face
1769  // label, as this is the same on both sides of the processor interface.
1770  // Invalidate the cell index.
1771  celli = -1;
1772  facei -= inPatch.start();
1773 }
1774 
1775 
1777 (
1778  const processorPolyPatch& outPatch,
1780  label& celli,
1781  label& facei,
1782  label& faceTrii
1783 )
1784 {
1785  const polyMesh& mesh = outPatch.boundaryMesh().mesh();
1786 
1787  // Restore the topology. Convert the patch-local face label to a mesh face
1788  // label. Set the cell as the face's owner.
1789  celli = outPatch.faceCells()[facei];
1790  facei += outPatch.start();
1791 
1792  // See tracking::crossCyclic
1793  faceTrii = mesh.faces()[facei].size() - 1 - faceTrii;
1794 
1795  // See tracking::crossCyclic
1797 }
1798 
1799 
1800 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
Templated 4x3 tensor derived from VectorSpace. Has 12 components. Can represent a barycentric transfo...
const Cmpt & b() const
Definition: BarycentricI.H:66
const Cmpt & c() const
Definition: BarycentricI.H:73
const Cmpt & d() const
Definition: BarycentricI.H:80
A 1D vector of objects of type <T> with a fixed size <Size>.
Definition: FixedList.H:78
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
An ordered pair of two objects of type <Type> with first() and second() elements.
Definition: Pair.H:66
const Type & second() const
Return second.
Definition: PairI.H:121
const Type & first() const
Return first.
Definition: PairI.H:107
Templated storage for the roots of polynomial equations, plus flags to indicate the nature of the roo...
Definition: Roots.H:67
A 2-tuple for storing two objects of different types.
Definition: Tuple2.H:66
const Type2 & second() const
Return second.
Definition: Tuple2.H:131
const Type1 & first() const
Return first.
Definition: Tuple2.H:119
void replace(const direction, const Cmpt &)
Definition: VectorSpaceI.H:149
scalar mag() const
The magnitude of the bounding box span.
Definition: boundBoxI.H:96
A cell is defined as a list of faces with extra functionality.
Definition: cell.H:60
Cubic equation of the form a*x^3 + b*x^2 + c*x + d = 0.
Definition: cubicEqn.H:52
scalar value(const scalar x) const
Evaluate at x.
Definition: cubicEqnI.H:103
Cyclic plane patch.
const cyclicPolyPatch & nbrPatch() const
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:61
label otherVertex(const label a) const
Given one vertex, return the other.
Definition: edgeI.H:140
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:76
edge faceEdge(const label n) const
Return n-th face edge.
Definition: faceI.H:94
const polyMesh & mesh() const
Return the mesh reference.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1344
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1357
const labelIOList & tetBasePtIs() const
Return the tetBasePtIs.
Definition: polyMesh.C:1046
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1363
const indexedOctree< treeDataCell > & cellTree() const
Return the cell search tree.
Definition: polyMesh.C:1080
const boundBox & bounds() const
Return mesh bounding box.
Definition: polyMesh.H:407
bool moving() const
Is mesh moving.
Definition: polyMesh.H:473
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:280
const polyBoundaryMesh & boundaryMesh() const
Return boundaryMesh reference.
Definition: polyPatch.C:270
const labelUList & faceCells() const
Return face-cell addressing.
Definition: polyPatch.C:313
const vectorField & cellCentres() const
bool isInternalFace(const label faceIndex) const
Return true if given face label is internal to the mesh.
const cellList & cells() const
Neighbour processor patch.
Quadratic equation of the form a*x^2 + b*x + c = 0.
Definition: quadraticEqn.H:52
Storage and named access for the indices of a tet which is part of the decomposition of a cell.
Definition: tetIndices.H:82
A triangular face using a FixedList of labels corresponding to mesh vertices.
Definition: triFace.H:71
Wedge front and back plane patch.
const vector & centreNormal() const
Return plane normal between the wedge boundaries.
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
volScalarField & b
Definition: createFields.H:27
#define DebugInfo
Report an information message using Foam::Info.
#define WarningInFunction
Report a warning using Foam::Warning.
const dimensionedScalar mu
Atomic mass unit.
const dimensionedScalar c
Speed of light in a vacuum.
static const coefficient A("A", dimPressure, 611.21)
void reflect(barycentric &coordinates)
Reflection transform. Corrects the coordinates when the track moves.
Definition: tracking.C:1028
void stationaryTetReverseTransform(const polyMesh &mesh, const label celli, const label facei, const label faceTrii, vector &centre, scalar &detA, barycentricTensor &T)
Get the reverse transform associated with the current tet. The.
Definition: tracking.C:244
barycentric coordinates(const polyMesh &mesh, const point &position, const label celli, const label facei, const label faceTrii, const scalar stepFraction)
Return the coordinates given the position and tet topology.
Definition: tracking.C:1259
Tuple2< bool, scalar > toCell(const polyMesh &mesh, const Displacement &displacement, const scalar fraction, barycentric &coordinates, label &celli, label &facei, label &faceTrii, scalar &stepFraction, scalar &stepFractionBehind, label &nTracksBehind, const string &debugPrefix=NullObjectRef< string >())
As toFace, except that if the track ends on an internal face then this.
Pair< vector > faceNormalAndDisplacement(const polyMesh &mesh, const barycentric &coordinates, const label celli, const label facei, const label faceTrii, const scalar stepFraction)
Return the normal of the corresponding point on the associated face and.
Definition: tracking.C:1302
void stationaryTetGeometry(const polyMesh &mesh, const label celli, const label facei, const label faceTrii, vector &centre, vector &base, vector &vertex1, vector &vertex2)
Get the vertices of the current tet.
Definition: trackingI.H:106
Tuple2< bool, scalar > toBoundary(const polyMesh &mesh, const Displacement &displacement, const scalar fraction, barycentric &coordinates, label &celli, label &facei, label &faceTrii, scalar &stepFraction, scalar &stepFractionBehind, label &nTracksBehind, const string &debugPrefix=NullObjectRef< string >())
As toFace, except that the track continues across multiple cells until.
barycentricTensor stationaryTetTransform(const polyMesh &mesh, const label celli, const label facei, const label faceTrii)
Get the transformation associated with the current tet. This.
Definition: trackingI.H:129
Tuple2< label, scalar > toMovingTri(const polyMesh &mesh, const vector &displacement, const scalar fraction, barycentric &coordinates, label &celli, label &facei, label &faceTrii, scalar &stepFraction, scalar &stepFractionBehind, label &nTracksBehind, const string &debugPrefix=NullObjectRef< string >())
See toTri. For a moving mesh.
Definition: tracking.C:719
void movingTetGeometry(const polyMesh &mesh, const label celli, const label facei, const label faceTrii, const scalar startStepFraction, const scalar endStepFraction, Pair< vector > &centre, Pair< vector > &base, Pair< vector > &vertex1, Pair< vector > &vertex2)
Get the vertices of the current moving tet. Two values are.
Definition: trackingI.H:154
void changeFace(const polyMesh &mesh, const label tetTrii, barycentric &coordinates, const label celli, label &facei, label &faceTrii)
Change face within a cell. Called (if necessary) by changeFaceTri.
Definition: tracking.C:1137
void crossInternalFace(const polyMesh &mesh, barycentric &coordinates, label &celli, label &facei, label &faceTrii)
Cross an internal face.
Definition: tracking.C:1681
static const label maxNTracksBehind
The counter nTracksBehind is the number of tracks carried out that.
Definition: tracking.C:106
Pair< barycentricTensor > movingTetTransform(const polyMesh &mesh, const label celli, const label facei, const label faceTrii, const scalar startStepFraction, const scalar endStepFraction)
Get the transformation associated with the current, moving, tet.
Definition: trackingI.H:188
void rotate(const bool reverse, barycentric &coordinates)
Rotation transform. Corrects the coordinates when the track moves.
Definition: tracking.C:1034
void changeFaceTri(const polyMesh &mesh, const label tetTrii, barycentric &coordinates, const label celli, label &facei, label &faceTrii)
Change face-triangle within a cell. Called after a tet-triangle is hit.
Definition: tracking.C:1054
void crossCyclic(const cyclicPolyPatch &inPatch, barycentric &coordinates, label &celli, label &facei, label &faceTrii)
Cross a cyclic patch.
Definition: tracking.C:1736
void outProcessor(const processorPolyPatch &outPatch, barycentric &coordinates, label &celli, label &facei, label &faceTrii)
Complete crossing of a processor patch. Restore the topology.
Definition: tracking.C:1777
Pair< vector > operator*(const scalar f, const Pair< vector > &displacement)
Scale a second-order displacement.
Definition: tracking.C:1014
void inProcessor(const processorPolyPatch &inPatch, label &celli, label &facei)
Initialise crossing of a processor patch. Breaks the topology in order.
Definition: tracking.C:1762
void crossWedge(const wedgePolyPatch &inPatch, barycentric &coordinates, label &celli, label &facei, label &faceTrii, const scalar stepFraction)
Cross a wedge patch.
Definition: tracking.C:1700
point position(const polyMesh &mesh, const barycentric &coordinates, const label celli, const label facei, const label faceTrii, const scalar stepFraction)
Return the position given the coordinates and tet topology.
Definition: trackingI.H:224
void movingTetReverseTransform(const polyMesh &mesh, const label celli, const label facei, const label faceTrii, const scalar startStepFraction, const scalar endStepFraction, Pair< vector > &centre, FixedList< scalar, 4 > &detA, FixedList< barycentricTensor, 3 > &T)
Get the reverse transformation associated with the current,.
Definition: tracking.C:277
Tuple2< label, scalar > toStationaryTri(const polyMesh &mesh, const Pair< vector > &displacement, const scalar fraction, barycentric &coordinates, label &celli, label &facei, label &faceTrii, scalar &stepFraction, scalar &stepFractionBehind, label &nTracksBehind, const string &debugPrefix=NullObjectRef< string >())
See toTri. Second order. For a stationary mesh.
Definition: tracking.C:512
Tuple2< label, scalar > toTri(const polyMesh &mesh, const Displacement &displacement, const scalar fraction, barycentric &coordinates, label &celli, label &facei, label &faceTrii, scalar &stepFraction, scalar &stepFractionBehind, label &nTracksBehind, const string &debugPrefix=NullObjectRef< string >())
Track along the displacement for a given fraction of the overall.
Tuple2< label, scalar > toMovingTri(const polyMesh &mesh, const Pair< vector > &displacement, const scalar fraction, barycentric &coordinates, label &celli, label &facei, label &faceTrii, scalar &stepFraction, scalar &stepFractionBehind, label &nTracksBehind, const string &debugPrefix=NullObjectRef< string >())
See toTri. Second order. For a moving mesh. Not implemented.
Definition: tracking.C:956
Tuple2< bool, scalar > toFace(const polyMesh &mesh, const Displacement &displacement, const scalar fraction, barycentric &coordinates, label &celli, label &facei, label &faceTrii, scalar &stepFraction, scalar &stepFractionBehind, label &nTracksBehind, const string &debugPrefix=NullObjectRef< string >())
Track along the displacement for a given fraction of the overall.
bool locate(const polyMesh &mesh, const point &position, barycentric &coordinates, label &celli, label &facei, label &faceTrii, const scalar stepFraction, const string &debugPrefix=NullObjectRef< string >())
Initialise the location at the given position. Returns whether or not a.
Definition: tracking.C:1593
Tuple2< label, scalar > toStationaryTri(const polyMesh &mesh, const vector &displacement, const scalar fraction, barycentric &coordinates, label &celli, label &facei, label &faceTrii, scalar &stepFraction, scalar &stepFractionBehind, label &nTracksBehind, const string &debugPrefix=NullObjectRef< string >())
See toTri. For a stationary mesh.
Definition: tracking.C:345
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
static const zero Zero
Definition: zero.H:97
Barycentric< scalar > barycentric
A scalar version of the templated Barycentric.
Definition: barycentric.H:45
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:258
dimensionedScalar y0(const dimensionedScalar &ds)
void T(LagrangianPatchField< Type > &f, const LagrangianPatchField< Type > &f1)
messageStream Info
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
void reverse(UList< T > &, const label n)
Definition: UListI.H:334
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
bool notNull(const T &t)
Return true if t is not a reference to the nullObject of type T.
Definition: nullObjectI.H:64
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
void sqr(LagrangianPatchField< typename outerProduct< Type, Type >::type > &f, const LagrangianPatchField< Type > &f1)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
triangle< point, const point & > triPointRef
Definition: triPointRef.H:44
BarycentricTensor< scalar > barycentricTensor
A scalar version of the templated BarycentricTensor.
error FatalError
void Swap(T &a, T &b)
Definition: Swap.H:43
Cmpt cmptSum(const VectorSpace< Form, Cmpt, Ncmpts > &vs)
Definition: VectorSpaceI.H:507
static const char nl
Definition: Ostream.H:267
labelList f(nPoints)
#define debugIndent