ensightMesh.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) 2016-2025 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "ensightMesh.H"
30 #include "ensightGeoFile.H"
31 #include "polyMesh.H"
32 #include "emptyPolyPatch.H"
33 #include "processorPolyPatch.H"
34 #include "ListOps.H"
35 
36 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
37 
38 const Foam::label Foam::ensightMesh::internalZone = -1;
39 
40 
41 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
42 
43 void Foam::ensightMesh::clear()
44 {
45  cellZoneParts_.clear();
46  faceZoneParts_.clear();
47  boundaryParts_.clear();
48 }
49 
50 
51 void Foam::ensightMesh::renumber()
52 {
53  label partNo = 0;
54 
55  for (const label id : cellZoneParts_.sortedToc())
56  {
57  cellZoneParts_[id].index() = partNo++;
58  }
59 
60  for (const label id : boundaryParts_.sortedToc())
61  {
62  boundaryParts_[id].index() = partNo++;
63  }
64 
65  for (const label id : faceZoneParts_.sortedToc())
66  {
67  faceZoneParts_[id].index() = partNo++;
68  }
69 }
70 
71 
72 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
73 
74 Foam::ensightMesh::ensightMesh
75 (
76  const polyMesh& mesh
77 )
78 :
80 {}
81 
82 
83 Foam::ensightMesh::ensightMesh
84 (
85  const polyMesh& mesh,
86  const ensightMesh::options& opts
87 )
88 :
89  options_(new options(opts)),
90  mesh_(mesh),
91  needsUpdate_(true),
92  verbose_(0)
93 {
94  if (!option().lazy())
95  {
97  }
98 }
99 
100 
101 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
104 {
105  return verbose_;
106 }
107 
108 
109 int Foam::ensightMesh::verbose(const int level) noexcept
110 {
111  int old(verbose_);
112  verbose_ = level;
113  return old;
114 }
115 
116 
118 {
119  clear();
120 
121  const auto& pbm = mesh_.boundaryMesh();
122 
123  // Selected patch indices
125 
126  if (option().useBoundaryMesh())
127  {
129  (
130  option().patchSelection(),
131  option().patchExclude()
132  );
133 
134  // Prune undesirable patches - empty and processor patches
135  label count = 0;
136  for (const label patchi : patchIds)
137  {
138  const auto& pp = pbm[patchi];
139 
140  if (isType<emptyPolyPatch>(pp))
141  {
142  continue;
143  }
144  else if (isA<processorPolyPatch>(pp))
145  {
146  break; // No processor patches
147  }
148 
149  patchIds[count] = patchi;
150  ++count;
151  }
153  }
154 
155 
156  // Selection of cellZones
157  const auto& czMatcher = option().cellZoneSelection();
158 
159  // Selected cell zone indices
160  labelList czoneIds;
161 
162  if (option().useCellZones())
163  {
164  // Use allow/deny to have desired behaviour with empty selection
165  czoneIds = mesh_.cellZones().indices
166  (
167  option().cellZoneSelection(),
168  option().cellZoneExclude()
169  );
170  }
171 
172  // Selected face zone indices
173  labelList fzoneIds;
174 
175  if (option().useFaceZones())
176  {
177  // Use allow/deny to have desired behaviour with empty selection
178  fzoneIds = mesh_.faceZones().indices
179  (
180  option().faceZoneSelection(),
181  option().faceZoneExclude()
182  );
183  }
184 
185 
186  // Track which cells are in a zone or not
187  bitSet cellSelection;
188 
189  // Faces to be excluded from export
190  bitSet excludeFace;
191 
192 
193  // cellZones first
194  for (const label zoneId : czoneIds)
195  {
196  const auto& zn = mesh_.cellZones()[zoneId];
197  const auto& zoneName = zn.name();
198 
199  if (returnReduceOr(!zn.empty()))
200  {
201  // Ensure full mesh coverage
202  cellSelection.resize(mesh_.nCells());
203 
204  cellSelection.set(zn);
205 
206  ensightCells& part = cellZoneParts_(zoneId);
207 
208  part.clear();
209  part.identifier() = zoneId;
210  part.rename(zoneName);
211 
212  part.classify(mesh_, zn);
213 
214  // Finalize
215  part.reduce();
216  if (verbose_)
217  {
218  Info<< part.info();
219  }
220  }
221  }
222 
223  if (option().useInternalMesh() && czMatcher.empty())
224  {
225  // The internal mesh:
226  // Either the entire mesh (if no zones specified)
227  // or whatever is leftover as unzoned
228 
229  if (cellZoneParts_.empty())
230  {
231  ensightCells& part = cellZoneParts_(internalZone);
232 
233  part.clear();
234  part.identifier() = internalZone;
235  part.rename("internalMesh");
236 
237  part.classify(mesh_);
238 
239  // Finalize
240  part.reduce();
241  if (verbose_)
242  {
243  Info<< part.info();
244  }
245  }
246  else
247  {
248  // Unzoned cells - flip selection from zoned to unzoned
249  cellSelection.flip();
250 
251  if (returnReduceOr(cellSelection.any()))
252  {
253  ensightCells& part = cellZoneParts_(internalZone);
254 
255  part.clear();
256  part.identifier() = internalZone;
257  part.rename("internalMesh");
258 
259  part.classify(mesh_, cellSelection);
260 
261  // Finalize
262  part.reduce();
263  if (verbose_)
264  {
265  Info<< part.info();
266  }
267  }
268  }
269 
270  // Handled all cells
271  cellSelection.clearStorage();
272  }
273  else if (cellSelection.none())
274  {
275  // No internal, no cellZones selected - just ignore
276  cellSelection.clearStorage();
277  }
278 
279 
280  // Face exclusion based on cellZones
281 
282  if (returnReduceOr(!cellSelection.empty()))
283  {
284  // Ensure full mesh coverage
285  excludeFace.resize(mesh_.nFaces());
286 
287  const labelList& owner = mesh_.faceOwner();
288 
289  forAll(owner, facei)
290  {
291  const label celli = owner[facei];
292 
293  if (!cellSelection.test(celli))
294  {
295  excludeFace.set(facei);
296  }
297  }
298  }
299 
300 
301  // Face exclusion for empty types and neighbour side of coupled
302  // so they are ignored for face zones
303 
304  if (fzoneIds.size())
305  {
306  // Ensure full mesh coverage
307  excludeFace.resize(mesh_.nFaces());
308 
309  for (const polyPatch& p : pbm)
310  {
311  const auto* cpp = isA<coupledPolyPatch>(p);
312 
313  if
314  (
315  isA<emptyPolyPatch>(p)
316  || (cpp && !cpp->owner())
317  )
318  {
319  // Ignore empty patch
320  // Ignore neighbour side of coupled
321  excludeFace.set(p.range());
322  }
323  }
324  }
325 
326 
327  // Patches
328  for (const label patchId : patchIds)
329  {
330  const auto& p = pbm[patchId];
331  const auto& patchName = p.name();
332 
333  if (isA<emptyPolyPatch>(p))
334  {
335  // Skip empty patch types
336  continue;
337  }
338  else if (isA<processorPolyPatch>(p))
339  {
340  // Only real (non-processor) boundaries.
341  break;
342  }
343 
344  ensightFaces& part = boundaryParts_(patchId);
345 
346  part.clear();
347  part.identifier() = patchId;
348  part.rename(patchName);
349 
350  part.classify
351  (
352  mesh_.faces(),
353  identity(p.range()),
354  boolList(), // no flip map
355  excludeFace
356  );
357 
358  // Finalize
359  part.reduce();
360  if (verbose_)
361  {
362  Info<< part.info();
363  }
364  if (!part.total())
365  {
366  boundaryParts_.erase(patchId);
367  }
368  }
369 
370 
371  // Face zones
372  for (const label zoneId : fzoneIds)
373  {
374  const auto& zn = mesh_.faceZones()[zoneId];
375  const auto& zoneName = zn.name();
376 
377  ensightFaces& part = faceZoneParts_(zoneId);
378 
379  part.clear();
380  part.identifier() = zoneId;
381  part.rename(zoneName);
382 
383  if (zn.size())
384  {
385  part.classify
386  (
387  mesh_.faces(),
388  zn,
389  zn.flipMap(),
390  excludeFace
391  );
392  }
393 
394  // Finalize
395  part.reduce();
396  if (verbose_)
397  {
398  Info<< part.info();
399  }
400  if (!part.total())
401  {
402  faceZoneParts_.erase(zoneId);
403  }
404  }
405 
407 
408  needsUpdate_ = false;
409 }
410 
411 
413 (
415  bool parallel
416 ) const
417 {
418  if (UPstream::master())
419  {
420  os.beginGeometry();
421  }
422 
423  // The internalMesh, cellZones
424  for (const label id : cellZoneParts_.sortedToc())
425  {
426  cellZoneParts_[id].write(os, mesh_, parallel);
427  }
428 
429  // Patches - sorted by index
430  for (const label id : boundaryParts_.sortedToc())
431  {
432  boundaryParts_[id].write(os, mesh_, parallel);
433  }
434 
435  // Requested faceZones - sorted by index
436  for (const label id : faceZoneParts_.sortedToc())
437  {
438  faceZoneParts_[id].write(os, mesh_, parallel);
439  }
440 
441  // No geometry parts written?
442  // - with lagrangian-only output the VTK EnsightReader still
443  // needs a volume geometry, and ensight usually does too
444  if
445  (
446  cellZoneParts_.empty()
447  && boundaryParts_.empty()
448  && faceZoneParts_.empty()
449  )
450  {
451  ensightCells::writeBox(os, mesh_.bounds());
452  }
453 }
454 
455 
456 // ************************************************************************* //
label patchId(-1)
labelList patchIds
const polyBoundaryMesh & pbm
virtual Ostream & write(const char c) override
Write character.
Definition: OBJstream.C:69
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:150
const ensightMesh::options & option() const
Reference to the writer/mesh options.
Definition: ensightMeshI.H:23
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:195
IntListType renumber(const labelUList &oldToNew, const IntListType &input)
Renumber the values within a list.
A variant of ensightFile (Ensight writing) that includes the extra geometry file header information...
Various functions to operate on Lists.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:286
labelList indices(const wordRe &matcher, const bool useGroups=true) const
The (sorted) patch indices for all matches, optionally matching patch groups.
void correct()
Update for new mesh.
Definition: ensightMesh.C:110
static void writeBox(ensightGeoFile &os, const boundBox &bb, const label partIndex=0, const word &partName="geometry-box")
Write bounding box geometry. All parameters are only relevant on master No beginGeometry() marker...
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of &#39;true&#39; entries.
Definition: BitOps.H:73
dynamicFvMesh & mesh
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...
Definition: labelLists.C:44
void write(ensightGeoFile &os, bool parallel=UPstream::parRun()) const
Write geometry to file (normally in parallel). Adds beginGeometry() marker.
Definition: ensightMesh.C:406
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
bool returnReduceOr(const bool value, const int communicator=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
const direction noexcept
Definition: scalarImpl.H:255
Encapsulation of volume meshes for writing in ensight format. It manages cellZones, facesZone, patches.
Definition: ensightMesh.H:78
OBJstream os(runTime.globalPath()/outputName)
surface1 clear()
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1619
messageStream Info
Information stream (stdout output on master, null elsewhere)
static const label internalZone
The zone-id for internal mesh or unzoned cells.
Definition: ensightMesh.H:83
Configuration options for the ensightMesh.
Definition: ensightMesh.H:305
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:75
int verbose() const noexcept
Output verbosity level.
Definition: ensightMesh.C:96
List< label > labelList
A List of labels.
Definition: List.H:61
volScalarField & p
List< bool > boolList
A List of bools.
Definition: List.H:59
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())