Loading...
Searching...
No Matches
mappedPatchBase.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2016 OpenFOAM Foundation
9 Copyright (C) 2015-2024 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "mappedPatchBase.H"
31#include "ListListOps.H"
34#include "meshTools.H"
35#include "OFstream.H"
36#include "Random.H"
37#include "treeDataFace.H"
38#include "treeDataPoint.H"
39#include "indexedOctree.H"
40#include "polyMesh.H"
41#include "polyPatch.H"
42#include "Time.H"
43#include "mapDistribute.H"
44#include "SubField.H"
45#include "triangle.H"
46#include "syncTools.H"
47#include "treeDataCell.H"
48#include "DynamicField.H"
50#include "OTstream.H"
51
52// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
53
54namespace Foam
55{
57}
58
59
60const Foam::Enum
61<
63>
65({
66 { sampleMode::NEARESTCELL, "nearestCell" },
67 { sampleMode::NEARESTPATCHFACE, "nearestPatchFace" },
68 { sampleMode::NEARESTPATCHFACEAMI, "nearestPatchFaceAMI" },
69 { sampleMode::NEARESTPATCHPOINT, "nearestPatchPoint" },
70 { sampleMode::NEARESTFACE, "nearestFace" },
71 { sampleMode::NEARESTONLYCELL, "nearestOnlyCell" },
72});
73
74
75const Foam::Enum
76<
78>
80({
81 { offsetMode::UNIFORM, "uniform" },
82 { offsetMode::NONUNIFORM, "nonuniform" },
83 { offsetMode::NORMAL, "normal" },
84});
85
86
87// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
90{
91 clearOut();
92}
93
94
96(
97 const dictionary& dict
98)
99{
100 if (dict.found("sampleDatabase"))
101 {
102 if (dict.get<bool>("sampleDatabase"))
103 {
105 (
106 dict.getOrDefault<fileName>
107 (
108 "sampleDatabasePath",
110 )
111 );
112 }
113 }
114 else if (dict.found("sampleDatabasePath"))
115 {
116 return autoPtr<fileName>::New(dict.get<fileName>("sampleDatabasePath"));
117 }
118
119 return nullptr;
120}
121
122
124{
125 if (sameWorld())
126 {
127 return true;
128 }
129
130 const Time& runTime = patch_.boundaryMesh().mesh().time();
131 return const_cast<multiWorldConnections&>
132 (
134 ).addConnectionByName(sampleWorld_);
135}
136
137
139{
140 if (sameWorld())
141 {
142 return UPstream::commWorld();
143 }
145 const Time& runTime = patch_.boundaryMesh().mesh().time();
146 return
148}
149
150
152(
153 const polyPatch& pp
154) const
155{
156 const polyMesh& mesh = pp.boundaryMesh().mesh();
157
158 // Force construction of min-tet decomp
159 (void)mesh.tetBasePtIs();
160
161 // Initialise to face-centre
162 auto tfacePoints = tmp<pointField>::New(patch_.size());
163 auto& facePoints = tfacePoints.ref();
164
165 forAll(pp, facei)
166 {
167 facePoints[facei] = facePoint
168 (
169 mesh,
170 pp.start()+facei,
172 ).point();
173 }
174
175 return tfacePoints;
176}
177
178
180(
181 const label mySampleWorld, // Wanted world
182 const pointField& facePoints,
183 pointField& samples, // All samples
184 labelList& patchFaceWorlds, // Per sample: wanted world
185 labelList& patchFaceProcs, // Per sample: originating processor
186 labelList& patchFaces, // Per sample: originating patchFace index
187 pointField& patchFc // Per sample: originating centre
188) const
189{
191
192 const label myComm = getCommunicator(); // Get or create
193 const label myRank = Pstream::myProcNo(myComm);
194 const label nProcs = Pstream::nProcs(myComm);
195
196 const label oldWarnComm = UPstream::commWarn(myComm);
197
198 if (debug & 2)
199 {
200 Perr<< "patch: " << patch_.name()
201 << "[rank=" << myRank << " procs=" << nProcs
202 << " comm=" << myComm << "] collect samples" << endl;
203 }
204
205 // Collect all sample points and the faces they come from.
206 {
207 List<pointField> globalFc(nProcs);
208 globalFc[myRank] = facePoints;
209
210 Pstream::allGatherList(globalFc, Pstream::msgType(), myComm);
211
212 // Rework into straight list
214 (
215 globalFc,
217 );
218 }
219
220 {
221 List<pointField> globalSamples(nProcs);
222 globalSamples[myRank] = samplePoints(facePoints);
223 Pstream::allGatherList(globalSamples, Pstream::msgType(), myComm);
224
225 // Rework into straight list
227 (
228 globalSamples,
230 );
231 }
232
233 {
234 labelListList globalFaces(nProcs);
235 globalFaces[myRank] = identity(patch_.size());
236 // Distribute to all processors
237 Pstream::allGatherList(globalFaces, Pstream::msgType(), myComm);
238
240 (
241 globalFaces,
243 );
244 }
245
246 {
247 labelList procToWorldIndex
248 (
249 UPstream::allGatherValues<label>(mySampleWorld, myComm)
250 );
251 labelList nPerProc
252 (
253 UPstream::allGatherValues<label>(patch_.size(), myComm)
254 );
255
256 patchFaceWorlds.resize(patchFaces.size());
257 patchFaceProcs.resize(patchFaces.size());
258
259 label sampleI = 0;
260 forAll(nPerProc, proci)
261 {
262 for (label i = 0; i < nPerProc[proci]; i++)
263 {
264 patchFaceWorlds[sampleI] = procToWorldIndex[proci];
265 patchFaceProcs[sampleI] = proci;
266 sampleI++;
267 }
268 }
270
271 // Restore communicator settings
272 UPstream::commWarn(oldWarnComm);
273}
274
275
277(
278 const sampleMode mode,
279
280 const label mySampleWorld, // local world to sample == my own world
281 const word& sampleRegion, // local region to sample
282 const word& samplePatch, // local patch to sample
283
284 const pointField& samples,
285 List<nearInfoWorld>& nearest
286) const
287{
289
290 const label myComm = getCommunicator(); // Get or create
291
292 // Find the local cell containing the samples
293 const label myRank = Pstream::myProcNo(myComm);
294
295 // Lookup the correct region
296 const polyMesh& mesh = lookupMesh(sampleRegion);
297
298 // All the info for nearest. Construct to miss
299 nearest.setSize(samples.size());
300 nearInfoWorld miss;
301 {
302 miss.first().second() = Tuple2<scalar, label>(Foam::sqr(GREAT), -1);
303 miss.second() = -1; // set world to be ignored
304 }
305 nearest = miss;
306
307 switch (mode)
308 {
309 case NEARESTCELL:
310 {
311 if (samplePatch.size() && samplePatch != "none")
312 {
314 << "No need to supply a patch name when in "
315 << sampleModeNames_[mode] << " mode." << exit(FatalError);
316 }
317
318 //- Note: face-diagonal decomposition
320
321 forAll(samples, sampleI)
322 {
323 const point& sample = samples[sampleI];
324 nearInfoWorld& near = nearest[sampleI];
325
326 label celli = tree.findInside(sample);
327
328 if (celli == -1)
329 {
330 near.first().second().first() = Foam::sqr(GREAT);
331 near.first().second().second() = myRank;
332 near.second() = mySampleWorld;
333 }
334 else
335 {
336 const point& cc = mesh.cellCentres()[celli];
337
338 near.first().first() = pointIndexHit
339 (
340 true,
341 cc,
342 celli
343 );
344 near.first().second().first() = sample.distSqr(cc);
345 near.first().second().second() = myRank;
346 near.second() = mySampleWorld;
347 }
348 }
349 break;
350 }
351
352 case NEARESTONLYCELL:
353 {
354 if (samplePatch.size() && samplePatch != "none")
355 {
357 << "No need to supply a patch name when in "
358 << sampleModeNames_[mode] << " mode." << exit(FatalError);
359 }
360
361 //- Note: face-diagonal decomposition
363
364 forAll(samples, sampleI)
365 {
366 const point& sample = samples[sampleI];
367 nearInfoWorld& near = nearest[sampleI];
368
369 near.first().first() = tree.findNearest(sample, sqr(GREAT));
370
371 near.first().second().first() =
372 near.first().first().hitPoint().distSqr(sample);
373
374 near.first().second().second() = myRank;
375 near.second() = mySampleWorld;
376 }
377 break;
378 }
379
380 case NEARESTPATCHFACE:
381 {
382 Random rndGen(123456);
383
384 const polyPatch& pp = lookupPatch(sampleRegion, samplePatch);
385
386 if (pp.empty())
387 {
388 forAll(samples, sampleI)
389 {
390 nearInfoWorld& near = nearest[sampleI];
391 near.first().second().first() = Foam::sqr(GREAT);
392 near.first().second().second() = myRank;
393 near.second() = mySampleWorld;
394 }
395 }
396 else
397 {
398 treeBoundBox patchBb
399 (
401 (
402 rndGen,
403 1e-4,
404 ROOTVSMALL
405 )
406 );
407
408 indexedOctree<treeDataFace> boundaryTree
409 (
410 treeDataFace(mesh, pp.range()), // Patch faces
411
412 patchBb, // overall search domain
413 8, // maxLevel
414 10, // leafsize
415 3.0 // duplicity
416 );
417 const auto& treeData = boundaryTree.shapes();
418
419 forAll(samples, sampleI)
420 {
421 const point& sample = samples[sampleI];
422
423 nearInfoWorld& near = nearest[sampleI];
424 pointIndexHit& nearInfo = near.first().first();
425 nearInfo = boundaryTree.findNearest
426 (
427 sample,
428 patchBb.magSqr()
429 );
430
431 if (!nearInfo.hit())
432 {
433 near.first().second().first() = Foam::sqr(GREAT);
434 near.first().second().second() = myRank;
435 near.second() = mySampleWorld;
436 }
437 else
438 {
439 const point& fc = treeData.centre(nearInfo.index());
440
441 nearInfo.setPoint(fc);
442 near.first().second().first() = sample.distSqr(fc);
443 near.first().second().second() = myRank;
444 near.second() = mySampleWorld;
445 }
446 }
447 }
448 break;
449 }
450
451 case NEARESTPATCHPOINT:
452 {
453 Random rndGen(123456);
454
455 const polyPatch& pp = lookupPatch(sampleRegion, samplePatch);
456
457 if (pp.empty())
458 {
459 forAll(samples, sampleI)
460 {
461 nearInfoWorld& near = nearest[sampleI];
462 near.first().second().first() = Foam::sqr(GREAT);
463 near.first().second().second() = myRank;
464 near.second() = mySampleWorld;
465 }
466 }
467 else
468 {
469 // patch (local) points
470 treeBoundBox patchBb
471 (
473 (
474 rndGen,
475 1e-4,
476 ROOTVSMALL
477 )
478 );
479
481 (
482 // Patch points
484
485 patchBb, // overall search domain
486 8, // maxLevel
487 10, // leafsize
488 3.0 // duplicity
489 );
490
491 forAll(samples, sampleI)
492 {
493 const point& sample = samples[sampleI];
494
495 nearInfoWorld& near = nearest[sampleI];
496 pointIndexHit& nearInfo = near.first().first();
497 nearInfo = boundaryTree.findNearest
498 (
499 sample,
500 patchBb.magSqr()
501 );
502
503 if (!nearInfo.hit())
504 {
505 near.first().second().first() = Foam::sqr(GREAT);
506 near.first().second().second() = myRank;
507 near.second() = mySampleWorld;
508 }
509 else
510 {
511 const point& pt = nearInfo.point();
512
513 near.first().second().first() = sample.distSqr(pt);
514 near.first().second().second() = myRank;
515 near.second() = mySampleWorld;
516 }
517 }
518 }
519 break;
520 }
521
522 case NEARESTFACE:
523 {
524 if (samplePatch.size() && samplePatch != "none")
525 {
527 << "No need to supply a patch name when in "
528 << sampleModeNames_[mode] << " mode." << exit(FatalError);
529 }
530
531 //- Note: face-diagonal decomposition
532 const meshSearchMeshObject& meshSearchEngine =
534
535 forAll(samples, sampleI)
536 {
537 const point& sample = samples[sampleI];
538 nearInfoWorld& near = nearest[sampleI];
539
540 label facei = meshSearchEngine.findNearestFace(sample);
541
542 if (facei == -1)
543 {
544 near.first().second().first() = Foam::sqr(GREAT);
545 near.first().second().second() = myRank;
546 near.second() = mySampleWorld;
547 }
548 else
549 {
550 const point& fc = mesh.faceCentres()[facei];
551
552 near.first().first() = pointIndexHit(true, fc, facei);
553 near.first().second().first() = sample.distSqr(fc);
554 near.first().second().second() = myRank;
555 near.second() = mySampleWorld;
556 }
557 }
558 break;
559 }
560
561 case NEARESTPATCHFACEAMI:
562 {
563 // nothing to do here
564 return;
565 }
566
567 default:
568 {
570 << "problem." << abort(FatalError);
571 }
572 }
573}
574
577(
578 const sampleMode mode,
579 const label myWorld,
580 const pointField& samples,
581 const labelList& wantedWorlds,
582 const labelList& origProcs,
583
584 labelList& sampleProcs,
585 labelList& sampleIndices,
586 pointField& sampleLocations
587) const
588{
590
591 // Find the processor/cell containing the samples. Does not account
592 // for samples being found in two processors.
593
594 const label myComm = getCommunicator(); // Get or create
595 const label myRank = Pstream::myProcNo(myComm);
596 const label nProcs = Pstream::nProcs(myComm);
597
598 const label oldWarnComm = UPstream::commWarn(myComm);
599
600 wordList samplePatches(nProcs);
601 {
602 samplePatches[myRank] = samplePatch_;
603 Pstream::allGatherList(samplePatches, Pstream::msgType(), myComm);
604 }
605 wordList sampleRegions(nProcs);
606 {
607 sampleRegions[myRank] = sampleRegion_;
608 Pstream::allGatherList(sampleRegions, Pstream::msgType(), myComm);
609 }
610
611
612 // Find all the info for nearest
613 List<nearInfoWorld> nearest(samples.size());
614 forAll(nearest, samplei)
615 {
616 nearest[samplei].first() = nearInfo
617 (
620 );
621 nearest[samplei].second() = wantedWorlds[samplei];
622 }
623
624
625 // Extract samples to search for locally
626 {
627 DynamicList<label> localMap(samples.size());
628 forAll(wantedWorlds, samplei)
629 {
630 if (wantedWorlds[samplei] == myWorld)
631 {
632 localMap.append(samplei);
633 }
634 }
635
636 if (localMap.size())
637 {
638 pointField localSamples(samples, localMap);
639 labelList localOrigProcs(origProcs, localMap);
640
641 //Assume single patch to sample for now
642 const word localOrigPatch(samplePatches[localOrigProcs[0]]);
643 const word localOrigRegion(sampleRegions[localOrigProcs[0]]);
644 List<nearInfoWorld> localNearest(localSamples.size());
645
646 if (debug)
647 {
648 Pout<< "Searching locally for " << localSamples.size()
649 << " samples on region:" << localOrigRegion
650 << " on patch:" << localOrigPatch << endl;
651 }
653 (
654 mode,
655 myWorld,
656 localOrigRegion,
657 localOrigPatch,
658 localSamples,
659 localNearest
660 );
661 UIndirectList<nearInfoWorld>(nearest, localMap) = localNearest;
662 }
663 }
664
665
666 // Find nearest - globally consistent
668 (
669 nearest,
672 myComm
673 );
674
675 //if (debug)
676 //{
677 // Pout<< "** After combining:" << endl;
678 // forAll(nearest, samplei)
679 // {
680 // Pout<< " sample:" << samples[samplei]
681 // << " origating from proc:" << origProcs[samplei]
682 // << " to be found on world:" << wantedWorlds[samplei] << nl
683 // << " found on world:" << nearest[samplei].second() << nl
684 // << " found on proc:"
685 // << nearest[samplei].first().second().second() << nl
686 // << " found on patchfacei:"
687 // << nearest[samplei].first().first().index() << nl
688 // << " found at location:"
689 // << nearest[samplei].first().first().point() << nl;
690 // }
691 // Pout<< endl;
692 //}
693
694 // Convert back into proc+local index
695 sampleProcs.setSize(samples.size());
696 sampleIndices.setSize(samples.size());
697 sampleLocations.setSize(samples.size());
698
699 forAll(nearest, sampleI)
700 {
701 const nearInfo& ni = nearest[sampleI].first();
702
703 if (!ni.first().hit())
704 {
705 sampleProcs[sampleI] = -1;
706 sampleIndices[sampleI] = -1;
707 sampleLocations[sampleI] = vector::max;
708 }
709 else
710 {
711 sampleProcs[sampleI] = ni.second().second();
712 sampleIndices[sampleI] = ni.first().index();
713 sampleLocations[sampleI] = ni.first().point();
714 }
715 }
716
717 // Return communicator settings
718 UPstream::commWarn(oldWarnComm);
719}
720
723{
725
726 static bool hasWarned = false;
727 if (mapPtr_)
728 {
730 << "Mapping already calculated" << exit(FatalError);
731 }
732
734
735
736 // Early construction of tetBasePtIs since does parallel
737 // communication which might conflict with inter-world communicator
738 // (for multi-world operation)
739 (void)patch_.boundaryMesh().mesh().tetBasePtIs();
740
741 const label myComm = getCommunicator(); // Get or create
742
744 //if (sampleDatabase() && !sameWorld())
745 //{
746 // const word syncName("syncObjects");
747 // const polyMesh& mesh = patch_.boundaryMesh().mesh();
748 // const Time& runTime = mesh.time();
749 // const functionObjectList& fos = runTime.functionObjects();
750 //
751 // forAll(fos, i)
752 // {
753 // Pout<< "** FO:" << fos[i].name() << " tpye:" << fos[i].type()
754 // << endl;
755 // }
756 //
757 // if (fos.findObjectID(syncName) == -1)
758 // {
759 // //FatalErrorInFunction
760 // WarningInFunction
761 // << "Did not detect functionObject " << syncName
762 // << ". This is used to synchronise data inbetween worlds"
763 // << endl;
764 // }
765 //}
766
767
768 // Get points on face (since cannot use face-centres - might be off
769 // face-diagonal decomposed tets.
770 tmp<pointField> patchPoints(facePoints(patch_));
771
772 // Get offsetted points
773 const pointField offsettedPoints(samplePoints(patchPoints()));
774
775 // Do a sanity check - am I sampling my own patch?
776 // This only makes sense for a non-zero offset.
777 bool sampleMyself =
778 (
781 && sampleRegion() == patch_.boundaryMesh().mesh().name()
782 && samplePatch() == patch_.name()
783 );
784
785 if (sampleMyself)
786 {
787 // Check offset
788 vectorField d(offsettedPoints-patchPoints());
789 bool coincident = (gAverage(mag(d)) <= ROOTVSMALL);
790
791 if (sampleMyself && coincident)
792 {
794 << "Invalid offset " << d << endl
795 << "Offset is the vector added to the patch face centres to"
796 << " find the patch face supplying the data." << endl
797 << "Setting it to " << d
798 << " on the same patch, on the same region"
799 << " will find the faces themselves which does not make sense"
800 << " for anything but testing." << endl
801 << "patch:" << patch_.name() << endl
802 << "sampleRegion:" << sampleRegion() << endl
803 << "mode:" << sampleModeNames_[mode_] << endl
804 << "samplePatch:" << samplePatch() << endl
805 << "offsetMode:" << offsetModeNames_[offsetMode_] << endl;
806 }
807 }
808
809 // Get local world and world-to-sample in index form
810 const label myWorld = Pstream::myWorldID();
811 const label mySampleWorld = Pstream::allWorlds().find(sampleWorld_);
812
813
814 // Get global list of all samples and the processor and face they come from.
815 pointField samples; // coordinates
816 labelList patchFaceWorlds; // world to sample
817 labelList patchFaceProcs; // originating processor
818 labelList patchFaces; // originating face on processor patch
819 pointField patchFc; // originating face centre
821 (
822 mySampleWorld, // world I want to sample
823 patchPoints,
824
825 samples,
826 patchFaceWorlds,
827 patchFaceProcs,
828 patchFaces,
829 patchFc
830 );
831
832 //if (debug)
833 //{
834 // forAll(samples, samplei)
835 // {
836 // Pout<< " sample:" << samples[samplei]
837 // << " origating from proc:" << patchFaceProcs[samplei]
838 // << " face:" << patchFaces[samplei]
839 // << " to be found on world:" << patchFaceWorlds[samplei] << nl;
840 // }
841 //}
842
843 // Find processor and cell/face samples are in and actual location.
844 labelList sampleProcs;
845 labelList sampleIndices;
846 pointField sampleLocations;
848 (
849 mode_,
850 myWorld, // my world (in index form)
851 samples,
852 patchFaceWorlds,
853 patchFaceProcs,
854
855 sampleProcs,
856 sampleIndices,
857 sampleLocations
858 );
859
860 // Check for samples that were not found. This will only happen for
861 // NEARESTCELL since finds cell containing a location
862 if (mode_ == NEARESTCELL)
863 {
864 label nNotFound = 0;
865 forAll(sampleProcs, sampleI)
866 {
867 if (sampleProcs[sampleI] == -1)
868 {
869 nNotFound++;
870 }
871 }
872 reduce(nNotFound, sumOp<label>(), Pstream::msgType(), myComm);
873
874 if (nNotFound > 0)
875 {
876 if (!hasWarned)
877 {
879 << "Did not find " << nNotFound
880 << " out of " << sampleProcs.size() << " total samples."
881 << " Sampling these on owner cell centre instead." << endl
882 << "On patch " << patch_.name()
883 << " on region " << sampleRegion()
884 << " in mode " << sampleModeNames_[mode_] << endl
885 << "with offset mode " << offsetModeNames_[offsetMode_]
886 << ". Suppressing further warnings from " << type() << endl;
887
888 hasWarned = true;
889 }
890
891 // Collect the samples that cannot be found
892 DynamicList<label> subMap;
893 forAll(sampleProcs, sampleI)
894 {
895 if (sampleProcs[sampleI] == -1)
896 {
897 subMap.append(sampleI);
898 }
899 }
900
901 // And re-search for pure nearest (should not fail)
902 labelList subSampleProcs;
903 labelList subSampleIndices;
904 pointField subSampleLocations;
906 (
908 myWorld, // my world (in index form)
909
910 pointField(samples, subMap),
911 UIndirectList<label>(patchFaceWorlds, subMap)(),
912 UIndirectList<label>(patchFaceProcs, subMap)(),
913
914 subSampleProcs,
915 subSampleIndices,
916 subSampleLocations
917 );
918
919 // Insert
920 labelUIndList(sampleProcs, subMap) = subSampleProcs;
921 labelUIndList(sampleIndices, subMap) = subSampleIndices;
922 UIndirectList<point>(sampleLocations, subMap) = subSampleLocations;
923 }
924 }
925
926 // Now we have all the data we need:
927 // - where sample originates from (so destination when mapping):
928 // patchFaces, patchFaceProcs.
929 // - cell/face sample is in (so source when mapping)
930 // sampleIndices, sampleProcs.
931
932 if (Pstream::master(myComm))
933 {
934 forAll(samples, i)
935 {
936 if (sampleProcs[i] == -1)
937 {
938 FatalErrorInFunction << "did not find sample "
939 << patchFc[i] << " on patch " << patch_.name()
940 << " on region "
941 << patch_.boundaryMesh().mesh().name()
942 << " on processor " << patchFaceProcs[i]
943 << exit(FatalError);
944 }
945 }
946 }
947
948
949 if (debug && Pstream::master(myComm))
950 {
951 //forAll(samples, i)
952 //{
953 // Pout<< i << " need data in region "
954 // << patch_.boundaryMesh().mesh().name()
955 // << " for proc:" << patchFaceProcs[i]
956 // << " face:" << patchFaces[i]
957 // << " at:" << patchFc[i] << endl
958 // << "Found data in region " << sampleRegion()
959 // << " at proc:" << sampleProcs[i]
960 // << " face:" << sampleIndices[i]
961 // << " at:" << sampleLocations[i]
962 // << nl << endl;
963 //}
964
965 OFstream str
966 (
967 patch_.boundaryMesh().mesh().time().path()
968 / patch_.name()
969 + "_mapped.obj"
970 );
971 Pout<< "Dumping mapping as lines from patch faceCentres to"
972 << " sampled cell/faceCentres/points to file " << str.name()
973 << endl;
974
975 label vertI = 0;
976
977 forAll(patchFc, i)
978 {
979 meshTools::writeOBJ(str, patchFc[i]);
980 vertI++;
981 meshTools::writeOBJ(str, sampleLocations[i]);
982 vertI++;
983 str << "l " << vertI-1 << ' ' << vertI << nl;
984 }
985 }
986
987 // Determine schedule.
988 mapPtr_.reset(new mapDistribute(sampleProcs, patchFaceProcs, myComm));
989
990 // Rework the schedule from indices into samples to cell data to send,
991 // face data to receive.
992
993 labelListList& subMap = mapPtr_().subMap();
994 labelListList& constructMap = mapPtr_().constructMap();
995
996 forAll(subMap, proci)
997 {
998 subMap[proci] = labelUIndList(sampleIndices, subMap[proci]);
999 constructMap[proci] = labelUIndList(patchFaces, constructMap[proci]);
1000
1001 if (debug)
1002 {
1003 Pout<< "To proc:" << proci << " sending values of cells/faces:"
1004 << subMap[proci] << endl;
1005 Pout<< "From proc:" << proci
1006 << " receiving values of patch faces:"
1007 << constructMap[proci] << endl;
1008 }
1009 }
1010
1011
1012 // Redo constructSize
1013 mapPtr_().constructSize() = patch_.size();
1014
1015 if (debug)
1016 {
1017 // Check that all elements get a value.
1018 bitSet used(patch_.size());
1019 forAll(constructMap, proci)
1020 {
1021 const labelList& map = constructMap[proci];
1022
1023 forAll(map, i)
1024 {
1025 label facei = map[i];
1026
1027 if (used.test(facei))
1028 {
1030 << "On patch " << patch_.name()
1031 << " patchface " << facei
1032 << " is assigned to more than once."
1033 << abort(FatalError);
1034 }
1035 else
1036 {
1037 used.set(facei);
1038 }
1039 }
1040 }
1041 forAll(used, facei)
1042 {
1043 if (!used.test(facei))
1044 {
1046 << "On patch " << patch_.name()
1047 << " patchface " << facei
1048 << " is never assigned to."
1049 << abort(FatalError);
1050 }
1051 }
1052 }
1053
1054 updateMeshTime().setUpToDate();
1055 if (sameWorld())
1056 {
1057 updateSampleMeshTime().setUpToDate();
1058 }
1059}
1060
1063const
1064{
1065 const word surfType(surfDict_.getOrDefault<word>("type", "none"));
1066
1067 if (!surfPtr_ && surfType != "none")
1068 {
1069 word surfName(surfDict_.getOrDefault("name", patch_.name()));
1070
1071 const polyMesh& mesh = patch_.boundaryMesh().mesh();
1072
1073 surfPtr_ =
1075 (
1076 surfType,
1077 IOobject
1078 (
1079 surfName,
1080 mesh.time().constant(),
1081 "triSurface",
1082 mesh,
1085 ),
1086 surfDict_
1087 );
1088 }
1089
1090 return surfPtr_;
1091}
1092
1095{
1096 if (AMIPtr_->upToDate())
1097 {
1099 << "AMI already up-to-date"
1100 << endl;
1101
1102 return;
1103 }
1104
1106
1107 const label myComm = getCommunicator(); // Get or create
1108
1109 // Pre-calculate surface (if any)
1110 const auto& surf = surfPtr();
1111
1112 // Check if running locally
1113 if (sampleWorld_.empty() || sameWorld())
1114 {
1115 const polyPatch& nbr = samplePolyPatch();
1116
1117 // Transform neighbour patch to local system
1118 const pointField nbrPoints(samplePoints(nbr.localPoints()));
1119
1120 const primitivePatch nbrPatch0
1121 (
1123 (
1124 nbr.localFaces(),
1125 nbr.size()
1126 ),
1127 nbrPoints
1128 );
1129
1130
1131 if (debug)
1132 {
1133 OFstream os(patch_.name() + "_neighbourPatch-org.obj");
1134 meshTools::writeOBJ(os, samplePolyPatch().localFaces(), nbrPoints);
1135
1136 OFstream osN(patch_.name() + "_neighbourPatch-trans.obj");
1137 meshTools::writeOBJ(osN, nbrPatch0, nbrPoints);
1138
1139 OFstream osO(patch_.name() + "_ownerPatch.obj");
1140 meshTools::writeOBJ(osO, patch_.localFaces(), patch_.localPoints());
1141 }
1142
1143 // Construct/apply AMI interpolation to determine addressing and
1144 // weights.
1145
1146 // Change to use inter-world communicator
1147 label oldWarnComm(-1);
1148 label oldWorldComm(-1);
1149 if (!sameWorld())
1150 {
1151 oldWarnComm = UPstream::commWarn(myComm);
1152 oldWorldComm = UPstream::commWorld(myComm);
1153 }
1154
1155 AMIPtr_->calculate(patch_, nbrPatch0, surf);
1156
1157 // Restore communicator settings
1158 UPstream::commWarn(oldWarnComm);
1159 UPstream::commWorld(oldWorldComm);
1160 }
1161 else
1162 {
1163 faceList dummyFaces;
1164 pointField dummyPoints;
1165 const primitivePatch dummyPatch
1166 (
1167 SubList<face>(dummyFaces),
1168 dummyPoints
1169 );
1170
1171 // Change to use inter-world communicator
1172 AMIPtr_->comm(myComm);
1173
1174 const label oldWarnComm = UPstream::commWarn(myComm);
1175 const label oldWorldComm = UPstream::commWorld(myComm);
1176
1177 if (masterWorld())
1178 {
1179 // Construct/apply AMI interpolation to determine addressing
1180 // and weights. Have patch_ for src faces, 0 faces for the
1181 // target side
1182 AMIPtr_->calculate(patch_, dummyPatch, surf);
1183 }
1184 else
1185 {
1186 // Construct/apply AMI interpolation to determine addressing
1187 // and weights. Have 0 faces for src side, patch_ for the tgt
1188 // side
1189 AMIPtr_->calculate(dummyPatch, patch_, surf);
1190 }
1191 // Now the AMI addressing/weights will be from src side (on masterWorld
1192 // processors) to tgt side (on other processors)
1193
1194 // Restore communicator settings
1195 UPstream::commWarn(oldWarnComm);
1196 UPstream::commWorld(oldWorldComm);
1197 }
1198}
1199
1202(
1203 const objectRegistry& obr,
1204 const wordList& names,
1205 const label index
1206)
1207{
1208 const objectRegistry& sub = obr.subRegistry(names[index], true);
1209 if (index == names.size()-1)
1210 {
1211 return sub;
1212 }
1213 else
1214 {
1215 return subRegistry(sub, names, index+1);
1216 }
1217}
1218
1219
1220// * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * //
1223:
1224 patch_(pp),
1225 sampleWorld_(),
1228 samplePatch_(),
1229 coupleGroup_(),
1232 offset_(Zero),
1233 offsets_(pp.size(), offset_),
1234 distance_(0),
1235 communicator_(-1), // Demand-driven (cached value)
1236 sameRegion_(true),
1237 mapPtr_(nullptr),
1238 AMIReverse_(false),
1240 surfPtr_(nullptr),
1241 surfDict_(fileName("surface"))
1242{
1243 // NOTE: same region, no sample-world. Thus no world-world communication
1244}
1245
1248(
1249 const polyPatch& pp,
1250 const word& sampleRegion,
1251 const sampleMode mode,
1252 const word& samplePatch,
1253 const vectorField& offsets
1254)
1255:
1257 (
1258 pp,
1260 mode,
1262 scalar(0)
1263 )
1264{
1266}
1267
1270(
1271 const polyPatch& pp,
1272 const word& sampleRegion,
1273 const sampleMode mode,
1274 const word& samplePatch,
1275 const vector& uniformOffset
1276)
1277:
1279 (
1280 pp,
1282 mode,
1284 scalar(0)
1285 )
1286{
1287 mappedPatchBase::setOffset(uniformOffset);
1288}
1289
1292(
1293 const polyPatch& pp,
1294 const word& sampleRegion,
1295 const sampleMode mode,
1296 const word& samplePatch,
1297 const scalar normalDistance
1298)
1299:
1300 patch_(pp),
1301 sampleWorld_(),
1303 mode_(mode),
1305 coupleGroup_(),
1308 offset_(Zero),
1309 offsets_(0),
1310 distance_(normalDistance),
1311 communicator_(-1), // Demand-driven (cached value)
1313 (
1314 sampleWorld_.empty()
1316 ),
1317 mapPtr_(nullptr),
1318 AMIReverse_(false),
1320 surfPtr_(nullptr),
1321 surfDict_(fileName("surface"))
1322{
1324}
1325
1328(
1329 const polyPatch& pp,
1330 const dictionary& dict
1331)
1332:
1333 patch_(pp),
1334 sampleWorld_(dict.getOrDefault<word>("sampleWorld", word::null)),
1335 sampleRegion_(dict.getOrDefault<word>("sampleRegion", word::null)),
1336 mode_(sampleModeNames_.get("sampleMode", dict)),
1337 samplePatch_(dict.getOrDefault<word>("samplePatch", word::null)),
1341 offset_(Zero),
1342 offsets_(0),
1343 distance_(0),
1344 communicator_(-1), // Demand-driven (cached value)
1346 (
1347 sampleWorld_.empty()
1349 ),
1350 mapPtr_(nullptr),
1352 (
1353 dict.getOrDefault
1354 (
1355 "reverseTarget", // AMIInterpolation uses this keyword
1356 dict.getOrDefault
1357 (
1358 "flipNormals",
1359 false
1360 )
1361 )
1362 ),
1363 AMIPtr_
1364 (
1366 (
1367 dict.getOrDefault("AMIMethod", faceAreaWeightAMI::typeName),
1368 dict,
1370 )
1371 ),
1372 surfPtr_(nullptr),
1373 surfDict_(dict.subOrEmptyDict("surface"))
1374{
1376
1377 if (!coupleGroup_.good())
1378 {
1379 if (sampleWorld_.empty() && sampleRegion_.empty())
1380 {
1381 // If no coupleGroup and no sampleRegion assume local region
1382 sampleRegion_ = patch_.boundaryMesh().mesh().name();
1383 sameRegion_ = true;
1384 }
1385 }
1386
1387 if (offsetModeNames_.readIfPresent("offsetMode", dict, offsetMode_))
1388 {
1389 switch (offsetMode_)
1390 {
1391 case UNIFORM:
1392 {
1393 dict.readEntry("offset", offset_);
1394 }
1395 break;
1396
1397 case NONUNIFORM:
1398 {
1399 offsets_ = pointField("offsets", dict, patch_.size());
1400 }
1401 break;
1402
1403 case NORMAL:
1404 {
1405 dict.readEntry("distance", distance_);
1406 }
1407 break;
1408 }
1409 }
1410 else if (dict.readIfPresent("offset", offset_))
1411 {
1413 }
1414 else if (dict.found("offsets"))
1415 {
1417 offsets_ = pointField("offsets", dict, patch_.size());
1418 }
1420 {
1422 << "Please supply the offsetMode as one of "
1424 << exit(FatalIOError);
1425 }
1426}
1427
1430(
1431 const polyPatch& pp,
1432 const sampleMode mode,
1433 const dictionary& dict
1434)
1435:
1436 patch_(pp),
1437 sampleWorld_(dict.getOrDefault<word>("sampleWorld", word::null)),
1438 sampleRegion_(dict.getOrDefault<word>("sampleRegion", word::null)),
1439 mode_(mode),
1440 samplePatch_(dict.getOrDefault<word>("samplePatch", word::null)),
1441 coupleGroup_(dict), //dict.getOrDefault<word>("coupleGroup", word::null)),
1444 offset_(Zero),
1445 offsets_(0),
1446 distance_(0),
1447 communicator_(-1), // Demand-driven (cached value)
1449 (
1450 sampleWorld_.empty()
1452 ),
1453 mapPtr_(nullptr),
1455 (
1456 dict.getOrDefault
1457 (
1458 "reverseTarget", // AMIInterpolation uses this keyword
1459 dict.getOrDefault
1460 (
1461 "flipNormals",
1462 false
1463 )
1464 )
1465 ),
1466 AMIPtr_
1467 (
1469 (
1470 dict.getOrDefault("AMIMethod", faceAreaWeightAMI::typeName),
1471 dict,
1473 )
1474 ),
1475 surfPtr_(nullptr),
1476 surfDict_(dict.subOrEmptyDict("surface"))
1477{
1479
1481 {
1483 << "Construct from sampleMode and dictionary only applicable for "
1484 << " collocated patches in modes "
1487 << exit(FatalIOError);
1488 }
1489
1490 if (!coupleGroup_.good())
1491 {
1492 if (sampleWorld_.empty() && sampleRegion_.empty())
1493 {
1494 // If no coupleGroup and no sampleRegion assume local region
1495 sampleRegion_ = patch_.boundaryMesh().mesh().name();
1496 sameRegion_ = true;
1497 }
1498 }
1499}
1500
1503(
1504 const polyPatch& pp,
1505 const mappedPatchBase& mpb
1506)
1507:
1508 patch_(pp),
1511 mode_(mpb.mode_),
1515 (
1517 ? new fileName(mpb.sampleDatabasePtr_())
1518 : nullptr
1519 ),
1521 offset_(mpb.offset_),
1522 offsets_(mpb.offsets_),
1523 distance_(mpb.distance_),
1526 mapPtr_(nullptr),
1528 AMIPtr_(mpb.AMIPtr_->clone()),
1529 surfPtr_(nullptr),
1530 surfDict_(mpb.surfDict_)
1531{}
1532
1535(
1536 const polyPatch& pp,
1537 const mappedPatchBase& mpb,
1538 const labelUList& mapAddressing
1539)
1540:
1541 patch_(pp),
1544 mode_(mpb.mode_),
1548 (
1550 ? new fileName(mpb.sampleDatabasePtr_())
1551 : nullptr
1552 ),
1554 offset_(mpb.offset_),
1555 offsets_
1556 (
1558 ? vectorField(mpb.offsets_, mapAddressing)
1559 : vectorField()
1560 ),
1561 distance_(mpb.distance_),
1564 mapPtr_(nullptr),
1566 AMIPtr_(mpb.AMIPtr_->clone()),
1567 surfPtr_(nullptr),
1568 surfDict_(mpb.surfDict_)
1569{}
1570
1571
1572// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1577}
1578
1581{
1582 mapPtr_.reset(nullptr);
1583 AMIPtr_->upToDate(false);
1584
1585 //Note: no need to clear out surface since not mesh related
1586}
1587
1588
1589// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1591void Foam::mappedPatchBase::setOffset(const scalar normalDist)
1592{
1593 clearOut();
1595 offset_ = Zero;
1596 offsets_.clear();
1597 distance_ = normalDist;
1598}
1599
1601void Foam::mappedPatchBase::setOffset(const vector& uniformOffset)
1602{
1603 clearOut();
1605 offset_ = uniformOffset;
1606 offsets_.clear();
1607 distance_ = Zero;
1608}
1609
1618}
1619
1622(
1623 const word& sampleRegion
1624) const
1625{
1626 const polyMesh& thisMesh = patch_.boundaryMesh().mesh();
1627 return
1628 (
1629 sampleRegion.empty() || sampleRegion == thisMesh.name()
1630 ? thisMesh
1631 : thisMesh.time().lookupObject<polyMesh>(sampleRegion)
1632 );
1633}
1634
1637(
1638 const word& sampleRegion,
1639 const word& samplePatch
1640) const
1641{
1642 const polyMesh& nbrMesh = lookupMesh(sampleRegion);
1643
1644 const label patchi = nbrMesh.boundaryMesh().findPatchID(samplePatch);
1645
1646 if (patchi == -1)
1647 {
1649 << "Cannot find patch " << samplePatch
1650 << " in region " << sampleRegion_ << endl
1651 << exit(FatalError);
1652 }
1653 return nbrMesh.boundaryMesh()[patchi];
1654}
1655
1658{
1660 {
1662 << "sampleWorld : " << sampleWorld_
1663 << " is not the current world : " << UPstream::myWorld()
1664 << exit(FatalError);
1665 }
1666 return lookupMesh(sampleRegion());
1667}
1668
1671{
1672 const polyMesh& nbrMesh = sampleMesh();
1673
1674 const label patchi = nbrMesh.boundaryMesh().findPatchID(samplePatch());
1675
1676 if (patchi == -1)
1677 {
1679 << "Cannot find patch " << samplePatch()
1680 << " in region " << sampleRegion_ << endl
1681 << "Valid patches are " << nbrMesh.boundaryMesh().names()
1682 << exit(FatalError);
1683 }
1684
1685 return nbrMesh.boundaryMesh()[patchi];
1686}
1687
1690{
1691 const polyMesh& thisMesh = patch_.boundaryMesh().mesh();
1692
1693 bool thisUpToDate = thisMesh.upToDatePoints(updateMeshTime());
1694 bool sampleUpToDate =
1695 (
1696 sameWorld()
1697 ? sampleMesh().upToDatePoints(updateSampleMeshTime())
1698 : true
1699 );
1700
1701
1702 // Lambda to check for points on the patch being the same
1703 auto checkPointMovement = []
1704 (
1705 const polyMesh& mesh,
1706 const polyPatch& patch,
1707 regIOobject& state
1708 ) -> bool
1709 {
1710 bool upToDate = true;
1711 const auto& oldPoints = mesh.oldPoints();
1712 const auto& points = mesh.points();
1713
1714 for (const label pointi : patch.meshPoints())
1715 {
1716 const point& oldPt = oldPoints[pointi];
1717 const point& currentPt = points[pointi];
1718
1719 if (mag(oldPt - currentPt) > SMALL)
1720 {
1721 upToDate = false;
1722 break;
1723 }
1724 }
1725
1727
1728 if (upToDate)
1729 {
1730 state.setUpToDate();
1731 }
1732
1733 return upToDate;
1734 };
1735
1736
1737 if (!thisUpToDate && thisMesh.moving())
1738 {
1739 // Moving (but not topoChanging mesh) : do more accurate check:
1740 // compare actual patch point position
1741
1742 thisUpToDate = checkPointMovement(thisMesh, patch_, updateMeshTime());
1743 }
1744
1745 if
1746 (
1747 !sampleUpToDate
1748 && sampleMesh().moving()
1749 && (
1753 )
1754 )
1755 {
1756 sampleUpToDate = checkPointMovement
1757 (
1758 sampleMesh(),
1761 );
1762 }
1763
1764 return (thisUpToDate && sampleUpToDate);
1765}
1766
1769(
1770 const pointField& fc
1771) const
1772{
1773 auto tfld = tmp<pointField>::New(fc);
1774 auto& fld = tfld.ref();
1775
1776 switch (offsetMode_)
1777 {
1778 case UNIFORM:
1779 {
1780 fld += offset_;
1781 break;
1782 }
1783 case NONUNIFORM:
1784 {
1785 fld += offsets_;
1786 break;
1787 }
1788 case NORMAL:
1789 {
1790 // Get outwards pointing normal
1791 vectorField n(patch_.faceAreas());
1792 n /= mag(n);
1793
1794 fld += distance_*n;
1795 break;
1796 }
1797 }
1798
1799 return tfld;
1800}
1801
1806}
1807
1810(
1811 const polyMesh& mesh,
1812 const label facei,
1813 const polyMesh::cellDecomposition decompMode
1814)
1815{
1816 const point& fc = mesh.faceCentres()[facei];
1817
1818 switch (decompMode)
1819 {
1822 {
1823 // For both decompositions the face centre is guaranteed to be
1824 // on the face
1825 return pointIndexHit(true, fc, facei);
1826 }
1827 break;
1828
1831 {
1832 // Find the intersection of a ray from face centre to cell centre
1833 // Find intersection of (face-centre-decomposition) centre to
1834 // cell-centre with face-diagonal-decomposition triangles.
1835
1836 const pointField& p = mesh.points();
1837 const face& f = mesh.faces()[facei];
1838
1839 if (f.size() <= 3)
1840 {
1841 // Return centre of triangle.
1842 return pointIndexHit(true, fc, 0);
1843 }
1844
1845 const label celli = mesh.faceOwner()[facei];
1846 const point& cc = mesh.cellCentres()[celli];
1847 vector d = fc-cc;
1848
1849 const label fp0 = mesh.tetBasePtIs()[facei];
1850 const point& basePoint = p[f[fp0]];
1851
1852 label fp = f.fcIndex(fp0);
1853 for (label i = 2; i < f.size(); i++)
1854 {
1855 const point& thisPoint = p[f[fp]];
1856 label nextFp = f.fcIndex(fp);
1857 const point& nextPoint = p[f[nextFp]];
1858
1859 const triPointRef tri(basePoint, thisPoint, nextPoint);
1860 pointHit hitInfo = tri.intersection
1861 (
1862 cc,
1863 d,
1865 );
1866
1867 if (hitInfo.hit() && hitInfo.distance() > 0)
1868 {
1869 return pointIndexHit(true, hitInfo.point(), i-2);
1870 }
1871
1872 fp = nextFp;
1873 }
1874
1875 // Fall-back
1876 return pointIndexHit(false, fc, -1);
1877 }
1878 break;
1879
1880 default:
1881 {
1883 << "problem" << abort(FatalError);
1884 return pointIndexHit();
1885 }
1886 }
1887}
1888
1891(
1892 const objectRegistry& obr,
1893 const fileName& path
1894)
1895{
1896 // Lookup (and create if non-existing) a registry using
1897 // '/' separated path. Like 'mkdir -p'
1898
1899 fileName cleanedPath(path);
1900 cleanedPath.clean(); // Remove unneeded ".."
1901 const wordList names(cleanedPath.components());
1902
1903 if (names.empty())
1904 {
1905 return obr;
1906 }
1907 else
1908 {
1909 return subRegistry(obr, names, 0);
1910 }
1911}
1912
1915(
1916 const fileName& root,
1917 const label proci
1918)
1919{
1920 const word processorName("processor"+Foam::name(proci));
1921 return root/"send"/processorName;
1922}
1923
1925Foam::fileName Foam::mappedPatchBase::sendPath(const label proci) const
1926{
1927 return sendPath(sampleDatabasePath(), proci);
1928}
1929
1932(
1933 const fileName& root,
1934 const label proci
1935)
1936{
1937 const word processorName("processor"+Foam::name(proci));
1938 return root/"receive"/processorName;
1939}
1940
1943{
1944 return receivePath(sampleDatabasePath(), proci);
1945}
1946
1949(
1950 const objectRegistry& obr,
1952)
1953{
1954 forAllIters(obr, iter)
1955 {
1956 regIOobject* objPtr = iter.val();
1957 const regIOobject& obj = *objPtr;
1958
1959 if (isA<objectRegistry>(obj))
1960 {
1961 dictionary& d = dict.subDictOrAdd(obj.name());
1962 writeDict(dynamic_cast<const objectRegistry&>(obj), d);
1963 }
1964 else if
1965 (
1967 || writeIOField<vector>(obj, dict)
1970 || writeIOField<tensor>(obj, dict)
1971 )
1972 {
1973 // IOField specialisation
1974 }
1975 else
1976 {
1977 // Not tested. No way of retrieving data anyway ...
1978 OTstream os;
1979 obj.writeData(os);
1980
1981 primitiveEntry* pePtr = new primitiveEntry(obj.name(), os.tokens());
1982 dict.add(pePtr);
1983 }
1984 }
1985}
1986
1989{
1990 // Reverse of writeDict - reads fields from dictionary into objectRegistry
1991 for (const entry& e : d)
1992 {
1993 if (e.isDict())
1994 {
1995 // Add sub registry
1996 objectRegistry& sub = const_cast<objectRegistry&>
1997 (
1998 obr.subRegistry(e.keyword(), true)
1999 );
2000
2001 readDict(e.dict(), sub);
2002 }
2003 else
2004 {
2005 ITstream& is = e.stream();
2006 token tok(is);
2007
2008 if
2009 (
2010 constructIOField<scalar>(e.keyword(), tok, is, obr)
2011 || constructIOField<vector>(e.keyword(), tok, is, obr)
2012 || constructIOField<sphericalTensor>(e.keyword(), tok, is, obr)
2013 || constructIOField<symmTensor>(e.keyword(), tok, is, obr)
2014 || constructIOField<tensor>(e.keyword(), tok, is, obr)
2015 )
2016 {
2017 // Done storing field
2018 }
2019 else
2020 {
2021 FatalErrorInFunction << "Unsupported type " << e.keyword()
2022 << exit(FatalError);
2023 }
2024 }
2025 }
2026}
2027
2030{
2031 os.writeEntry("sampleMode", sampleModeNames_[mode_]);
2032 os.writeEntryIfDifferent<word>("sampleWorld", word::null, sampleWorld_);
2033 os.writeEntryIfDifferent<word>("sampleRegion", word::null, sampleRegion_);
2034 os.writeEntryIfDifferent<word>("samplePatch", word::null, samplePatch_);
2035
2037 {
2038 os.writeEntry("sampleDatabase", Switch::name(true));
2039 // Write database path if differing
2040 os.writeEntryIfDifferent<fileName>
2041 (
2042 "sampleDatabasePath",
2045 );
2046 }
2047 coupleGroup_.write(os);
2048
2049 if
2050 (
2052 && offset_ == vector::zero
2054 )
2055 {
2056 // Collocated mode. No need to write offset data
2057 }
2058 else
2059 {
2060 os.writeEntry("offsetMode", offsetModeNames_[offsetMode_]);
2061
2062 switch (offsetMode_)
2063 {
2064 case UNIFORM:
2065 {
2066 os.writeEntry("offset", offset_);
2067 break;
2068 }
2069 case NONUNIFORM:
2070 {
2071 offsets_.writeEntry("offsets", os);
2072 break;
2073 }
2074 case NORMAL:
2075 {
2076 os.writeEntry("distance", distance_);
2077 break;
2078 }
2079 }
2080 }
2081
2083 {
2084 if (AMIPtr_)
2085 {
2086 // Use AMI to write itself. Problem: outputs:
2087 // - restartUncoveredSourceFace
2088 // - reverseTarget (instead of flipNormals)
2089 AMIPtr_->write(os);
2090 }
2091 if (!surfDict_.empty())
2092 {
2093 surfDict_.writeEntry(surfDict_.dictName(), os);
2094 }
2095 }
2096}
2097
2098
2099// ************************************************************************* //
label n
Macros for easy insertion into run-time selection tables.
Info<< nl;Info<< "Write faMesh in vtk format:"<< nl;{ vtk::uindirectPatchWriter writer(aMesh.patch(), fileName(aMesh.time().globalPath()/vtkBaseFileName));writer.writeGeometry();globalIndex procAddr(aMesh.nFaces());labelList cellIDs;if(UPstream::master()) { cellIDs.resize(procAddr.totalSize());for(const labelRange &range :procAddr.ranges()) { auto slice=cellIDs.slice(range);slice=identity(range);} } writer.beginCellData(4);writer.writeProcIDs();writer.write("cellID", cellIDs);writer.write("area", aMesh.S().field());writer.write("normal", aMesh.faceAreaNormals());writer.beginPointData(1);writer.write("normal", aMesh.pointAreaNormals());Info<< " "<< writer.output().name()<< nl;}{ vtk::lineWriter writer(aMesh.points(), aMesh.edges(), fileName(aMesh.time().globalPath()/(vtkBaseFileName+"-edges")));writer.writeGeometry();writer.beginCellData(4);writer.writeProcIDs();{ Field< scalar > fld(faMeshTools::flattenEdgeField(aMesh.magLe(), true))
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Interpolation class dealing with transfer of data between two primitive patches with an arbitrary mes...
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
void append(const T &val)
Copy append an element to the end of this list.
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition Enum.H:57
Minimal example by using system/controlDict.functions:
bool test(const Key &key) const
Same as contains() - return true if key exists in the set.
Definition HashSet.H:215
bool set(const Key &key)
Same as insert (no value to overwrite).
Definition HashSet.H:237
@ MUST_READ
Reading required.
@ NO_WRITE
Ignore writing from objectRegistry::writeObject().
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition IOobject.H:191
const word & name() const noexcept
Return the object name.
Definition IOobjectI.H:205
An input stream of tokens.
Definition ITstream.H:56
bool empty() const noexcept
True if the list is empty (ie, size() is zero).
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition List.H:72
void setSize(label n)
Alias for resize().
Definition List.H:536
void resize(const label len)
Adjust allocated size of list.
Definition ListI.H:153
static FOAM_NO_DANGLING_REFERENCE const meshSearchMeshObject & New(const polyMesh &mesh, Args &&... args)
Output to file stream as an OSstream, normally using std::ofstream for the actual output.
Definition OFstream.H:75
virtual const fileName & name() const override
Read/write access to the name of the stream.
Definition OSstream.H:134
virtual const fileName & name() const override
Get the name of the output serial stream. (eg, the name of the Fstream file name).
Definition OSstream.H:134
A simple output token stream that can be used to build token lists. Always UNCOMPRESSED.
Definition OTstream.H:56
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition Ostream.H:59
scalar distance() const noexcept
Return distance to hit.
Definition pointHit.H:169
bool hit() const noexcept
Is there a hit.
Definition pointHit.H:145
const point_type & point() const noexcept
Return the point, no checks.
Definition pointHit.H:161
label index() const noexcept
Return the hit index.
bool hit() const noexcept
Is there a hit?
const point_type & point() const noexcept
Return point, no checks.
const labelList & meshPoints() const
Return labelList of mesh points in patch.
const Field< point_type > & localPoints() const
Return pointField of points in patch.
const Field< point_type > & points() const noexcept
Return reference to global points.
const List< face_type > & localFaces() const
Return patch faces addressing into local point list.
Buffers for inter-processor communications streams (UOPstream, UIPstream).
static void allGatherList(UList< T > &values, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Gather data, but keep individual values separate. Uses MPI_Allgather or manual communication.
static void listCombineReduce(UList< T > &values, CombineOp cop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Forwards to Pstream::listReduce with an in-place cop.
Random number generator.
Definition Random.H:56
A non-owning sub-view of a List (allocated or unallocated storage).
Definition SubList.H:61
static const char * name(bool b) noexcept
A string representation of bool as "false" / "true".
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition Time.H:75
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition Tuple2.H:51
const T1 & first() const noexcept
Access the first element.
Definition Tuple2.H:132
const T2 & second() const noexcept
Access the second element.
Definition Tuple2.H:142
A List with indirect addressing. Like IndirectList but does not store addressing.
T & first()
Access first element of the list, position [0].
Definition UList.H:957
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
label find(const T &val) const
Find index of the first occurrence of the value.
Definition UList.C:160
static label commWorld() noexcept
Communicator for all ranks (respecting any local worlds).
Definition UPstream.H:1101
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
Definition UPstream.H:1706
static int & msgType() noexcept
Message tag of standard messages.
Definition UPstream.H:1926
static void reduceAnd(bool &value, const int communicator=worldComm)
Logical (and) reduction (MPI_AllReduce).
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition UPstream.H:1714
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run.
Definition UPstream.H:1697
static label myWorldID()
My worldID.
Definition UPstream.H:1838
static label commWarn(const label communicator) noexcept
Alter communicator debugging setting. Warns for use of any communicator differing from specified....
Definition UPstream.H:1122
static const wordList & allWorlds() noexcept
All worlds.
Definition UPstream.H:1822
static List< T > allGatherValues(const T &localValue, const int communicator=UPstream::worldComm)
Allgather individual values into list locations.
static const word & myWorld()
My world.
Definition UPstream.H:1846
static const Form zero
static const Form max
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition autoPtr.H:65
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition autoPtr.H:178
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition bitSet.H:61
scalar magSqr() const
The magnitude/length squared of bounding box diagonal.
Definition boundBoxI.H:204
Addressing for all faces on surface of mesh. Can either be read from polyMesh or from triSurface....
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
bool found(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry (const access) with the given keyword.
A keyword and a list of tokens is an 'entry'.
Definition entry.H:66
Face area weighted Arbitrary Mesh Interface (AMI) method.
A face is a list of labels corresponding to mesh vertices.
Definition face.H:71
A class for handling file names.
Definition fileName.H:75
wordList components(const char delim='/') const
Return path components as wordList.
Definition fileName.C:532
static bool clean(std::string &str)
Cleanup filename string, possibly applies other transformations such as changing the path separator e...
Definition fileName.C:192
static const fileName null
An empty fileName.
Definition fileName.H:111
Non-pointer based hierarchical recursive searching.
const Type & shapes() const noexcept
Reference to shape.
Class containing processor-to-processor mapping information.
Determines a mapping between patch face centres and mesh cell or face centres and processors they're ...
const fileName & sampleDatabasePath() const
label communicator_
Communicator.
virtual void updateMesh(PstreamBuffers &)
Update of the patch topology.
tmp< pointField > facePoints(const polyPatch &) const
Get the points from face-centre-decomposition face centres and project them onto the face-diagonal-de...
label getWorldCommunicator() const
Get the communicator for the world-world connection.
tmp< pointField > samplePoints(const pointField &) const
Get the sample points given the face points.
const mapDistribute & map() const
Return reference to the parallel distribution map.
scalar distance_
Offset distance (normal).
uniformDimensionedScalarField & updateSampleMeshTime() const
Local mesh update time.
static FOAM_NO_DANGLING_REFERENCE const objectRegistry & subRegistry(const objectRegistry &obr, const wordList &names, const label index)
Lookup (sub)objectRegistry by following names of sub registries. Creates non-existing intermediate on...
const polyPatch & patch_
Patch to sample.
autoPtr< searchableSurface > surfPtr_
Pointer to projection surface employed by AMI interpolator.
vectorField offsets_
Offset vector (nonuniform).
const vectorField & offsets() const noexcept
Offset vectors (from patch faces to destination mesh objects).
void findSamples(const sampleMode mode, const label myWorldIndex, const pointField &, const labelList &wantedWorlds, const labelList &origProcs, labelList &sampleProcs, labelList &sampleIndices, pointField &sampleLocations) const
Find (global) cells/faces containing samples.
Tuple2< pointIndexHit, Tuple2< scalar, label > > nearInfo
Helper class for finding nearest.
uniformDimensionedScalarField & updateMeshTime() const
Sample mesh upate time.
static void readDict(const dictionary &d, objectRegistry &obr)
(recursively) construct and register IOFields from dictionary
sampleMode
Mesh items to sample.
@ NEARESTPATCHPOINT
nearest point on selected patch
@ NEARESTONLYCELL
nearest cell (even if not containing cell)
@ NEARESTCELL
nearest cell containing sample
@ NEARESTPATCHFACE
nearest face on selected patch
@ NEARESTPATCHFACEAMI
nearest patch face + AMI interpolation
void findLocalSamples(const sampleMode mode, const label sampleWorld, const word &sampleRegion, const word &samplePatch, const pointField &samplePoints, List< nearInfoWorld > &nearest) const
Find (local) cells/faces containing samples.
const bool AMIReverse_
Flag to indicate that slave patch should be reversed for AMI.
void calcAMI() const
Calculate AMI interpolator.
static fileName sendPath(const fileName &root, const label proci)
Helper: return path to store data to be sent to processor i.
static autoPtr< fileName > readDatabase(const dictionary &dict)
Read optional database name from dictionary.
static const Enum< sampleMode > sampleModeNames_
const polyPatch & lookupPatch(const word &sampleRegion, const word &samplePatch) const
Lookup patch.
const sampleMode mode_
What to sample.
const polyMesh & sampleMesh() const
Get the region mesh.
virtual void write(Ostream &os) const
Write as a dictionary.
bool sameRegion_
Same region.
word sampleRegion_
Region to sample.
static bool writeIOField(const regIOobject &obj, dictionary &dict)
Attempt to detect an IOField<Type> and write to dictionary.
tmp< pointField > samplePoints() const
Get the sample points.
bool addWorldConnection()
Add a world-world connection.
word samplePatch_
Patch (if in sampleMode NEARESTPATCH*).
autoPtr< mapDistribute > mapPtr_
Communication schedule:
const polyPatch & samplePolyPatch() const
Get the patch on the region.
mappedPatchBase(const polyPatch &)
Construct from patch.
void collectSamples(const label mySampleWorld, const pointField &facePoints, pointField &samples, labelList &patchFaceWorlds, labelList &patchFaceProcs, labelList &patchFaces, pointField &patchFc) const
Collect single list of samples and originating processor+face + wanted world.
const autoPtr< Foam::searchableSurface > & surfPtr() const
Return a pointer to the AMI projection surface.
bool masterWorld() const
Is my world ordered before the sampleWorld?
static fileName receivePath(const fileName &root, const label proci)
Helper: return path to store data to be received from processor i.
label getCommunicator() const
Get the communicator (worldComm or world-to-world).
offsetMode
How to project face centres.
@ NORMAL
use face normal + distance
@ UNIFORM
single offset vector
@ NONUNIFORM
per-face offset vector
const word & sampleWorld() const noexcept
World to sample.
dictionary surfDict_
Dictionary storing projection surface description.
static bool constructIOField(const word &name, token &tok, Istream &is, objectRegistry &obr)
Attempt to read an IOField<Type> and store on objectRegistry.
const word & samplePatch() const
Patch (only if NEARESTPATCHFACE).
static const Enum< offsetMode > offsetModeNames_
virtual ~mappedPatchBase()
Destructor.
void calcMapping() const
Calculate mapping.
static pointIndexHit facePoint(const polyMesh &, const label facei, const polyMesh::cellDecomposition)
Get a point on the face given a face decomposition method:
Tuple2< nearInfo, label > nearInfoWorld
nearest + world
sampleMode mode() const noexcept
What to sample.
const coupleGroupIdentifier coupleGroup_
PatchGroup (if in sampleMode NEARESTPATCH*).
const word & sampleRegion() const
Region to sample.
void setOffset(const scalar normalDist)
Change to normal offset with given distance.
const autoPtr< fileName > sampleDatabasePtr_
Empty or location of database.
const polyMesh & lookupMesh(const word &region) const
Lookup mesh.
vector offset_
Offset vector (uniform).
offsetMode offsetMode_
How to obtain samples.
bool sameWorld() const
Is sample world the local world?
static void writeDict(const objectRegistry &obr, dictionary &dict)
Convert objectRegistry contents into dictionary.
autoPtr< AMIPatchToPatchInterpolation > AMIPtr_
Pointer to AMI interpolator.
word sampleWorld_
World to sample.
MeshObject wrapper around meshSearch(mesh).
label findNearestFace(const point &location, const label seedFacei=-1, const bool useTreeSearch=true) const
Definition meshSearch.C:736
Centralized handling of multi-world MPI connections.
label getCommByName(const word &otherWorld) const
Get communicator for myWorld to other world connection by NAME.
static const multiWorldConnections & New(const Time &runTime)
Access mesh object.
Registry of regIOobjects.
const Time & time() const noexcept
Return time registry.
const objectRegistry & subRegistry(const word &name, const bool forceCreate=false, const bool recursive=false) const
Lookup and return a const sub-objectRegistry.
const Type & lookupObject(const word &name, const bool recursive=false) const
Lookup and return const reference to the object of the given Type. Fatal if not found or the wrong ty...
label findPatchID(const word &patchName, const bool allowNotFound=true) const
Find patch index given a name, return -1 if not found.
wordList names() const
Return a list of patch names.
Mesh consisting of general polyhedral cells.
Definition polyMesh.H:79
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition polyMesh.H:609
cellDecomposition
Enumeration defining the decomposition of the cell for.
Definition polyMesh.H:105
bool moving() const noexcept
Is mesh moving.
Definition polyMesh.H:732
virtual bool upToDatePoints(const regIOobject &io) const
Return true if io is up-to-date with points.
Definition polyMesh.C:1076
virtual const pointField & points() const
Return raw points.
Definition polyMesh.C:1063
const indexedOctree< treeDataCell > & cellTree() const
Return the cell search tree.
Definition polyMesh.C:924
A patch is a list of labels that address the faces in the global face list.
Definition polyPatch.H:73
A keyword and a list of tokens comprise a primitiveEntry. A primitiveEntry can be read,...
const vectorField & faceCentres() const
const vectorField & cellCentres() const
regIOobject is an abstract class derived from IOobject to handle automatic object registration with t...
Definition regIOobject.H:71
virtual bool writeData(Ostream &) const =0
Pure virtual writeData function.
static autoPtr< searchableSurface > New(const word &surfaceType, const IOobject &io, const dictionary &dict)
Return a reference to the selected searchableSurface.
A class for managing temporary objects.
Definition tmp.H:75
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition tmp.H:215
A token holds an item read from Istream.
Definition token.H:70
Standard boundBox with extra functionality for use in octree.
treeBoundBox extend(Random &rndGen, const scalar s) const
Return slightly wider bounding box.
Encapsulation of data for searching on faces.
Holds (reference to) pointField. Encapsulation of data needed for octree searches....
pointHit intersection(const point &p, const vector &q, const intersection::algorithm alg, const scalar tol=0.0) const
Fast intersection with a ray.
Definition triangleI.H:672
A class for handling words, derived from Foam::string.
Definition word.H:66
static const word null
An empty word.
Definition word.H:84
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
volScalarField & p
dynamicFvMesh & mesh
engineTime & runTime
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition error.H:629
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
OBJstream os(runTime.globalPath()/outputName)
auto & names
const pointField & points
#define WarningInFunction
Report a warning using Foam::Warning.
#define DebugInFunction
Report an information message using Foam::Info.
AccessType combine(const UList< T > &lists, AccessOp aop=accessOp< T >())
Combines sub-lists into a single list.
Definition ListListOps.C:62
Namespace for handling debugging switches.
Definition debug.C:45
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition meshTools.C:196
Namespace for OpenFOAM.
Type gAverage(const FieldField< Field, Type > &f, const label comm)
The global arithmetic average of a FieldField.
List< word > wordList
List of word.
Definition fileName.H:60
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh > > &tf1, const word &name, const dimensionSet &dimensions, const bool initCopy=false)
Global function forwards to reuseTmpDimensionedField::New.
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
PointHit< point > pointHit
A PointHit with a 3D point.
Definition pointHit.H:51
List< label > labelList
A List of labels.
Definition List.H:62
dimensionedSymmTensor sqr(const dimensionedVector &dv)
prefixOSstream Perr
OSstream wrapped stderr (std::cerr) with parallel prefix.
UIndirectList< label > labelUIndList
UIndirectList of labels.
label facePoint(const int facei, const block &block, const label i, const label j)
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field.
mode_t mode(const fileName &name, const bool followLink=true)
Return the file mode, normally following symbolic links.
Definition POSIX.C:775
List< face > faceList
List of faces.
Definition faceListFwd.H:41
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition POSIX.C:801
const word GlobalIOList< Tuple2< scalar, vector > >::typeName("scalarVectorTable")
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
const Type * isA(const U &obj)
Attempt dynamic_cast to Type.
Definition typeInfo.H:87
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void reduce(T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce).
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
errorManip< error > abort(error &err)
Definition errorManip.H:139
Field< vector > vectorField
Specialisation of Field<T> for vector.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional 'FOAM FATAL IO ERROR' header text and ...
vector point
Point is a vector.
Definition point.H:37
static constexpr const zero Zero
Global zero (0).
Definition zero.H:127
triangle< point, const point & > triPointRef
A triangle using referred points.
Definition triangleFwd.H:46
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition exprTraits.C:127
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
vectorField pointField
pointField is a vectorField.
Vector< scalar > vector
Definition vector.H:57
PointIndexHit< point > pointIndexHit
A PointIndexHit with a 3D point.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
UList< label > labelUList
A UList of labels.
Definition UList.H:75
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
labelList f(nPoints)
dictionary dict
Tree tree(triangles.begin(), triangles.end())
volScalarField & e
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
#define forAllIters(container, iter)
Iterate across all elements in the container object.
Definition stdFoam.H:214
Object access operator or list access operator (default is pass-through).
Definition UList.H:1120
scalarField samples(nIntervals, Zero)
Random rndGen