ensightOutputTemplates.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) 2019-2025 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "ensightOutput.H"
29 #include "ensightPTraits.H"
30 #include "globalIndex.H"
31 
32 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
33 
34 template<class LabelListListType>
36 (
38  const LabelListListType& listOfLists,
39  const label pointOffset
40 )
41 {
42  const label off = (pointOffset + 1); // 1-based for Ensight
43 
44  forAll(listOfLists, listi)
45  {
46  for (const label pointi : listOfLists[listi])
47  {
48  os.write(pointi + off);
49  }
50  os.newline(); // One list (cell/faces) per line (ASCII)
51  }
52 }
53 
54 
55 template<template<typename> class FieldContainer, class Type>
57 (
58  const FieldContainer<Type>& input,
59  const direction cmpt,
60  UList<float>& cmptBuffer
61 )
62 {
63  typedef typename pTraits<Type>::cmptType cmptType;
64 
65  if (cmptBuffer.size() < input.size())
66  {
68  << "Component buffer too small: "
69  << cmptBuffer.size() << " < " << input.size() << nl
70  << exit(FatalError);
71  }
72 
73  auto iter = cmptBuffer.begin();
74 
75  for (const Type& val : input)
76  {
77  if constexpr (std::is_same_v<float, cmptType>)
78  {
79  // Direct copy
80  *iter = component(val, cmpt);
81  }
82  else
83  {
84  // Copy with narrowing
85  *iter = narrowFloat(component(val, cmpt));
86  }
87  ++iter;
88  }
89 }
90 
91 
92 template<template<typename> class FieldContainer, class Type>
94 (
96  ensightFile& os,
97  const char* key,
98  const FieldContainer<Type>& fld,
99  bool parallel
100 )
101 {
102  parallel = parallel && Pstream::parRun();
103 
104  const label localSize = fld.size();
105 
106  // Gather sizes (offsets irrelevant)
107  const globalIndex procAddr
108  (
109  parallel
110  ? globalIndex(globalIndex::gatherOnly{}, localSize)
111  : globalIndex(globalIndex::gatherNone{}, localSize)
112  );
113 
114  if (Pstream::master() && key)
115  {
116  os.writeKeyword(key);
117  }
118 
119  if (Pstream::master())
120  {
121  // Scratch buffer:
122  // - allocate enough space to process an individual rank
123  // - potentially enough to process multiple ranks before writing
124  // - permit use of the full buffer capacity
125 
126  // Buffer size needed for an individual rank
127  const label minSize(max(localSize, procAddr.maxSize()));
128 
129  // Maximum off-processor transfer size
130  const label maxSize =
131  (
133  ? min
134  (
135  static_cast<label>(ensightOutput::maxChunk_),
136  (procAddr.totalSize() - localSize)
137  )
138  : scratch.capacity()
139  );
140 
141  scratch.resize_nocopy
142  (
143  max(max(minSize, maxSize), scratch.capacity())
144  );
145 
146  if (Pstream::master() && debug > 1)
147  {
148  Info<< "ensight";
149  if (key)
150  {
151  Info<< " (" << key << ')';
152  }
153 
154  Info<< " total-size:" << procAddr.totalSize()
155  << " buf-size:" << scratch.size() << "/" << scratch.capacity()
156  << " any-proc:" << minSize
157  << " off-proc:" << (procAddr.totalSize() - localSize) << endl;
158 
159  Info<< "proc-sends: (";
160 
161  label nPending = localSize;
162 
163  Info<< (localSize ? '0' : '_');
164 
165  // Receive others, writing as needed
166  for (const label proci : procAddr.subProcs())
167  {
168  const label procSize = procAddr.localSize(proci);
169 
170  if (procSize)
171  {
172  if (nPending + procSize > scratch.size())
173  {
174  // Flush buffer
175  nPending = 0;
176  Info<< ") (";
177  }
178  else
179  {
180  Info<< ' ';
181  }
182 
183  Info<< proci;
184  nPending += procSize;
185  }
186  }
187 
188  Info<< ')' << endl;
189  }
190 
192  {
193  const direction cmpt = ensightPTraits<Type>::componentOrder[d];
194 
195  // Master
196  copyComponent(fld, cmpt, scratch);
197  label nPending = localSize;
198 
199  // Receive others, writing as needed
200  for (const label proci : procAddr.subProcs())
201  {
202  const label procSize = procAddr.localSize(proci);
203 
204  if (procSize)
205  {
206  if (nPending + procSize > scratch.size())
207  {
208  // Flush buffer
209  os.writeList(SubList<float>(scratch, nPending));
210  nPending = 0;
211  }
212 
213  SubList<float> slot(scratch, procSize, nPending);
214  nPending += procSize;
215 
217  (
218  UPstream::commsTypes::scheduled,
219  proci,
220  slot
221  );
222  }
223  }
224 
225  if (nPending)
226  {
227  // Flush buffer
228  os.writeList(SubList<float>(scratch, nPending));
229  }
230  }
231  }
232  else if (parallel && localSize)
233  {
234  scratch.resize_nocopy(localSize);
235 
237  {
238  const direction cmpt = ensightPTraits<Type>::componentOrder[d];
239 
240  copyComponent(fld, cmpt, scratch);
241 
243  (
244  UPstream::commsTypes::scheduled,
245  UPstream::masterNo(),
246  scratch
247  );
248  }
249  }
250 }
251 
252 
253 template<template<typename> class FieldContainer>
255 (
256  ensightGeoFile& os,
257  const label partId,
258  const word& partName,
259  const label nPoints,
260  const FieldContainer<Foam::point>& fld,
261  bool parallel
262 )
263 {
264  if (Pstream::master())
265  {
266  os.beginPart(partId, partName);
267  os.beginCoordinates(nPoints);
268  }
269 
270  bool ok = (Pstream::master() && (nPoints > 0));
271  if (parallel)
272  {
273  Pstream::broadcast(ok);
274  }
275 
276  if (ok)
277  {
280  (
281  scratch,
282  os,
283  nullptr, // (no element type)
284  fld,
285  parallel
286  );
287  }
289  return true;
290 }
291 
292 
293 template<class Type>
295 (
297  ensightFile& os,
298  const Field<Type>& fld,
299  const ensightFaces& part,
300  bool parallel
301 )
302 {
303  parallel = parallel && Pstream::parRun();
304 
305  // Need geometry and field. part.total() is already reduced
306  const bool good =
307  (
308  parallel
309  ? (part.total() && returnReduceOr(fld.size()))
310  : (part.size() && fld.size())
311  );
312 
313  if (!good)
314  {
315  return false;
316  }
317 
318 
319  if (Pstream::master())
320  {
321  os.beginPart(part.index());
322  }
323 
324  for (int typei=0; typei < ensightFaces::nTypes; ++typei)
325  {
326  const auto etype = ensightFaces::elemType(typei);
327 
328  // Write elements of this type
329  if (parallel ? part.total(etype) : part.size(etype))
330  {
332  (
333  scratch,
334  os,
335  ensightFaces::key(etype),
336  SubField<Type>(fld, part.range(etype)),
337  parallel
338  );
339  }
340  }
342  return true;
343 }
344 
345 
346 template<class Type>
348 (
350  ensightFile& os,
351  const Field<Type>& fld,
352  const ensightFaces& part,
353  bool parallel
354 )
355 {
356  parallel = parallel && Pstream::parRun();
357 
358  // Need geometry and field. part.total() is already reduced
359  const bool good =
360  (
361  parallel
362  ? (part.total() && returnReduceOr(fld.size()))
363  : (part.size() && fld.size())
364  );
365 
366  if (!good)
367  {
368  return false;
369  }
370 
371 
372  bool validAddressing = (part.size() == part.faceOrder().size());
373 
374  if (parallel)
375  {
376  Pstream::reduceOr(validAddressing);
377  }
378 
379  if (!validAddressing)
380  {
382  << "Called without faceOrder having been set" << nl
383  << exit(FatalError);
384  }
385 
386 
387  if (Pstream::master())
388  {
389  os.beginPart(part.index());
390  }
391 
392  for (int typei=0; typei < ensightFaces::nTypes; ++typei)
393  {
394  const auto etype = ensightFaces::elemType(typei);
395 
396  // Write elements of this type
397  if (parallel ? part.total(etype) : part.size(etype))
398  {
400  (
401  scratch,
402  os,
403  ensightFaces::key(etype),
404  UIndirectList<Type>(fld, part.faceOrder(etype)),
405  parallel
406  );
407  }
408  }
409 
410  return true;
411 }
412 
413 
414 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
415 
416 template<class Type>
418 (
420  ensightFile& os,
421  const Field<Type>& fld,
422  const ensightCells& part,
423  bool parallel
424 )
425 {
426  parallel = parallel && Pstream::parRun();
427 
428  // Need geometry and field. part.total() is already reduced
429  const bool good =
430  (
431  parallel
432  ? (part.total() && returnReduceOr(fld.size()))
433  : (part.size() && fld.size())
434  );
435 
436  if (!good)
437  {
438  return false;
439  }
440 
441 
442  if (Pstream::master())
443  {
444  os.beginPart(part.index());
445  }
446 
447  for (int typei=0; typei < ensightCells::nTypes; ++typei)
448  {
449  const auto etype = ensightCells::elemType(typei);
450 
451  // Write elements of this type
452  if (parallel ? part.total(etype) : part.size(etype))
453  {
455  (
456  scratch,
457  os,
458  ensightCells::key(etype),
459  UIndirectList<Type>(fld, part.cellIds(etype)),
460  parallel
461  );
462  }
463  }
465  return true;
466 }
467 
468 
469 template<class Type>
471 (
473  ensightFile& os,
474  const Field<Type>& fld,
475  const ensightFaces& part,
476  bool parallel
477 )
478 {
479  parallel = parallel && Pstream::parRun();
480 
481  // Need geometry and field. part.total() is already reduced
482  const bool good =
483  (
484  parallel
485  ? (part.total() && returnReduceOr(fld.size()))
486  : (part.size() && fld.size())
487  );
488 
489  if (!good)
490  {
491  return false;
492  }
493 
494 
495  if (Pstream::master())
496  {
497  os.beginPart(part.index());
498  }
499 
500  for (int typei=0; typei < ensightFaces::nTypes; ++typei)
501  {
502  const auto etype = ensightFaces::elemType(typei);
503 
504  // Write elements of this type
505  if (parallel ? part.total(etype) : part.size(etype))
506  {
508  (
509  scratch,
510  os,
511  ensightFaces::key(etype),
512  UIndirectList<Type>(fld, part.faceIds(etype)),
513  parallel
514  );
515  }
516  }
517 
518  return true;
519 }
520 
521 
522 // ************************************************************************* //
A variant of OFstream with specialised handling for Ensight writing of strings, integers and floats (...
Definition: ensightFile.H:47
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
uint8_t direction
Definition: direction.H:46
float narrowFloat(const double val)
Type narrowing from double to float.
Definition: scalar.H:240
label total() const noexcept
Same as totalSize.
Definition: ensightFaces.H:203
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:600
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:529
A traits class, which is primarily used for primitives and vector-space.
Definition: pTraits.H:61
A variant of ensightFile (Ensight writing) that includes the extra geometry file header information...
labelRange range(const elemType etype) const
Processor-local offset/size of element type.
Definition: ensightFacesI.H:66
Dispatch tag: Construct &#39;one-sided&#39; from local sizes, using gather but no broadcast.
Definition: globalIndex.H:117
Sorting/classification of faces (2D) into corresponding ensight types.
Definition: ensightFaces.H:66
bool writeFaceSubField(ensightOutput::floatBufferType &scratch, ensightFile &os, const Field< Type > &fld, const ensightFaces &part, bool parallel)
Write a sub-field of faces values as an indirect list, using the sub-list sizing information from ens...
const labelList & faceOrder() const noexcept
Processor-local face order (where applicable)
Definition: ensightFacesI.H:98
bool broadcast(Type *values, int count, MPI_Datatype datatype, const int communicator)
bool writeFaceLocalField(ensightOutput::floatBufferType &scratch, ensightFile &os, const Field< Type > &fld, const ensightFaces &part, bool parallel)
Write a field of faces values as an indirect list, using the face order from ensightFaces.
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept
The number of components associated with given valueTypeCode.
Definition: exprTraits.C:40
bool writeField(ensightOutput::floatBufferType &scratch, ensightFile &os, const Field< Type > &fld, const ensightCells &part, bool parallel)
Write a field of cell values as an indirect list, using the cell ids from ensightCells.
label capacity() const noexcept
Size of the underlying storage.
Definition: DynamicList.H:236
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:286
void copyComponent(const FieldContainer< Type > &input, const direction cmpt, UList< float > &cmptBuffer)
Copy specified field component into a scalar buffer. Works for various lists types. Must be adequately sized before calling.
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:61
label total() const noexcept
Same as totalSize()
Definition: ensightCells.H:232
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:51
void writeFieldComponents(ensightOutput::floatBufferType &scratch, ensightFile &os, const char *key, const FieldContainer< Type > &fld, bool parallel)
Write field content (component-wise) for the given ensight element type.
Generic templated field type.
Definition: Field.H:63
label size(const elemType etype) const
Processor-local size of the specified element type.
Definition: ensightFacesI.H:60
label nPoints
Sorting/classification of cells (3D) into corresponding ensight element types.
Definition: ensightCells.H:50
static Istream & input(Istream &is, IntRange< T > &range)
Definition: IntRanges.C:33
label size(const elemType etype) const
Processor-local size of the specified element type.
Definition: ensightCellsI.H:60
auto key(const Type &t) -> std::enable_if_t< std::is_enum_v< Type >, std::underlying_type_t< Type > >
Definition: foamGltfBase.H:103
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
bool returnReduceOr(const bool value, const int communicator=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
iterator begin() noexcept
Return an iterator to begin traversing the UList.
Definition: UListI.H:385
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:105
DynamicList< float > floatBufferType
The list type used for component-wise buffering.
const labelList & faceIds() const noexcept
Processor-local face ids of all elements.
Definition: ensightFacesI.H:72
void read(Istream &, label &val, const dictionary &)
In-place read with dictionary lookup.
int debug
Static debugging option.
OBJstream os(runTime.globalPath()/outputName)
label index() const noexcept
The index in a list (0-based)
Definition: ensightPart.H:153
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< ' ';}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< ' ';}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< ' ';}gmvFile<< nl;for(const word &name :lagrangianScalarNames){ IOField< scalar > fld(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
void writeLabelListList(ensightGeoFile &os, const labelUList &offsets, const labelUList &values, const label pointOffset)
Write CompactListList<label> by components.
globalIndex procAddr(aMesh.nFaces())
messageStream Info
Information stream (stdout output on master, null elsewhere)
void resize_nocopy(const label len)
Alter addressable list size, allocating new space if required without necessarily recovering old cont...
Definition: DynamicListI.H:388
int maxChunk_
Upper limit on number of items for bundled off-processor field transfers. The component-wise transfer...
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
bool writeCoordinates(ensightGeoFile &os, const label partId, const word &partName, const label nPoints, const FieldContainer< Foam::point > &fld, bool parallel)
Write coordinates (component-wise) for the given part.
const labelList & cellIds() const
Processor-local cell ids of all elements.
Definition: ensightCellsI.H:72