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