mapLagrangian.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) 2011-2025 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 "MapLagrangianFields.H"
27 #include "passiveParticleCloud.H"
28 #include "meshBoundarySearch.H"
29 #include "OSspecific.H"
30 
31 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
32 
33 namespace Foam
34 {
35 
36 static const scalar perturbFactor = 1e-6;
37 
38 
39 static label findCell
40 (
41  const meshSearch& searchEngine,
42  const meshBoundarySearch& boundarySearchEngine,
43  const lagrangian::Cloud<passiveParticle>& cloud,
44  const point& pt
45 )
46 {
47  const polyMesh& mesh = cloud.pMesh();
48 
49  // Standard search for the cell that contains the point
50  const label celli = searchEngine.findCell(pt);
51  if (celli >= 0)
52  {
53  return celli;
54  }
55 
56  // See if the particle is on a boundary face by finding the nearest
57  // boundary face and shifting the particle slightly into the owner cell
58  // before doing the standard search again
59  const label facei = boundarySearchEngine.findNearestBoundaryFace(pt);
60  if (facei >= 0)
61  {
62  const point& cc = mesh.cellCentres()[mesh.faceOwner()[facei]];
63  const point perturbPt = (1 - perturbFactor)*pt + perturbFactor*cc;
64  return searchEngine.findCell(perturbPt);
65  }
66 
67  return -1;
68 }
69 
70 
71 void mapLagrangian(const meshToMesh0& meshToMesh0Interp)
72 {
73  // Determine which particles are in meshTarget
74  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75 
76  // target to source cell map
77  const labelList& cellAddressing = meshToMesh0Interp.cellAddressing();
78 
79  // Invert celladdressing to get source to target(s).
80  // Note: could use sparse addressing but that is too storage inefficient
81  // (Map<labelList>)
82  labelListList sourceToTargets
83  (
84  invertOneToMany(meshToMesh0Interp.fromMesh().nCells(), cellAddressing)
85  );
86 
87  const fvMesh& meshSource = meshToMesh0Interp.fromMesh();
88  const fvMesh& meshTarget = meshToMesh0Interp.toMesh();
89 
90  const meshSearch& targetInternalSearchEngine =
91  meshSearch::New(meshTarget);
92  const meshBoundarySearch& targetBoundarySearchEngine =
93  meshBoundarySearch::New(meshTarget);
94 
95  fileNameList cloudDirs
96  (
97  readDir
98  (
99  meshSource.time().timePath()/lagrangian::cloud::prefix,
101  )
102  );
103 
104  forAll(cloudDirs, cloudI)
105  {
106  // Search for list of lagrangian objects for this time
107  IOobjectList objects
108  (
109  meshSource,
110  meshSource.time().name(),
111  lagrangian::cloud::prefix/cloudDirs[cloudI]
112  );
113 
114  IOobject* positionsPtr = objects.lookup("positions");
115 
116  if (positionsPtr)
117  {
118  Info<< nl << " processing cloud " << cloudDirs[cloudI] << endl;
119 
120  // Read positions & cell
121  passiveParticleCloud sourceParcels
122  (
123  meshSource,
124  cloudDirs[cloudI],
125  false
126  );
127  Info<< " read " << sourceParcels.size()
128  << " parcels from source mesh." << endl;
129 
130  // Construct empty target cloud
131  passiveParticleCloud targetParcels
132  (
133  meshTarget,
134  cloudDirs[cloudI],
135  IDLList<passiveParticle>()
136  );
137 
138  passiveParticle::trackingData td(targetParcels);
139 
140  label sourceParticleI = 0;
141 
142  // Indices of source particles that get added to targetParcels
143  DynamicList<label> addParticles(sourceParcels.size());
144 
145  // Unmapped particles
146  labelHashSet unmappedSource(sourceParcels.size());
147 
148 
149  // Initial: track from fine-mesh cell centre to particle position
150  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
151  // This requires there to be no boundary in the way.
152 
153 
155  (
156  lagrangian::Cloud<passiveParticle>,
157  sourceParcels,
158  iter
159  )
160  {
161  bool foundCell = false;
162 
163  // Assume that cell from read parcel is the correct one...
164  if (iter().cell() >= 0)
165  {
166  const labelList& targetCells =
167  sourceToTargets[iter().cell()];
168 
169  // Particle probably in one of the targetcells. Try
170  // all by tracking from their cell centre to the parcel
171  // position.
172 
173  forAll(targetCells, i)
174  {
175  // Track from its cellcentre to position to make sure.
176  autoPtr<passiveParticle> newPtr
177  (
178  new passiveParticle
179  (
180  meshTarget,
181  barycentric(1, 0, 0, 0),
182  targetCells[i],
183  meshTarget.cells()[targetCells[i]][0],
184  1
185  )
186  );
187  passiveParticle& newP = newPtr();
188 
189  newP.track
190  (
191  meshTarget,
192  iter().position(meshSource)
193  - newP.position(meshTarget),
194  0
195  );
196 
197  if (!newP.onFace())
198  {
199  // Hit position.
200  foundCell = true;
201  addParticles.append(sourceParticleI);
202  targetParcels.addParticle(newPtr.ptr());
203  break;
204  }
205  }
206  }
207 
208  if (!foundCell)
209  {
210  // Store for closer analysis
211  unmappedSource.insert(sourceParticleI);
212  }
213 
214  sourceParticleI++;
215  }
216 
217  Info<< " after meshToMesh0 addressing found "
218  << targetParcels.size()
219  << " parcels in target mesh." << endl;
220 
221 
222  // Do closer inspection for unmapped particles
223  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224 
225  if (unmappedSource.size())
226  {
227  sourceParticleI = 0;
228 
229  forAllIter
230  (
231  lagrangian::Cloud<passiveParticle>,
232  sourceParcels,
233  iter
234  )
235  {
236  if (unmappedSource.found(sourceParticleI))
237  {
238  label targetCell =
239  findCell
240  (
241  targetInternalSearchEngine,
242  targetBoundarySearchEngine,
243  targetParcels,
244  iter().position(meshSource)
245  );
246 
247  if (targetCell >= 0)
248  {
249  label nLocateBoundaryHits = 0;
250  autoPtr<passiveParticle> pPtr
251  (
252  new passiveParticle
253  (
254  targetInternalSearchEngine,
255  iter().position(meshSource),
256  targetCell,
257  nLocateBoundaryHits
258  )
259  );
260 
261  if (nLocateBoundaryHits == 0)
262  {
263  unmappedSource.erase(sourceParticleI);
264  addParticles.append(sourceParticleI);
265  targetParcels.addParticle(pPtr.ptr());
266  sourceParcels.remove(&iter());
267  }
268  }
269  }
270  sourceParticleI++;
271  }
272  }
273  addParticles.shrink();
274 
275  Info<< " after additional mesh searching found "
276  << targetParcels.size() << " parcels in target mesh." << endl;
277 
278  if (addParticles.size())
279  {
280  IOPosition<passiveParticleCloud>(targetParcels).write();
281 
282  // addParticles now contains the indices of the sourceMesh
283  // particles that were appended to the target mesh.
284 
285  // Map lagrangian fields
286  // ~~~~~~~~~~~~~~~~~~~~~
287 
288  MapLagrangianFields<label>
289  (cloudDirs[cloudI], objects, meshToMesh0Interp, addParticles);
290  MapLagrangianFields<scalar>
291  (cloudDirs[cloudI], objects, meshToMesh0Interp, addParticles);
292  MapLagrangianFields<vector>
293  (cloudDirs[cloudI], objects, meshToMesh0Interp, addParticles);
294  MapLagrangianFields<sphericalTensor>
295  (cloudDirs[cloudI], objects, meshToMesh0Interp, addParticles);
296  MapLagrangianFields<symmTensor>
297  (cloudDirs[cloudI], objects, meshToMesh0Interp, addParticles);
298  MapLagrangianFields<tensor>
299  (cloudDirs[cloudI], objects, meshToMesh0Interp, addParticles);
300  }
301  }
302  }
303 }
304 
305 
306 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
307 
308 } // End namespace Foam
309 
310 // ************************************************************************* //
Gets the indices of (source)particles that have been appended to the target cloud and maps the lagran...
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:474
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:492
static meshBoundarySearch & New(const word &name, const polyMesh &mesh)
Construct and return the named DemandDrivenMeshObject.
virtual Ostream & write(const token &)
Write token.
Definition: Ostream.C:51
static const word prefix
The prefix to local: lagrangian.
Definition: cloud.H:69
static const meshSearch & New(const polyMesh &mesh, const pointInCellShapes=pointInCellShapes::tets)
Lookup or construct from mesh and cell decomposition option.
Definition: meshSearch.C:61
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1321
const vectorField & cellCentres() const
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
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
Namespace for OpenFOAM.
const doubleScalar e
Definition: doubleScalar.H:106
List< fileName > fileNameList
A List of fileNames.
Definition: fileNameList.H:50
Barycentric< scalar > barycentric
A scalar version of the templated Barycentric.
Definition: barycentric.H:45
List< label > labelList
A List of labels.
Definition: labelList.H:56
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
messageStream Info
labelListList invertOneToMany(const label len, const labelUList &)
Invert one-to-many map. Unmapped elements will be size 0.
Definition: ListOps.C:67
vector point
Point is a vector.
Definition: point.H:41
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
void mapLagrangian(const meshToMesh0 &meshToMesh0Interp)
Maps lagrangian positions and fields.
HashSet< label, Hash< label > > labelHashSet
A HashSet with label keys.
Definition: HashSet.H:213
static const char nl
Definition: Ostream.H:297
fileNameList readDir(const fileName &, const fileType=fileType::file, const bool filterVariants=true, const bool followLink=true)
Read a directory and return the entries as a string list.
Definition: POSIX.C:662
objects