OpenLB 1.8.1
Loading...
Searching...
No Matches
vtuSurfaceWriter.hh
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2024 Christoph Gaul
4 * E-mail contact: info@openlb.net
5 * The most recent release of OpenLB can be downloaded at
6 * <http://www.openlb.net/>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#ifndef VTU_SURFACE_WRITER_HH
25#define VTU_SURFACE_WRITER_HH
26
27#include "vtuSurfaceWriter.h"
28
29#include <cmath>
30#include <unordered_set>
31#include <unordered_map>
32
33namespace olb {
34
35template <typename T>
36VTUsurfaceWriter<T>::VTUsurfaceWriter(std::string const name, CuboidDecomposition3D<T>& cuboidDecomposition,
37 LoadBalancer<T>& loadBalancer, bool binary)
38 : _name(name)
39 , _cuboidDecomposition(cuboidDecomposition)
40 , _loadBalancer(loadBalancer)
41 , _binary(binary)
42 , clout(std::cout, "VTUsurfaceWriter3D")
43{}
44
45template <typename T>
47{
48 _functorsA.push_back(&f);
49}
50
51template <typename T>
53{
54 _functors.push_back(&f);
55}
56
57template <typename T>
58void VTUsurfaceWriter<T>::addPoints(std::vector<Vector<T, 3>>& new_positions)
59{
60 for (unsigned long i = 0; i < new_positions.size(); i++) {
61 pos.push_back(new_positions[i]);
62 }
63}
64
65template <typename T>
67{
68 pos.push_back(new_position);
69}
70
71template <typename T>
72void VTUsurfaceWriter<T>::addConnectivity(std::vector<std::vector<int>>& connections)
73{
74 for (unsigned long i = 0; i < connections.size(); i++) {
75 connectivity.push_back(connections[i]);
76 }
77}
78
80template <typename T>
81void VTUsurfaceWriter<T>::write(std::size_t iT)
82{
83 int rank = 0;
84#ifdef PARALLEL_MODE_MPI
85 rank = singleton::mpi().getRank();
86#endif
87
88 for (SuperF3D<T, T>* f : _functors) {
89 f->getSuperStructure().communicate();
90 }
91 std::string fullNamePVDmaster = singleton::directories().getVtkOutDir() + createFileName(_name) + ".pvd";
92 std::string namePiece = "data/" + createFileName(_name, iT, 0) + ".vtu";
93
94 if (rank == 0) {
95 // puts name of .vti piece to a .pvd file [fullNamePVD]
96 // adds a namePiece to master.pvd file.
97 // To do so we overwrite closePVD() and add new entry.
98 dataPVDmaster(iT, 1, fullNamePVDmaster, namePiece);
99 }
100
101 //VTU data files
102
103 std::string fullNameVTU = singleton::directories().getVtkOutDir() + "data/" + createFileName(_name, iT, 0) + ".vtu";
104 if (rank == 0) {
105 preambleVTU(fullNameVTU, pos.size());
106 }
107 //writes data arrays into vtu file. All ranks are needed for this
108 this->dataArray(fullNameVTU);
109 if (rank == 0) {
110 closeVTU(fullNameVTU);
111 }
112}
113
114template <typename T>
115void VTUsurfaceWriter<T>::preambleVTU(const std::string& fullName, int num)
116{
117 std::ofstream fout(fullName.c_str(), std::ios::trunc);
118 if (!fout) {
119 clout << "Error: could not open " << fullName << std::endl;
120 }
121 fout << "<?xml version=\"1.0\"?>" << std::endl << std::flush;
122 fout << "<VTKFile type=\"UnstructuredGrid\" version=\"1.0\" byte_order=\"LittleEndian\">" << std::endl;
123 fout << "<UnstructuredGrid>" << std::endl;
124 fout << "<Piece NumberOfPoints=\"" << num << "\" NumberOfCells=\"" << connectivity.size() << "\">" << std::endl;
125 fout << "<PointData Vectors=\"Particles\">" << std::endl;
126 fout.close();
127}
128
129template <typename T>
130void VTUsurfaceWriter<T>::closeVTU(const std::string& fullNamePiece)
131{
132 std::ofstream fout(fullNamePiece.c_str(), std::ios::app);
133 if (!fout) {
134 clout << "Error: could not open " << fullNamePiece << std::endl;
135 }
136 fout << "</UnstructuredGrid>\n";
137 fout << "</VTKFile>\n";
138 fout.close();
139}
140
141template <typename T>
142void VTUsurfaceWriter<T>::dataPVD(int iT, int i, const std::string& fullNamePVD, const std::string& namePiece)
143{
144 std::ofstream fout(fullNamePVD.c_str(), std::ios::app);
145 if (!fout) {
146 clout << "Error: could not open " << fullNamePVD << std::endl;
147 }
148 fout << "<DataSet timestep=\"" << iT << "\" " << "group=\"\" part=\" " << i << "\" " << "file=\"" << namePiece
149 << "\"/>\n";
150 fout.close();
151}
152
153template <typename T>
154void VTUsurfaceWriter<T>::dataPVDmaster(int iT, int i, const std::string& fullNamePVDMaster,
155 const std::string& namePiece)
156{
157 std::ofstream fout(fullNamePVDMaster.c_str(), std::ios::in | std::ios::out | std::ios::ate);
158 if (fout) {
159 fout.seekp(-25, std::ios::end); // jump -25 from the end of file to overwrite closePVD
160 fout << "<DataSet timestep=\"" << iT << "\" " << "group=\"\" part=\" " << i << "\" " << "file=\"" << namePiece
161 << "\"/>\n";
162 fout.close();
163 closePVD(fullNamePVDMaster);
164 }
165 else {
166 clout << "Error: could not open " << fullNamePVDMaster << std::endl;
167 }
168}
169
170template <typename T>
172{
173 int rank = 0;
174#ifdef PARALLEL_MODE_MPI
175 rank = singleton::mpi().getRank();
176#endif
177 if (rank == 0) {
178 std::string fullNamePVDmaster = singleton::directories().getVtkOutDir() + createFileName(_name) + ".pvd";
179 preamblePVD(fullNamePVDmaster);
180 closePVD(fullNamePVDmaster);
181 }
182}
183
184template <typename T>
185void VTUsurfaceWriter<T>::preamblePVD(const std::string& fullNamePVD)
186{
187 std::ofstream fout(fullNamePVD.c_str(), std::ios::trunc);
188 if (!fout) {
189 clout << "Error: could not open " << fullNamePVD << std::endl;
190 }
191 fout << "<?xml version=\"1.0\"?>\n";
192 fout << "<VTKFile type=\"Collection\" version=\"1.0\" "
193 << "byte_order=\"LittleEndian\">\n"
194 << "<Collection>\n";
195 fout.close();
196}
197
198template <typename T>
199void VTUsurfaceWriter<T>::closePVD(const std::string& fullNamePVD)
200{
201 int rank = 0;
202#ifdef PARALLEL_MODE_MPI
203 rank = singleton::mpi().getRank();
204#endif
205 if (rank == 0) {
206
207 std::ofstream fout(fullNamePVD.c_str(), std::ios::app);
208 if (!fout) {
209 clout << "Error: could not open " << fullNamePVD << std::endl;
210 }
211 fout << "</Collection>\n";
212 fout << "</VTKFile>\n";
213 fout.close();
214 }
215}
216
217template <typename T>
218void VTUsurfaceWriter<T>::dataArray(const std::string& fullName)
219{
220
221 std::ofstream fout(fullName.c_str(), std::ios::app);
222 if (!fout) {
223 clout << "Error: could not open " << fullName << std::endl;
224 }
225
226 for (unsigned long i = 0; i < _functors.size(); i++) {
227 writeFunctor(fullName, fout, *_functors[i]);
228 }
229
230 for (unsigned long i = 0; i < _functorsA.size(); i++) {
231 writeAnalyticalFunctor(fullName, fout, *_functorsA[i]);
232 }
233 // master writes points and connectivity
234 int rank = 0;
235#ifdef PARALLEL_MODE_MPI
236 rank = singleton::mpi().getRank();
237#endif
238 if (rank == 0) {
239 fout << "</PointData>" << std::endl;
240 fout << "<CellData /> " << std::endl;
241 fout << "<Cells>" << std::endl;
242 fout << "<DataArray type=\"Int32\" Name=\"connectivity\" format=\"ascii\">" << std::endl;
243 // if there are no connections between the points, connectivity simply counts up
244 if (connectivity.size() == 0) {
245 for (int i = 0; i < 1; ++i) {
246 for (unsigned long j = 0; j < pos.size(); ++j) {
247 fout << j << " ";
248 }
249 }
250 }
251 // if there are connections, connectivity is written out
252 else {
253 for (int i = 0; i < 1; ++i) {
254 for (unsigned long j = 0; j < connectivity.size(); ++j) {
255 for (unsigned long k = 0; k < connectivity[j].size(); ++k) {
256 fout << connectivity[j][k] << " ";
257 }
258 if (j % 2 == 0) {
259 fout << std::endl;
260 }
261 }
262 }
263 }
264
265 fout << "</DataArray>" << std::endl;
266 fout << "<DataArray type=\"Int32\" Name=\"offsets\" format=\"ascii\">" << std::endl;
267 // if there are no connections, offsets just count up
268 if (connectivity.size() == 0) {
269 for (std::size_t i = 0; i < 1; ++i) {
270 for (unsigned long j = 1; j <= connectivity.size(); ++j) {
271 fout << j << " ";
272 if (j % 6 == 0) {
273 fout << std::endl;
274 }
275 }
276 }
277 // if connections are present, offset is multiplied by 3
278 }
279 else { //connectivity.size() != 0
280 for (std::size_t i = 0; i < 1; ++i) {
281 for (unsigned long j = 1; j <= connectivity.size(); ++j) {
282 fout << j * 3 << " ";
283 if (j % 6 == 0) {
284 fout << std::endl;
285 }
286 }
287 }
288 }
289 // Connected points have the type 5, unconnected points have the type 3
290 fout << "</DataArray>" << std::endl;
291 fout << "<DataArray type=\"UInt8\" Name=\"types\" format=\"ascii\">" << std::endl;
292 if (connectivity.size() != 0) {
293 for (unsigned long j = 0; j < connectivity.size(); ++j) {
294 fout << 5 << " ";
295 if (j % 6 == 0) {
296 fout << std::endl;
297 }
298 }
299 }
300 else {
301 for (unsigned long j = 0; j < pos.size(); ++j) {
302 fout << 3 << " ";
303 if (j % 6 == 0) {
304 fout << std::endl;
305 }
306 }
307 }
308 // write out all coordinates of points
309 fout << "</DataArray>" << std::endl;
310 fout << "</Cells>" << std::endl;
311 fout << "<Points>" << std::endl;
312 writePosition(fout);
313 fout << "</Points>" << std::endl;
314 fout << "</Piece>" << std::endl;
315
316 fout.close();
317 }
318}
319
320template <typename T>
321void VTUsurfaceWriter<T>::writePosition(std::ofstream& fout)
322{
323 fout << "<DataArray type=\"Float32\" Name=\"Point\" NumberOfComponents=\"" << 3 << "\">" << std::endl;
324
325 int num = pos.size();
326 for (int j = 0; j < num; ++j) {
327 fout << pos[j][0] << " " << pos[j][1] << " " << pos[j][2] << " " << std::endl;
328 }
329 fout << "</DataArray>" << std::endl;
330}
331
332template <typename T>
333void VTUsurfaceWriter<T>::writeFunctor(const std::string& fullName, std::ofstream& fout, SuperF3D<T, T>& f)
334{
335 AnalyticalFfromSuperF3D<T> interpolateF(f, false);
336 writeAnalyticalFunctor(fullName, fout, interpolateF);
337}
338
339template <typename T>
340void VTUsurfaceWriter<T>::writeAnalyticalFunctor(const std::string& fullName, std::ofstream& fout,
342{
343 std::size_t dimF = f.getTargetDim();
344
345 //Header DataArray, ascii or Base64 binary
346 int rank = 0;
347#ifdef PARALLEL_MODE_MPI
348 rank = singleton::mpi().getRank();
349#endif
350
351 if (rank == 0) {
352 if (!_binary) {
353 fout << "<DataArray type=\"Float32\" Name=\"" << f.getName() << "\" NumberOfComponents=\"" << dimF << "\">"
354 << std::endl;
355 }
356 else if (_binary) {
357 fout << "<DataArray type=\"Float32\" Name=\"" << f.getName()
358 << "\" format=\"binary\" encoding=\"base64\" NumberOfComponents=\"" << dimF << "\">" << std::endl;
359 }
360 }
361
362 if (_binary) {
363 fout.close();
364 }
365 std::size_t numPoints = pos.size();
366 std::ofstream ofstr(fullName.c_str(), std::ios::out | std::ios::app | std::ios::binary);
367 size_t binarySize = size_t(numPoints * dimF * sizeof(float));
368 Base64Encoder<unsigned int> sizeEncoder(ofstr, 1);
369 unsigned int uintBinarySize = (unsigned int)binarySize;
370 if (_binary) {
371 sizeEncoder.encode(&uintBinarySize, 1);
372 }
373 Base64Encoder<float> dataEncoder(ofstr, numPoints);
374
375 std::vector<T> vals(dimF * numPoints, 0);
376
377 T* val = new T[dimF];
378
379 for (std::size_t j = 0; j < numPoints; ++j) {
380 if (_loadBalancer.isLocal(_cuboidDecomposition.getC(pos[j]).value())) {
381 f(val, pos[j].data());
382
383 for (std::size_t i = 0; i < dimF; ++i) {
384 vals[i * numPoints + j] = val[i];
385 }
386 }
387 }
388#ifdef PARALLEL_MODE_MPI
389 // reduce the values to the master, then master writes them to the file
390 std::vector<T> valsCummulated(dimF * numPoints, 0);
391 singleton::mpi().reduceVect(vals, valsCummulated, MPI_SUM);
392
393 if (rank == 0) {
394 for (std::size_t j = 0; j < numPoints; ++j) {
395 for (std::size_t i = 0; i < dimF; ++i) {
396 if (!_binary) {
397 fout << valsCummulated[i * numPoints + j] << " ";
398 }
399 else {
400 const float helper = float(valsCummulated[j]);
401 dataEncoder.encode(&helper, 1);
402 }
403 }
404 }
405 }
406#endif
407#ifndef PARALLEL_MODE_MPI
408 // write the values to the file
409 for (std::size_t j = 0; j < numPoints; ++j) {
410 for (std::size_t i = 0; i < dimF; ++i) {
411 if (!_binary) {
412 fout << vals[i * numPoints + j] << " ";
413 }
414 else {
415 const float helper = float(vals[i * numPoints + j]);
416 dataEncoder.encode(&helper, 1);
417 }
418 }
419 }
420#endif
421
422 if (_binary) {
423 ofstr.close();
424 fout.open(fullName.c_str(), std::ios::out | std::ios::app);
425 }
426 if (rank == 0) {
427 fout << "</DataArray>" << std::endl;
428 }
429}
430
431template <typename T>
432std::vector<std::vector<T>> VTUsurfaceWriter<T>::removeDuplicates(const std::vector<std::vector<T>>& points)
433{
434 struct VectorHash {
435 std::size_t operator()(const std::vector<T>& v) const
436 {
437 return std::hash<T>()(v[0]) ^ std::hash<T>()(v[1]) ^ std::hash<T>()(v[2]);
438 }
439 };
440
441 struct VectorEqual {
442 bool operator()(const std::vector<T>& a, const std::vector<T>& b) const
443 {
444 return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
445 }
446 };
447 std::unordered_set<std::vector<T>, VectorHash, VectorEqual> uniquePoints(points.begin(), points.end());
448 return std::vector<std::vector<T>>(uniquePoints.begin(), uniquePoints.end());
449}
450
451template <typename T>
452bool VTUsurfaceWriter<T>::areVectorsEqual(const std::vector<T>& v1, const std::vector<T>& v2, T epsilon)
453{
454 for (std::size_t i = 0; i < v1.size(); ++i) {
455 if (std::fabs(v1[i] - v2[i]) > epsilon) {
456 return false;
457 }
458 }
459 return true;
460}
461
462template <typename T>
463std::vector<std::vector<int>> VTUsurfaceWriter<T>::mapTrianglesToIndices(STLreader<T>& stlReader)
464{
465 STLmesh<T> mesh = stlReader.getMesh();
466
467 struct VectorHash {
468 std::size_t operator()(const std::vector<T>& v) const
469 {
470 return std::hash<T>()(v[0]) ^ std::hash<T>()(v[1]) ^ std::hash<T>()(v[2]);
471 }
472 };
473
474 struct VectorEqual {
475 bool operator()(const std::vector<T>& a, const std::vector<T>& b) const { return areVectorsEqual(a, b); }
476 };
477
478 std::unordered_map<std::vector<T>, int, VectorHash, VectorEqual> pointIndexMap;
479 std::vector<std::vector<int>> indexedTriangles;
480
481 // Create a map from points to their indices
482 for (std::size_t i = 0; i < pos.size(); ++i) {
483 pointIndexMap[{pos[i][0], pos[i][1], pos[i][2]}] = i;
484 }
485
486 // Map each triangle's points to their indices
487 for (const STLtriangle<T>& triangle : mesh.getTriangles()) {
488 std::vector<int> indices(3);
489 for (int j = 0; j < 3; ++j) {
490 std::vector<T> pt = {triangle.point[j][0], triangle.point[j][1], triangle.point[j][2]};
491 auto it = pointIndexMap.find(pt);
492 if (it != pointIndexMap.end()) {
493 indices[j] = it->second;
494 }
495 else {
496 // Handle the case where the point is not found (should not happen if removeDuplicates worked correctly)
497 indices[j] = -1; // Invalid index
498 }
499 }
500 if (indices[0] != -1 && indices[1] != -1 && indices[2] != -1) {
501 indexedTriangles.push_back(indices);
502 }
503 }
504
505 return indexedTriangles;
506}
507
508template <typename T>
510{
511 STLmesh<T> mesh = stlReader.getMesh();
512 std::vector<std::vector<T>> points;
513
514 // Collect points from triangles
515 for (const STLtriangle<T>& triangle : mesh.getTriangles()) {
516 std::vector<T> pt = {triangle.point[0][0], triangle.point[0][1], triangle.point[0][2]};
517 points.push_back(pt);
518 pt = {triangle.point[1][0], triangle.point[1][1], triangle.point[1][2]};
519 points.push_back(pt);
520 pt = {triangle.point[2][0], triangle.point[2][1], triangle.point[2][2]};
521 points.push_back(pt);
522 }
523 // Remove duplicate points
524 points = removeDuplicates(points);
525
526 // Add points to pos
527 for (const std::vector<T>& point : points) {
528 pos.push_back(Vector<T, 3>(point[0], point[1], point[2]));
529 }
530
531 std::vector<std::vector<int>> indexedTriangles = mapTrianglesToIndices(stlReader);
532 for (const auto& tri : indexedTriangles) {
533 connectivity.push_back(tri);
534 }
535}
536
537} // end namespace olb
538#endif
AnalyticalF are applications from DD to XD, where X is set by the constructor.
Converts super functors to analytical functors.
void encode(const T *data, size_t length)
Definition base64.hh:59
int getTargetDim() const
read only access to member variable _n
Definition genericF.hh:45
std::string & getName()
read and write access to name
Definition genericF.hh:51
Base class for all LoadBalancer.
Definition vtiWriter.h:42
std::vector< STLtriangle< T > > & getTriangles()
Returns reference to all triangles.
Definition stlReader.h:166
STLmesh< T > & getMesh()
Returns mesh.
Definition stlReader.h:439
represents all functors that operate on a SuperStructure<T,3> in general
Definition aliases.h:184
void addPoints(std::vector< Vector< T, 3 > > &new_positions)
Add multiple locations to output set.
void addPoint(Vector< T, 3 > &new_position)
Add single location to output set.
void writeAnalyticalFunctor(const std::string &fullName, std::ofstream &fout, AnalyticalF3D< T, T > &f)
writes functors.
void addConnectivity(std::vector< std::vector< int > > &connections)
Define connectivity between previously-added output points, rendering them a surface.
void dataPVD(int iT, int i, const std::string &fullNamePVD, const std::string &namePiece)
performes <DataSet timestep= ... file=namePiece >
static bool areVectorsEqual(const std::vector< T > &v1, const std::vector< T > &v2, T epsilon=1e-6)
void closeVTU(const std::string &fullNamePiece)
performes </ImageData> and </VTKFile>
VTUsurfaceWriter(std::string name, CuboidDecomposition3D< T > &cuboidDecomposition, LoadBalancer< T > &loadBalancer, bool binary=false)
void addFunctor(AnalyticalF3D< T, T > &f)
Schedule analytical functor for output.
void write(std::size_t iT)
writes vtu files and assigns values
void closePVD(const std::string &fullNamePVD)
void writePosition(std::ofstream &fout)
writes coordinates of points
void dataPVDmaster(int iT, int i, const std::string &fullNamePVDMaster, const std::string &namePiece)
performes <DataSet timestep= ... file=namePiece >
std::vector< std::vector< T > > removeDuplicates(const std::vector< std::vector< T > > &points)
removes duplicates from points, needed for STL files
void preambleVTU(const std::string &fullName, int num)
performes <VTKFile ...>, <ImageData ...> and <PieceExtent ...>
void addSTL(STLreader< T > &stlReader)
Add full STL surface as output surface.
void writeFunctor(const std::string &fullName, std::ofstream &fout, SuperF3D< T, T > &f)
interpolates and writes functors stored at functors
void preamblePVD(const std::string &fullNamePVD)
std::vector< std::vector< int > > mapTrianglesToIndices(STLreader< T > &stlReader)
void dataArray(const std::string &fullName)
Plain old scalar vector.
std::string getVtkOutDir() const
Definition singleton.h:103
void reduceVect(std::vector< T > &sendVal, std::vector< T > &recvVal, MPI_Op op, int root=0, MPI_Comm comm=MPI_COMM_WORLD)
Element-per-element reduction of a vector of data.
int getRank() const
Returns the process ID.
MpiManager & mpi()
Directories & directories()
Definition singleton.h:162
Top level namespace for all of OpenLB.
std::string createFileName(std::string name)
for .pvd masterFile
Definition fileName.hh:34