foamVtkPatchMeshWriter.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) 2016-2023 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 "foamVtkPatchMeshWriter.H"
29 #include "foamVtkOutput.H"
30 #include "globalIndex.H"
31 #include "Time.H"
32 #include "processorPolyPatch.H"
33 
34 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
35 
37 {
38  // Basic sizes
40 
42  nLocalPolyConn_ = 0;
43 
44  for (const label patchId : patchIDs_)
45  {
46  const polyPatch& pp = patches[patchId];
47 
48  nLocalPoints_ += pp.nPoints();
49  nLocalPolys_ += pp.size();
50 
51  for (const face& f : pp)
52  {
53  nLocalPolyConn_ += f.size();
54  }
55  }
56 
59 
60  if (parallel_)
61  {
64  }
65 
66 
67  // Nothing else to do for legacy
68  if (legacy()) return;
69 
70 
71  if (format_)
72  {
73  format()
74  .tag
75  (
79  );
80  }
81 }
82 
83 
85 {
86  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
87 
88  this->beginPoints(numberOfPoints_);
89 
90  if (parallel_ ? Pstream::master() : true)
91  {
92  for (const label patchId : patchIDs_)
93  {
94  const polyPatch& pp = patches[patchId];
95 
96  vtk::writeList(format(), pp.localPoints());
97  }
98  }
99 
100 
101  if (parallel_)
102  {
103  // Patch Ids are identical across all processes
104  const label nPatches = patchIDs_.size();
105 
106  if (Pstream::master())
107  {
108  pointField recv;
109 
110  // Receive each point field and write
111  for (const int subproci : Pstream::subProcs())
112  {
113  IPstream fromProc(Pstream::commsTypes::blocking, subproci);
114 
115  for (label i=0; i < nPatches; ++i)
116  {
117  fromProc >> recv;
118 
119  vtk::writeList(format(), recv);
120  }
121  }
122  }
123  else
124  {
125  // Send each point field
126  OPstream toProc
127  (
130  );
131 
132  for (const label patchId : patchIDs_)
133  {
134  const polyPatch& pp = patches[patchId];
135 
136  toProc << pp.localPoints();
137  }
138  }
139  }
140 
141 
142  this->endPoints();
143 }
144 
145 
146 void Foam::vtk::patchMeshWriter::writePolysLegacy(const label pointOffset)
147 {
148  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
149 
150  // Connectivity count without additional storage (done internally)
151 
152  label nPolys = nLocalPolys_;
153  label nPolyConn = nLocalPolyConn_;
154 
155  if (parallel_)
156  {
157  reduce(nPolys, sumOp<label>());
158  reduce(nPolyConn, sumOp<label>());
159  }
160 
161  if (nPolys != numberOfCells_)
162  {
164  << "Expecting " << numberOfCells_
165  << " faces, but found " << nPolys
166  << exit(FatalError);
167  }
168 
169  legacy::beginPolys(os_, nPolys, nPolyConn);
170 
171  labelList vertLabels(nLocalPolys_ + nLocalPolyConn_);
172 
173  {
174  // Legacy: size + connectivity together
175  // [nPts, id1, id2, ..., nPts, id1, id2, ...]
176 
177  auto iter = vertLabels.begin();
178 
179  label off = pointOffset;
180 
181  for (const label patchId : patchIDs_)
182  {
183  const polyPatch& pp = patches[patchId];
184 
185  for (const face& f : pp.localFaces())
186  {
187  *iter = f.size(); // The size prefix
188  ++iter;
189 
190  for (const label id : f)
191  {
192  *iter = id + off; // Face vertex label
193  ++iter;
194  }
195  }
196  off += pp.nPoints();
197  }
198  }
199 
200 
201  if (parallel_)
202  {
203  vtk::writeListParallel(format_.ref(), vertLabels);
204  }
205  else
206  {
207  vtk::writeList(format(), vertLabels);
208  }
209 
210  if (format_)
211  {
212  format().flush();
213  }
214 }
215 
216 
217 void Foam::vtk::patchMeshWriter::writePolys(const label pointOffset)
218 {
219  if (format_)
220  {
222  }
223 
224  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
225 
226  //
227  // 'connectivity'
228  //
229  {
230  labelList vertLabels(nLocalPolyConn_);
231 
232  label nVerts = nLocalPolyConn_;
233 
234  if (parallel_)
235  {
236  reduce(nVerts, sumOp<label>());
237  }
238 
239  if (format_)
240  {
241  const uint64_t payLoad =
242  vtk::sizeofData<label>(nVerts);
243 
244  format().beginDataArray<label>(vtk::dataArrayAttr::CONNECTIVITY);
245  format().writeSize(payLoad);
246  }
247 
248  {
249  // XML: connectivity only
250  // [id1, id2, ..., id1, id2, ...]
251 
252  auto iter = vertLabels.begin();
253 
254  label off = pointOffset;
255 
256  for (const label patchId : patchIDs_)
257  {
258  const polyPatch& pp = patches[patchId];
259 
260  for (const face& f : pp.localFaces())
261  {
262  for (const label id : f)
263  {
264  *iter = id + off; // Face vertex label
265  ++iter;
266  }
267  }
268  off += pp.nPoints();
269  }
270  }
271 
272 
273  if (parallel_)
274  {
275  vtk::writeListParallel(format_.ref(), vertLabels);
276  }
277  else
278  {
279  vtk::writeList(format(), vertLabels);
280  }
281 
282  if (format_)
283  {
284  format().flush();
285  format().endDataArray();
286  }
287  }
288 
289 
290  //
291  // 'offsets' (connectivity offsets)
292  //
293  {
294  labelList vertOffsets(nLocalPolys_);
295  label nOffs = vertOffsets.size();
296 
297  if (parallel_)
298  {
299  reduce(nOffs, sumOp<label>());
300  }
301 
302  if (format_)
303  {
304  const uint64_t payLoad =
305  vtk::sizeofData<label>(nOffs);
306 
307  format().beginDataArray<label>(vtk::dataArrayAttr::OFFSETS);
308  format().writeSize(payLoad);
309  }
310 
311 
312  // processor-local connectivity offsets
313  label off =
314  (
315  parallel_ ? globalIndex(nLocalPolyConn_).localStart() : 0
316  );
317 
318 
319  auto iter = vertOffsets.begin();
320 
321  for (const label patchId : patchIDs_)
322  {
323  const polyPatch& pp = patches[patchId];
324 
325  for (const face& f : pp)
326  {
327  off += f.size(); // End offset
328  *iter = off;
329  ++iter;
330  }
331  }
332 
333 
334  if (parallel_)
335  {
336  vtk::writeListParallel(format_.ref(), vertOffsets);
337  }
338  else
339  {
340  vtk::writeList(format_.ref(), vertOffsets);
341  }
342 
343 
344  if (format_)
345  {
346  format().flush();
347  format().endDataArray();
348  }
349  }
350 
351  if (format_)
352  {
353  format().endTag(vtk::fileTag::POLYS);
354  }
355 }
356 
357 
358 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
359 
361 (
362  const polyMesh& mesh,
363  const labelList& patchIDs,
364  const vtk::outputOptions opts
365 )
366 :
367  vtk::fileWriter(vtk::fileTag::POLY_DATA, opts),
368  numberOfPoints_(0),
369  numberOfCells_(0),
370  nLocalPoints_(0),
371  nLocalPolys_(0),
372  nLocalPolyConn_(0),
373 
374  mesh_(mesh),
375  patchIDs_(patchIDs)
376 {
377  // We do not currently support append mode
378  opts_.append(false);
379 }
380 
381 
383 (
384  const polyMesh& mesh,
385  const labelList& patchIDs,
386  const fileName& file,
387  bool parallel
388 )
389 :
391 {
392  open(file, parallel);
393 }
394 
395 
397 (
398  const polyMesh& mesh,
399  const labelList& patchIDs,
400  const vtk::outputOptions opts,
401  const fileName& file,
402  bool parallel
403 )
404 :
406 {
407  open(file, parallel);
408 }
409 
410 
411 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
412 
413 bool Foam::vtk::patchMeshWriter::beginFile(std::string title)
414 {
415  if (title.size())
416  {
417  return vtk::fileWriter::beginFile(title);
418  }
419 
420  // Provide default title
421 
422  if (legacy())
423  {
424  title =
425  (
426  patchIDs_.size() == 1
427  ? mesh_.boundaryMesh()[patchIDs_.first()].name()
428  : "patches"
429  );
430 
431  return vtk::fileWriter::beginFile(title);
432  }
433 
434 
435  // XML (inline)
436 
437  if (patchIDs_.size() == 1)
438  {
439  title =
440  (
441  "patch='" + mesh_.boundaryMesh()[patchIDs_.first()].name() + "'"
442  );
443  }
444  else
445  {
446  title =
447  (
448  "npatches='" + Foam::name(patchIDs_.size()) + "'"
449  );
450  }
451 
452  title +=
453  (
454  " time='" + mesh_.time().timeName()
455  + "' index='" + Foam::name(mesh_.time().timeIndex())
456  + "'"
457  );
458 
459  return vtk::fileWriter::beginFile(title);
460 }
461 
462 
464 {
465  enter_Piece();
466 
467  beginPiece();
468 
469  writePoints();
470 
471  const label pointOffset =
472  (
473  parallel_ ? globalIndex(nLocalPoints_).localStart() : 0
474  );
475 
476  if (legacy())
477  {
478  writePolysLegacy(pointOffset);
479  }
480  else
481  {
482  writePolys(pointOffset);
483  }
484 
485  return true;
486 }
487 
490 {
491  return enter_CellData(numberOfCells_, nFields);
492 }
493 
496 {
497  return enter_PointData(numberOfPoints_, nFields);
498 }
499 
500 
502 {
503  if (isState(outputState::CELL_DATA))
504  {
505  ++nCellData_;
506  }
507  else
508  {
510  << " for patchID field" << nl << endl
511  << exit(FatalError);
512  }
513 
514  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
515 
516  label nPolys = nLocalPolys_;
517 
518  if (parallel_)
519  {
520  reduce(nPolys, sumOp<label>());
521  }
522 
523 
524  this->beginDataArray<label>("patchID", nPolys);
525 
526  if (parallel_ ? Pstream::master() : true)
527  {
528  for (const label patchId : patchIDs_)
529  {
530  vtk::write(format(), patchId, patches[patchId].size());
531  }
532  }
533 
534  if (parallel_)
535  {
536  if (Pstream::master())
537  {
538  labelList recv;
539 
540  // Receive each pair
541  for (const int subproci : Pstream::subProcs())
542  {
543  IPstream fromProc(Pstream::commsTypes::blocking, subproci);
544 
545  fromProc >> recv;
546 
547  // Receive as [size, id] pairs
548  for (label i=0; i < recv.size(); i += 2)
549  {
550  const label len = recv[i];
551  const label val = recv[i+1];
552 
553  vtk::write(format(), val, len);
554  }
555  }
556  }
557  else
558  {
559  // Send
560  OPstream toProc
561  (
564  );
565 
566  // Encode as [size, id] pairs
567  labelList send(2*patchIDs_.size());
568  label i = 0;
569  for (const label patchId : patchIDs_)
570  {
571  send[i] = patches[patchId].size();
572  send[i+1] = patchId;
573 
574  i += 2;
575  }
576 
577  toProc << send;
578  }
579  }
580 
581 
582  this->endDataArray();
583 }
584 
585 
587 {
588  if (this->isPointData())
589  {
590  return vtk::fileWriter::writeProcIDs(nLocalPoints_);
591  }
592  return vtk::fileWriter::writeProcIDs(nLocalPolys_);
593 }
594 
595 
597 {
598  if (!Pstream::parRun())
599  {
600  // Skip in non-parallel
601  return false;
602  }
603 
604  if (isState(outputState::CELL_DATA))
605  {
606  ++nCellData_;
607  }
608  else
609  {
611  << " for patchID field" << nl << endl
612  << exit(FatalError);
613  }
614 
615  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
616 
617  label nPolys = nLocalPolys_;
618 
619  if (parallel_)
620  {
621  reduce(nPolys, sumOp<label>());
622  }
623 
624 
625  this->beginDataArray<label>("neighID", nPolys);
626 
627  bool good = false;
628 
629  if (parallel_ ? Pstream::master() : true)
630  {
631  for (const label patchId : patchIDs_)
632  {
633  const auto* pp = isA<processorPolyPatch>(patches[patchId]);
634 
635  const label val = (pp ? pp->neighbProcNo() : -1);
636 
637  vtk::write(format(), val, patches[patchId].size());
638  }
639 
640  good = true;
641  }
642 
643  if (parallel_)
644  {
645  if (Pstream::master())
646  {
647  labelList recv;
648 
649  // Receive each pair
650  for (const int subproci : Pstream::subProcs())
651  {
652  IPstream fromProc(Pstream::commsTypes::blocking, subproci);
653 
654  fromProc >> recv;
655 
656  // Receive as [size, id] pairs
657  for (label i=0; i < recv.size(); i += 2)
658  {
659  const label len = recv[i];
660  const label val = recv[i+1];
661 
662  vtk::write(format(), val, len);
663  }
664  }
665  }
666  else
667  {
668  // Send
669  OPstream toProc
670  (
673  );
674 
675  // Encode as [size, id] pairs
676  labelList send(2*patchIDs_.size());
677  label i = 0;
678  for (const label patchId : patchIDs_)
679  {
680  const auto* pp = isA<processorPolyPatch>(patches[patchId]);
681 
682  send[i] = patches[patchId].size();
683  send[i+1] = (pp ? pp->neighbProcNo() : -1);
684 
685  i += 2;
686  }
687 
688  toProc << send;
689  }
690  }
691 
692  this->endDataArray();
693 
694  // MPI barrier
695  return parallel_ ? returnReduceOr(good) : good;
696 }
697 
698 
699 // ************************************************************************* //
label nPatches
Definition: readKivaGrid.H:396
label patchId(-1)
const labelList patchIDs(pbm.indices(polyPatchNames, true))
Write OpenFOAM patches and patch fields in VTP or legacy vtk format.
autoPtr< vtk::formatter > format_
The VTK formatter in use (only valid on master process)
"blocking" : (MPI_Bsend, MPI_Recv)
A class for handling file names.
Definition: fileName.H:72
virtual bool beginCellData(label nFields=0)
Begin CellData output section for specified number of fields.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
patchMeshWriter(const patchMeshWriter &)=delete
No copy construct.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
vtk::formatter & format()
The VTK formatter in use. FatalError for off-processor.
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:598
label nLocalPolys_
Local number of polys (faces)
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
void writePatchIDs()
Write patch ids as CellData.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1049
void beginPolys(std::ostream &os, label nPolys, label nConnectivity)
Emit header for POLYGONS (with trailing newline).
label numberOfPoints_
The number of field points for the current Piece.
bool parallel_
Parallel writing (via master)
void beginPiece()
Determine sizes (nLocalPoints_, nLocalPolys_), and begin piece.
bool legacy() const noexcept
Commonly used query.
Encapsulated combinations of output format options. This is primarily useful when defining the output...
label numberOfCells_
The number of field cells (faces) for the current Piece.
virtual bool beginPointData(label nFields=0)
Begin PointData for specified number of fields.
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
void beginPoints(std::ostream &os, label nPoints)
Emit header for POINTS (with trailing newline).
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:61
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
dynamicFvMesh & mesh
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
void writePolys(const label pointOffset)
Write patch faces.
bool writeProcIDs()
Write processor ids for each line as CellData or for each point as PointData, depending on isPointDat...
bool writeProcIDs(const label nValues)
Write nValues of processor ids as CellData or PointData (no-op in serial)
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:608
static constexpr int masterNo() noexcept
Relative rank for the master process - is always 0.
Definition: UPstream.H:1059
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO...
void writeList(vtk::formatter &fmt, const UList< uint8_t > &values)
Write a list of uint8_t values.
label nLocalPoints_
Local number of points.
labelList f(nPoints)
virtual bool writeGeometry()
Write patch topology.
word format(conversionProperties.get< word >("format"))
virtual bool beginFile(std::string title="")
Write file header (non-collective)
labelList patchIDs_
The selected patch ids.
fileTag
Some common XML tags for vtk files.
Definition: foamVtkCore.H:122
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
const polyBoundaryMesh & patches
void writePoints()
Write patch points.
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
bool writeNeighIDs()
Write processor neighbour ids as CellData. This is no-op in serial.
label localStart(const label proci) const
Start of proci data.
Definition: globalIndexI.H:233
void writeListParallel(vtk::formatter &fmt, const UList< Type > &values)
Write a list of values.
label nLocalPolyConn_
Local connectivity count for polys (faces) == sum of face sizes.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
List< label > labelList
A List of labels.
Definition: List.H:62
static rangeType subProcs(const label communicator=worldComm)
Range of process indices for sub-processes.
Definition: UPstream.H:1185
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:69
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
formatter & tag(const word &t, Args &&... args)
Write XML tag without any attributes. Combines openTag/closeTag.
void writePolysLegacy(const label pointOffset)
Write patch faces, legacy format.
const polyMesh & mesh_
Reference to the OpenFOAM mesh (or subset)
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
virtual bool beginFile(std::string title="")
Write file header (non-collective)