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-2022 OpenCFD Ltd.
9-------------------------------------------------------------------------------
10License
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
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
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 {
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
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
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
217void 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:
390 patchMeshWriter(mesh, patchIDs)
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:
405 patchMeshWriter(mesh, patchIDs, opts)
406{
407 open(file, parallel);
408}
409
410
411// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
412
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
488
490{
491 return enter_CellData(numberOfCells_, nFields);
492}
493
494
496{
497 return enter_PointData(numberOfPoints_, nFields);
498}
499
500
502{
503 if (isState(outputState::CELL_DATA))
504 {
505 ++nCellData_;
506 }
507 else
508 {
509 reportBadState(FatalErrorInFunction, outputState::CELL_DATA)
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 {
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 return vtk::fileWriter::writeProcIDs(nLocalPolys_);
589}
590
591
593{
594 if (!Pstream::parRun())
595 {
596 // Skip in non-parallel
597 return false;
598 }
599
600 if (isState(outputState::CELL_DATA))
601 {
602 ++nCellData_;
603 }
604 else
605 {
606 reportBadState(FatalErrorInFunction, outputState::CELL_DATA)
607 << " for patchID field" << nl << endl
608 << exit(FatalError);
609 }
610
611 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
612
613 label nPolys = nLocalPolys_;
614
615 if (parallel_)
616 {
617 reduce(nPolys, sumOp<label>());
618 }
619
620
621 this->beginDataArray<label>("neighID", nPolys);
622
623 bool good = false;
624
625 if (parallel_ ? Pstream::master() : true)
626 {
627 for (const label patchId : patchIDs_)
628 {
629 const auto* pp = isA<processorPolyPatch>(patches[patchId]);
630
631 const label val = (pp ? pp->neighbProcNo() : -1);
632
633 vtk::write(format(), val, patches[patchId].size());
634 }
635
636 good = true;
637 }
638
639 if (parallel_)
640 {
641 if (Pstream::master())
642 {
643 labelList recv;
644
645 // Receive each pair
646 for (const int subproci : Pstream::subProcs())
647 {
648 IPstream fromProc(Pstream::commsTypes::blocking, subproci);
649
650 fromProc >> recv;
651
652 // Receive as [size, id] pairs
653 for (label i=0; i < recv.size(); i += 2)
654 {
655 const label len = recv[i];
656 const label val = recv[i+1];
657
658 vtk::write(format(), val, len);
659 }
660 }
661 }
662 else
663 {
664 // Send
665 OPstream toProc
666 (
669 );
670
671 // Encode as [size, id] pairs
672 labelList send(2*patchIDs_.size());
673 label i = 0;
674 for (const label patchId : patchIDs_)
675 {
676 const auto* pp = isA<processorPolyPatch>(patches[patchId]);
677
678 send[i] = patches[patchId].size();
679 send[i+1] = (pp ? pp->neighbProcNo() : -1);
680
681 i += 2;
682 }
683
684 toProc << send;
685 }
686 }
687
688 this->endDataArray();
689
690 // MPI barrier
691 return parallel_ ? returnReduce(good, orOp<bool>()) : good;
692}
693
694
695// ************************************************************************* //
Input inter-processor communications stream.
Definition: IPstream.H:57
Output inter-processor communications stream.
Definition: OPstream.H:57
label nPoints() const
Number of points supporting patch faces.
const Field< point_type > & localPoints() const
Return pointField of points in patch.
const List< face_type > & localFaces() const
Return patch faces addressing into local point list.
UPstream::rangeType subProcs() const noexcept
Range of sub-processes indices associated with PstreamBuffers.
iterator begin() noexcept
Return an iterator to begin traversing the UList.
Definition: UListI.H:329
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
static constexpr int masterNo() noexcept
Process index of the master (always 0)
Definition: UPstream.H:451
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:433
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
A class for handling file names.
Definition: fileName.H:76
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:68
label localStart() const
My local start.
Definition: globalIndexI.H:195
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:81
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:456
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:75
splitCell * master() const
Definition: splitCell.H:113
Base class for VTK output writers that handle geometry and fields (eg, vtp, vtu data)....
bool legacy() const noexcept
Commonly used query.
autoPtr< vtk::formatter > format_
The VTK formatter in use (only valid on master process)
bool parallel_
Parallel writing (via master)
bool parallel() const noexcept
Parallel output requested?
vtk::outputOptions opts_
Requested output options.
virtual bool open(const fileName &file, bool parallel=Pstream::parRun())
Open file for writing (creates parent directory).
virtual bool beginFile(std::string title="")
Write file header (non-collective)
vtk::formatter & format()
The VTK formatter in use. FatalError for off-processor.
formatter & beginPointData()
Begin "PointData" XML section.
formatter & tag(const word &t, Args &&... args)
Write XML tag without any attributes. Combines openTag/closeTag.
formatter & beginCellData()
Begin "CellData" XML section.
Encapsulated combinations of output format options. This is primarily useful when defining the output...
bool append() const noexcept
True if output format uses an append mode.
Write OpenFOAM patches and patch fields in VTP or legacy vtk format.
label nLocalPolys_
Local number of polys (faces)
bool writeNeighIDs()
Write processor neighbour ids as CellData. This is no-op in serial.
void writePolys(const label pointOffset)
Write patch faces.
label numberOfPoints_
The number of field points for the current Piece.
label nLocalPoints_
Local number of points.
label numberOfCells_
The number of field cells (faces) for the current Piece.
bool writeProcIDs()
Write processor ids as CellData. This is no-op in serial.
label nLocalPolyConn_
Local connectivity count for polys (faces) == sum of face sizes.
void writePoints()
Write patch points.
void writePatchIDs()
Write patch ids as CellData.
virtual bool writeGeometry()
Write patch topology.
virtual bool beginFile(std::string title="")
Write file header (non-collective)
const polyMesh & mesh_
Reference to the OpenFOAM mesh (or subset)
labelList patchIDs_
The selected patch ids.
void writePolysLegacy(const label pointOffset)
Write patch faces, legacy format.
const polyBoundaryMesh & patches
dynamicFvMesh & mesh
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
const label nPatches
label patchId(-1)
void beginPolys(std::ostream &os, label nPolys, label nConnectivity)
Emit header for POLYGONS (with trailing newline).
@ NUMBER_OF_POLYS
"NumberOfPolys"
@ NUMBER_OF_POINTS
"NumberOfPoints"
@ CONNECTIVITY
"connectivity"
fileTag
Some common XML tags for vtk files.
Definition: foamVtkCore.H:114
@ POLY_DATA
"PolyData"
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
void writeList(vtk::formatter &fmt, const UList< uint8_t > &values)
Write a list of uint8_t values.
void writeListParallel(vtk::formatter &fmt, const UList< Type > &values)
Write a list of values.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
error FatalError
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Reduce (copy) and return value.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
word format(conversionProperties.get< word >("format"))
labelList f(nPoints)