Chaste Commit::baa90ac2819b962188b7562f2326be23c47859a7
VertexMeshWriter.cpp
1/*
2
3Copyright (c) 2005-2024, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "VertexMeshWriter.hpp"
37#include "Version.hpp"
38#include "Cylindrical2dVertexMesh.hpp"
39#include "Toroidal2dVertexMesh.hpp"
40
44template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
46{
51};
52
54// Implementation
56
57template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
59 const std::string& rBaseName,
60 const bool clearOutputDir)
61 : AbstractMeshWriter<ELEMENT_DIM, SPACE_DIM>(rDirectory, rBaseName, clearOutputDir),
62 mpMesh(nullptr),
63 mpIters(new MeshWriterIterators<ELEMENT_DIM, SPACE_DIM>),
64 mpNodeMap(nullptr),
65 mNodeMapCurrentIndex(0)
66{
67 mpIters->pNodeIter = nullptr;
68 mpIters->pElemIter = nullptr;
69
70#ifdef CHASTE_VTK
71 // Dubious, since we shouldn't yet know what any details of the mesh are.
72 mpVtkUnstructedMesh = vtkUnstructuredGrid::New();
73#endif //CHASTE_VTK
74}
75
76template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
78{
79 if (mpIters->pNodeIter)
80 {
81 delete mpIters->pNodeIter;
82 delete mpIters->pElemIter;
83 }
84
85 delete mpIters;
86
87 if (mpNodeMap)
88 {
89 delete mpNodeMap;
90 }
91
92#ifdef CHASTE_VTK
93 // Dubious, since we shouldn't yet know what any details of the mesh are.
94 mpVtkUnstructedMesh->Delete(); // Reference counted
95#endif //CHASTE_VTK
96}
97
98template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
100{
101 if (mpMesh)
102 {
103 // Sanity check
104 assert(this->mNumNodes == mpMesh->GetNumNodes());
105
106 std::vector<double> coordinates(SPACE_DIM+1);
107
108 // Get the node coordinates using the node iterator (thus skipping deleted nodes)
109 for (unsigned j=0; j<SPACE_DIM; j++)
110 {
111 coordinates[j] = (*(mpIters->pNodeIter))->GetPoint()[j];
112 }
113 coordinates[SPACE_DIM] = (*(mpIters->pNodeIter))->IsBoundaryNode();
114
115 ++(*(mpIters->pNodeIter));
116
117 return coordinates;
118 }
119 else
120 {
122 }
123}
124
125template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
127{
128 // This method should only be called in 3D
129 assert(SPACE_DIM == 3); // LCOV_EXCL_LINE
130 assert(mpMesh);
131 assert(this->mNumElements == mpMesh->GetNumElements());
132
133 // Create data structure for this element
134 VertexElementData elem_data;
135
136 // Store node indices owned by this element
137 elem_data.NodeIndices.resize((*(mpIters->pElemIter))->GetNumNodes());
138 for (unsigned j=0; j<elem_data.NodeIndices.size(); j++)
139 {
140 unsigned old_index = (*(mpIters->pElemIter))->GetNodeGlobalIndex(j);
141 elem_data.NodeIndices[j] = mpMesh->IsMeshChanging() ? mpNodeMap->GetNewIndex(old_index) : old_index;
142 }
143
144 // Store faces owned by this element
145 elem_data.Faces.resize((*(mpIters->pElemIter))->GetNumFaces());
146 for (unsigned i=0; i<elem_data.Faces.size(); i++)
147 {
148 // Get pointer to this face
149 VertexElement<ELEMENT_DIM-1, SPACE_DIM>* p_face = (*(mpIters->pElemIter))->GetFace(i);
150
151 // Create data structure for this face
152 ElementData face_data;
153
154 // Store this face's index
155 face_data.AttributeValue = p_face->GetIndex();
156
157 // Store node indices owned by this face
158 face_data.NodeIndices.resize(p_face->GetNumNodes());
159 for (unsigned j=0; j<face_data.NodeIndices.size(); j++)
160 {
161 unsigned old_index = p_face->GetNodeGlobalIndex(j);
162 face_data.NodeIndices[j] = mpMesh->IsMeshChanging() ? mpNodeMap->GetNewIndex(old_index) : old_index;
163 }
164
165 // Store this face
166 elem_data.Faces[i] = face_data;
167
169 }
170
171 // Set attribute
172 elem_data.AttributeValue = (unsigned)(*(mpIters->pElemIter))->GetAttribute();
173 ++(*(mpIters->pElemIter));
174
175 return elem_data;
176}
177
178template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
180{
182
183 if (mpMesh)
184 {
185 assert(this->mNumElements == mpMesh->GetNumElements());
186
187 ElementData elem_data;
188 elem_data.NodeIndices.resize((*(mpIters->pElemIter))->GetNumNodes());
189 for (unsigned j=0; j<elem_data.NodeIndices.size(); j++)
190 {
191 unsigned old_index = (*(mpIters->pElemIter))->GetNodeGlobalIndex(j);
192 elem_data.NodeIndices[j] = mpMesh->IsMeshChanging() ? mpNodeMap->GetNewIndex(old_index) : old_index;
193 }
194
195 // Set attribute
196 elem_data.AttributeValue = (*(mpIters->pElemIter))->GetAttribute();
197 ++(*(mpIters->pElemIter));
198
199 return elem_data;
200 }
201 else
202 {
204 }
205}
206
207template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
209{
210#ifdef CHASTE_VTK
211 assert(SPACE_DIM==3 || SPACE_DIM == 2); // LCOV_EXCL_LINE
212
213 // Create VTK mesh
214 MakeVtkMesh(rMesh);
215
216 // Now write VTK mesh to file
217 assert(mpVtkUnstructedMesh->CheckAttributes() == 0);
218 vtkXMLUnstructuredGridWriter* p_writer = vtkXMLUnstructuredGridWriter::New();
219#if VTK_MAJOR_VERSION >= 6
220 p_writer->SetInputData(mpVtkUnstructedMesh);
221#else
222 p_writer->SetInput(mpVtkUnstructedMesh);
223#endif
224 // Uninitialised stuff arises (see #1079), but you can remove valgrind problems by removing compression:
225 // **** REMOVE WITH CAUTION *****
226 p_writer->SetCompressor(nullptr);
227 // **** REMOVE WITH CAUTION *****
228
229 std::string vtk_file_name = this->mpOutputFileHandler->GetOutputDirectoryFullPath() + this->mBaseName;
230 if (stamp != "")
231 {
232 vtk_file_name += "_" + stamp;
233 }
234 vtk_file_name += ".vtu";
235
236 p_writer->SetFileName(vtk_file_name.c_str());
237 //p_writer->PrintSelf(std::cout, vtkIndent());
238 p_writer->Write();
239 p_writer->Delete(); // Reference counted
240#endif //CHASTE_VTK
241}
242
249template<>
251{
252#ifdef CHASTE_VTK
253 // Create VTK mesh
254 VertexMesh<2, 2>* p_mesh_for_vtk = rMesh.GetMeshForVtk();
255 MakeVtkMesh(*p_mesh_for_vtk);
256
257 // Now write VTK mesh to file
258 assert(mpVtkUnstructedMesh->CheckAttributes() == 0);
259 vtkXMLUnstructuredGridWriter* p_writer = vtkXMLUnstructuredGridWriter::New();
260#if VTK_MAJOR_VERSION >= 6
261 p_writer->SetInputData(mpVtkUnstructedMesh);
262#else
263 p_writer->SetInput(mpVtkUnstructedMesh);
264#endif
265 // Uninitialised stuff arises (see #1079), but you can remove valgrind problems by removing compression:
266 // **** REMOVE WITH CAUTION *****
267 p_writer->SetCompressor(nullptr);
268 // **** REMOVE WITH CAUTION *****
269
270 std::string vtk_file_name = this->mpOutputFileHandler->GetOutputDirectoryFullPath() + this->mBaseName;
271 if (stamp != "")
272 {
273 vtk_file_name += "_" + stamp;
274 }
275 vtk_file_name += ".vtu";
276
277 p_writer->SetFileName(vtk_file_name.c_str());
278 //p_writer->PrintSelf(std::cout, vtkIndent());
279 p_writer->Write();
280 p_writer->Delete(); // Reference counted
281#endif //CHASTE_VTK
282}
283
284template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
286{
287#ifdef CHASTE_VTK
288 // Make the Vtk mesh
289 vtkPoints* p_pts = vtkPoints::New(VTK_DOUBLE);
290 p_pts->GetData()->SetName("Vertex positions");
291 for (unsigned node_num = 0; node_num < rMesh.GetNumNodes(); node_num++)
292 {
293 c_vector<double, SPACE_DIM> position;
294 position = rMesh.GetNode(node_num)->rGetLocation();
295 if constexpr (SPACE_DIM == 2)
296 {
297 p_pts->InsertPoint(node_num, position[0], position[1], 0.0);
298 }
299 else if constexpr (SPACE_DIM == 3)
300 {
301 p_pts->InsertPoint(node_num, position[0], position[1], position[2]);
302 }
303 else
304 {
306 }
307 }
308
309 mpVtkUnstructedMesh->SetPoints(p_pts);
310 p_pts->Delete(); // Reference counted
312 iter != rMesh.GetElementIteratorEnd();
313 ++iter)
314 {
315 vtkCell* p_cell;
316 if (SPACE_DIM == 2)
317 {
318 p_cell = vtkPolygon::New();
319 }
320 else
321 {
322 p_cell = vtkConvexPointSet::New();
323 }
324 vtkIdList* p_cell_id_list = p_cell->GetPointIds();
325 p_cell_id_list->SetNumberOfIds(iter->GetNumNodes());
326 for (unsigned j=0; j<iter->GetNumNodes(); ++j)
327 {
328 p_cell_id_list->SetId(j, iter->GetNodeGlobalIndex(j));
329 }
330 mpVtkUnstructedMesh->InsertNextCell(p_cell->GetCellType(), p_cell_id_list);
331 p_cell->Delete(); // Reference counted
332 }
333#endif //CHASTE_VTK
334}
335
336template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
337void VertexMeshWriter<ELEMENT_DIM, SPACE_DIM>::AddCellData(std::string dataName, std::vector<double> dataPayload)
338{
339#ifdef CHASTE_VTK
340 vtkDoubleArray* p_scalars = vtkDoubleArray::New();
341 p_scalars->SetName(dataName.c_str());
342 for (unsigned i=0; i<dataPayload.size(); i++)
343 {
344 p_scalars->InsertNextValue(dataPayload[i]);
345 }
346
347 vtkCellData* p_cell_data = mpVtkUnstructedMesh->GetCellData();
348 p_cell_data->AddArray(p_scalars);
349 p_scalars->Delete(); // Reference counted
350#endif //CHASTE_VTK
351}
352
353template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
354void VertexMeshWriter<ELEMENT_DIM, SPACE_DIM>::AddPointData(std::string dataName, std::vector<double> dataPayload)
355{
356#ifdef CHASTE_VTK
357 vtkDoubleArray* p_scalars = vtkDoubleArray::New();
358 p_scalars->SetName(dataName.c_str());
359 for (unsigned i=0; i<dataPayload.size(); i++)
360 {
361 p_scalars->InsertNextValue(dataPayload[i]);
362 }
363
364 vtkPointData* p_point_data = mpVtkUnstructedMesh->GetPointData();
365 p_point_data->AddArray(p_scalars);
366 p_scalars->Delete(); // Reference counted
367#endif //CHASTE_VTK
368}
369
371template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
373{
374 this->mpMeshReader = nullptr;
375 mpMesh = &rMesh;
376
377 this->mNumNodes = mpMesh->GetNumNodes();
378 this->mNumElements = mpMesh->GetNumElements();
379
380 typedef typename AbstractMesh<ELEMENT_DIM,SPACE_DIM>::NodeIterator NodeIterType;
381 mpIters->pNodeIter = new NodeIterType(mpMesh->GetNodeIteratorBegin());
382
384 mpIters->pElemIter = new ElemIterType(mpMesh->GetElementIteratorBegin());
385
386 // Set up node map if we might have deleted nodes
387 mNodeMapCurrentIndex = 0;
388 if (mpMesh->IsMeshChanging())
389 {
390 mpNodeMap = new NodeMap(mpMesh->GetNumAllNodes());
391 for (NodeIterType it = mpMesh->GetNodeIteratorBegin(); it != mpMesh->GetNodeIteratorEnd(); ++it)
392 {
393 mpNodeMap->SetNewIndex(it->GetIndex(), mNodeMapCurrentIndex++);
394 }
395 }
396 WriteFiles();
397}
398
399template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
401{
402 std::string comment = "# " + ChasteBuildInfo::GetProvenanceString();
403
404 // Write node file
405 std::string node_file_name = this->mBaseName + ".node";
406 out_stream p_node_file = this->mpOutputFileHandler->OpenOutputFile(node_file_name);
407
408 // Write the node header
409 unsigned num_attr = 0;
410 unsigned max_bdy_marker = 1; // as we include boundary node information in the node file
411 unsigned num_nodes = this->GetNumNodes();
412
413 *p_node_file << num_nodes << "\t";
414 *p_node_file << SPACE_DIM << "\t";
415 *p_node_file << num_attr << "\t";
416 *p_node_file << max_bdy_marker << "\n";
417 *p_node_file << std::setprecision(6);
418
419 // Write each node's data
420 for (unsigned item_num=0; item_num<num_nodes; item_num++)
421 {
422 std::vector<double> current_item = this->GetNextNode();
423 *p_node_file << item_num;
424 for (unsigned i=0; i<SPACE_DIM+1; i++)
425 {
426 *p_node_file << "\t" << current_item[i];
427 }
428 *p_node_file << "\n";
429 }
430 *p_node_file << comment << "\n";
431 p_node_file->close();
432
433 // Write element file
434 std::string element_file_name = this->mBaseName + ".cell";
435 out_stream p_element_file = this->mpOutputFileHandler->OpenOutputFile(element_file_name);
436
437 // Write the element header
438 num_attr = 1; //Always write element attributes
439 unsigned num_elements = this->GetNumElements();
440 *p_element_file << num_elements << "\t" << num_attr << "\n";
441
442 // Write each element's data
443 for (unsigned item_num=0; item_num<num_elements; item_num++)
444 {
445 if (SPACE_DIM == 2) // In 2D, write the node indices owned by this element
446 {
447 // Get data for this element
448 ElementData elem_data = this->GetNextElement();
449
450 // Get the node indices owned by this element
451 std::vector<unsigned> node_indices = elem_data.NodeIndices;
452
453 // Write this element's index and the number of nodes owned by it to file
454 *p_element_file << item_num << "\t" << node_indices.size();
455
456 // Write the node indices owned by this element to file
457 for (unsigned i=0; i<node_indices.size(); i++)
458 {
459 *p_element_file << "\t" << node_indices[i];
460 }
461
462 *p_element_file << "\t" << elem_data.AttributeValue;
463
464 // New line
465 *p_element_file << "\n";
466 }
467 else // 3D
468 {
469 assert(SPACE_DIM == 3); // LCOV_EXCL_LINE
470
471 // Get data for this element
472 VertexElementData elem_data_with_faces = this->GetNextElementWithFaces();
473
474 // Get the node indices owned by this element
475 std::vector<unsigned> node_indices = elem_data_with_faces.NodeIndices;
476
477 // Write this element's index and the number of nodes owned by it to file
478 *p_element_file << item_num << "\t" << node_indices.size();
479
480 // Write the node indices owned by this element to file
481 for (unsigned i=0; i<node_indices.size(); i++)
482 {
483 *p_element_file << "\t" << node_indices[i];
484 }
485
486 // Get the faces owned by this element (if any)
487 std::vector<ElementData> faces = elem_data_with_faces.Faces;
488
489 // Write the number of faces owned by this element to file
490 *p_element_file << "\t" << faces.size();
491
492 for (unsigned j=0; j<faces.size(); j++)
493 {
494 // Get the node indices owned by this face
495 std::vector<unsigned> face_node_indices = faces[j].NodeIndices;
496
497 // Write this face's index and the number of nodes owned by it to file
498 *p_element_file << "\t" << faces[j].AttributeValue << "\t" << face_node_indices.size();
499
500 // Write the node indices owned by this element to file
501 for (unsigned i=0; i<face_node_indices.size(); i++)
502 {
503 *p_element_file << "\t" << face_node_indices[i];
504 }
505
507 }
508
509 *p_element_file << "\t" << elem_data_with_faces.AttributeValue;
510
511 // New line
512 *p_element_file << "\n";
513 }
514 }
515
516 *p_element_file << comment << "\n";
517 p_element_file->close();
518}
519
521
522template class VertexMeshWriter<1,1>;
523template class VertexMeshWriter<1,2>;
524template class VertexMeshWriter<1,3>;
525template class VertexMeshWriter<2,2>;
526template class VertexMeshWriter<2,3>;
527template class VertexMeshWriter<3,3>;
#define NEVER_REACHED
virtual ElementData GetNextElement()
virtual std::vector< double > GetNextNode()
Node< SPACE_DIM > * GetNode(unsigned index) const
static std::string GetProvenanceString()
VertexElement< ELEMENT_DIM-1, SPACE_DIM > * GetFace(unsigned index) const
vtkUnstructuredGrid * mpVtkUnstructedMesh
ElementData GetNextElement()
std::vector< double > GetNextNode()
void AddPointData(std::string dataName, std::vector< double > dataPayload)
void MakeVtkMesh(VertexMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
void WriteVtkUsingMesh(VertexMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, std::string stamp="")
MeshWriterIterators< ELEMENT_DIM, SPACE_DIM > * mpIters
void AddCellData(std::string dataName, std::vector< double > dataPayload)
VertexElementData GetNextElementWithFaces()
void WriteFilesUsingMesh(VertexMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
VertexMeshWriter(const std::string &rDirectory, const std::string &rBaseName, const bool clearOutputDir=true)
VertexElementIterator GetElementIteratorEnd()
virtual unsigned GetNumNodes() const
VertexElementIterator GetElementIteratorBegin(bool skipDeletedElements=true)
virtual VertexMesh< ELEMENT_DIM, SPACE_DIM > * GetMeshForVtk()
std::vector< unsigned > NodeIndices
VertexMesh< ELEMENT_DIM, SPACE_DIM >::VertexElementIterator * pElemIter
AbstractMesh< ELEMENT_DIM, SPACE_DIM >::NodeIterator * pNodeIter
std::vector< ElementData > Faces
std::vector< unsigned > NodeIndices