Chaste Commit::29c3c4ab2bee00ddd311a035a1e522c03cbe928a
VtkMeshWriter.cpp
1/*
2
3Copyright (c) 2005-2026, 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 <boost/scoped_array.hpp>
37#include "VtkMeshWriter.hpp"
38#include "DistributedTetrahedralMesh.hpp"
39#include "MixedDimensionMesh.hpp"
40#include "NodesOnlyMesh.hpp"
41
42#include "vtkQuadraticTetra.h"
43#include "vtkQuadraticTriangle.h"
44
45
47// Implementation
49template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
51 const std::string& rBaseName,
52 const bool& rCleanDirectory)
53 : AbstractTetrahedralMeshWriter<ELEMENT_DIM, SPACE_DIM>(rDirectory, rBaseName, rCleanDirectory),
54 mWriteParallelFiles(false),
55 mWriteMeshCells(true)
56{
57 this->mIndexFromZero = true;
58
59 // Dubious, since we shouldn't yet know what any details of the mesh are.
60 mpVtkUnstructedMesh = vtkUnstructuredGrid::New();
61
62
63}
64
65template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
67{
68 mpVtkUnstructedMesh->Delete(); // Reference counted
69}
70
71template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
73{
74 //Construct nodes aka as Points
75 vtkPoints* p_pts = vtkPoints::New(VTK_DOUBLE);
76 p_pts->GetData()->SetName("Vertex positions");
77 for (unsigned item_num=0; item_num<this->GetNumNodes(); item_num++)
78 {
79 std::vector<double> current_item = this->GetNextNode(); //this->mNodeData[item_num];
80 // Add zeroes if the dimension is below 3
81 for (unsigned dim=SPACE_DIM; dim<3; dim++)
82 {
83 current_item.push_back(0.0);//For y and z-coordinates if necessary
84 }
85 assert(current_item.size() == 3);
86 p_pts->InsertPoint(item_num, current_item[0], current_item[1], current_item[2]);
87 }
88 mpVtkUnstructedMesh->SetPoints(p_pts);
89 p_pts->Delete(); //Reference counted
90
91 if (mWriteMeshCells == true)
92 {
93 //Construct elements aka Cells
94 for (unsigned item_num=0; item_num<this->GetNumElements(); item_num++)
95 {
96 std::vector<unsigned> current_element = this->GetNextElement().NodeIndices; // this->mElementData[item_num];
97
98 assert((current_element.size() == ELEMENT_DIM + 1) || (current_element.size() == (ELEMENT_DIM+1)*(ELEMENT_DIM+2)/2));
99
100 vtkCell* p_cell=nullptr;
101 if (ELEMENT_DIM == 3 && current_element.size() == 4)
102 {
103 p_cell = vtkTetra::New();
104 }
105 else if (ELEMENT_DIM == 3 && current_element.size() == 10)
106 {
107 p_cell = vtkQuadraticTetra::New();
108 }
109 else if (ELEMENT_DIM == 2 && current_element.size() == 3)
111 p_cell = vtkTriangle::New();
112 }
113 else if (ELEMENT_DIM == 2 && current_element.size() == 6)
114 {
115 p_cell = vtkQuadraticTriangle::New();
116 }
117 else if (ELEMENT_DIM == 1)
118 {
119 p_cell = vtkLine::New();
120 }
122 //Set the linear nodes
123 vtkIdList* p_cell_id_list = p_cell->GetPointIds();
124 for (unsigned j = 0; j < current_element.size(); ++j)
125 {
126 p_cell_id_list->SetId(j, current_element[j]);
127 }
128
129 //VTK defines the node ordering in quadratic triangles differently to Chaste, so they must be treated as a special case
130 if (SPACE_DIM == 2 && current_element.size() == 6)
131 {
132 p_cell_id_list->SetId(3, current_element[5]);
133 p_cell_id_list->SetId(4, current_element[3]);
134 p_cell_id_list->SetId(5, current_element[4]);
135 }
137 mpVtkUnstructedMesh->InsertNextCell(p_cell->GetCellType(), p_cell_id_list);
138 p_cell->Delete(); //Reference counted
139 }
140 }
141
142 if (SPACE_DIM > 1)
143 {
145 for (unsigned item_num=0; item_num<this->GetNumBoundaryFaces(); item_num++)
146 {
147 this->GetNextBoundaryElement();
148 }
149 }
150
151 //If necessary, construct cables
152 if (this->GetNumCableElements() > 0)
153 {
154 AugmentCellData();
155 //Make a blank cell radius data for the regular elements
156 std::vector<double> radii(this->GetNumElements(), 0.0);
157 for (unsigned item_num=0; item_num<this->GetNumCableElements(); item_num++)
158 {
159 ElementData cable_element_data = this->GetNextCableElement();
160 std::vector<unsigned> current_element = cable_element_data.NodeIndices;
161 radii.push_back(cable_element_data.AttributeValue);
162 assert(current_element.size() == 2);
163 vtkCell* p_cell=vtkLine::New();
164 vtkIdList* p_cell_id_list = p_cell->GetPointIds();
165 for (unsigned j = 0; j < 2; ++j)
166 {
167 p_cell_id_list->SetId(j, current_element[j]);
168 }
169 mpVtkUnstructedMesh->InsertNextCell(p_cell->GetCellType(), p_cell_id_list);
170 p_cell->Delete(); //Reference counted
171 }
172 AddCellData("Cable radius", radii);
173
175
176}
177
178template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
180{
181 mWriteMeshCells = writeMeshCells;
182}
184template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
186{
187 std::string comment = "<!-- " + ChasteBuildInfo::GetProvenanceString() + "-->";
188
189 out_stream p_vtu_file = this->mpOutputFileHandler->OpenOutputFile(fileName, std::ios::out | std::ios::app);
190
191 *p_vtu_file << "\n" << comment << "\n";
192 p_vtu_file->close();
193}
194
195template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
197{
198 // Using separate scope here to make sure file is properly closed before re-opening it to add provenance info.
199 {
200 MakeVtkMesh();
201 assert(mpVtkUnstructedMesh->CheckAttributes() == 0);
202 vtkXMLUnstructuredGridWriter* p_writer = vtkXMLUnstructuredGridWriter::New();
203 p_writer->SetInputData(mpVtkUnstructedMesh);
204 std::string vtk_file_name = this->mpOutputFileHandler->GetOutputDirectoryFullPath() + this->mBaseName+".vtu";
205 p_writer->SetFileName(vtk_file_name.c_str());
206 //p_writer->PrintSelf(std::cout, vtkIndent());
207 p_writer->Write();
208 p_writer->Delete(); //Reference counted
209 }
210
211 AddProvenance(this->mBaseName + ".vtu");
212}
213
214template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
215void VtkMeshWriter<ELEMENT_DIM,SPACE_DIM>::AddCellData(std::string dataName, std::vector<double> dataPayload)
216{
217 vtkDoubleArray* p_scalars = vtkDoubleArray::New();
218 p_scalars->SetName(dataName.c_str());
219 for (unsigned i=0; i<dataPayload.size(); i++)
220 {
221 p_scalars->InsertNextValue(dataPayload[i]);
222 }
223
224 vtkCellData* p_cell_data = mpVtkUnstructedMesh->GetCellData();
225 p_cell_data->AddArray(p_scalars);
226 p_scalars->Delete(); //Reference counted
228
229template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
231{
232 unsigned num_cell_arrays = mpVtkUnstructedMesh->GetCellData()->GetNumberOfArrays();
233 for (unsigned i = 0; i < num_cell_arrays; i++)
234 {
235 vtkDataArray* array = mpVtkUnstructedMesh->GetCellData()->GetArray(i);
236
237 //Check data was the correct size before the cables were added
238 unsigned num_cable_pads = this->GetNumCableElements();
239 if (mWriteParallelFiles)
241 assert((unsigned)array->GetNumberOfTuples() == this->mpDistributedMesh->GetNumLocalElements());
242 num_cable_pads = this->mpMixedMesh->GetNumLocalCableElements();
243 }
244 else
245 {
246 assert((unsigned)array->GetNumberOfTuples() == this->GetNumElements());
247 }
248
249 //Check that tuples of size 3 will be big enough for padding the rest of the data
250 assert(array->GetNumberOfComponents() <= 3);
251 double null_data[3] = {0.0, 0.0, 0.0};
252
253 //Pad data
254 for (unsigned new_index = 0; new_index < num_cable_pads; new_index++)
255 {
256 array->InsertNextTuple(null_data);
257 }
258 }
259}
260
261
262template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
263void VtkMeshWriter<ELEMENT_DIM,SPACE_DIM>::AddCellData(std::string dataName, std::vector<c_vector<double, SPACE_DIM> > dataPayload)
264{
265 vtkDoubleArray* p_vectors = vtkDoubleArray::New();
266 p_vectors->SetName(dataName.c_str());
267 p_vectors->SetNumberOfComponents(3);
268 for (unsigned i=0; i<dataPayload.size(); i++)
269 {
270 for (unsigned j=0; j<SPACE_DIM; j++)
271 {
272 p_vectors->InsertNextValue(dataPayload[i][j]);
273 }
274 //When SPACE_DIM<3, then pad
275 for (unsigned j=SPACE_DIM; j<3; j++)
276 {
277 p_vectors->InsertNextValue(0.0);
278 }
279 }
280
281 vtkCellData* p_cell_data = mpVtkUnstructedMesh->GetCellData();
282 p_cell_data->AddArray(p_vectors);
283 p_vectors->Delete(); //Reference counted
284}
285
286template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
287void VtkMeshWriter<ELEMENT_DIM,SPACE_DIM>::AddTensorCellData(std::string dataName, std::vector<c_vector<double,SPACE_DIM*(SPACE_DIM+1)/2> > dataPayload)
288{
289 assert(SPACE_DIM != 1); // LCOV_EXCL_LINE
290
291 vtkDoubleArray* p_vectors = vtkDoubleArray::New();
292 p_vectors->SetName(dataName.c_str());
293 p_vectors->SetNumberOfComponents(SPACE_DIM*SPACE_DIM);
294 for (unsigned i=0; i<dataPayload.size(); i++)
295 {
296 if (SPACE_DIM == 2)
297 {
298 p_vectors->InsertNextValue(dataPayload[i](0)); //a11
299 p_vectors->InsertNextValue(dataPayload[i](1)); //a12
300 p_vectors->InsertNextValue(dataPayload[i](1)); //a21
301 p_vectors->InsertNextValue(dataPayload[i](2)); //a22
302 }
303 else if (SPACE_DIM == 3)
304 {
305 p_vectors->InsertNextValue(dataPayload[i](0)); //a11
306 p_vectors->InsertNextValue(dataPayload[i](1)); //a12
307 p_vectors->InsertNextValue(dataPayload[i](2)); //a13
308 p_vectors->InsertNextValue(dataPayload[i](1)); //a21
309 p_vectors->InsertNextValue(dataPayload[i](3)); //a22
310 p_vectors->InsertNextValue(dataPayload[i](4)); //a23
311 p_vectors->InsertNextValue(dataPayload[i](2)); //a31
312 p_vectors->InsertNextValue(dataPayload[i](4)); //a32
313 p_vectors->InsertNextValue(dataPayload[i](5)); //a33
314 }
315 }
316
317 vtkCellData* p_cell_data = mpVtkUnstructedMesh->GetCellData();
318 p_cell_data->AddArray(p_vectors);
319 p_vectors->Delete(); //Reference counted
320}
321
322template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
323void VtkMeshWriter<ELEMENT_DIM,SPACE_DIM>::AddTensorCellData(std::string dataName, std::vector<c_matrix<double,SPACE_DIM,SPACE_DIM> > dataPayload)
324{
325 assert(SPACE_DIM != 1); // LCOV_EXCL_LINE
326
327 vtkDoubleArray* p_vectors = vtkDoubleArray::New();
328 p_vectors->SetName(dataName.c_str());
329 p_vectors->SetNumberOfComponents(SPACE_DIM*SPACE_DIM);
330 for (unsigned i=0; i<dataPayload.size(); i++)
331 {
332 if (SPACE_DIM == 2)
333 {
334 p_vectors->InsertNextValue(dataPayload[i](0,0)); //a11
335 p_vectors->InsertNextValue(dataPayload[i](0,1)); //a12
336 p_vectors->InsertNextValue(dataPayload[i](1,0)); //a21
337 p_vectors->InsertNextValue(dataPayload[i](1,1)); //a22
338 }
339 else if (SPACE_DIM == 3)
340 {
341 p_vectors->InsertNextValue(dataPayload[i](0,0)); //a11
342 p_vectors->InsertNextValue(dataPayload[i](0,1)); //a12
343 p_vectors->InsertNextValue(dataPayload[i](0,2)); //a13
344 p_vectors->InsertNextValue(dataPayload[i](1,0)); //a21
345 p_vectors->InsertNextValue(dataPayload[i](1,1)); //a22
346 p_vectors->InsertNextValue(dataPayload[i](1,2)); //a23
347 p_vectors->InsertNextValue(dataPayload[i](2,0)); //a31
348 p_vectors->InsertNextValue(dataPayload[i](2,1)); //a32
349 p_vectors->InsertNextValue(dataPayload[i](2,2)); //a33
350 }
351 }
352
353 vtkCellData* p_cell_data = mpVtkUnstructedMesh->GetCellData();
354 p_cell_data->AddArray(p_vectors);
355 p_vectors->Delete(); //Reference counted
356}
357
358
359template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
360void VtkMeshWriter<ELEMENT_DIM,SPACE_DIM>::AddPointData(std::string dataName, std::vector<double> dataPayload)
361{
362 vtkDoubleArray* p_scalars = vtkDoubleArray::New();
363 p_scalars->SetName(dataName.c_str());
364
365 if (mWriteParallelFiles && this->mpDistributedMesh != nullptr)
366 {
367 // In parallel, the vector we pass will only contain the values from the privately owned nodes.
368 // To get the values from the halo nodes (which will be inserted at the end of the vector we need to
369 // communicate with the equivalent vectors on other processes.
370
371 // resize the payload data to include halos
372 assert( dataPayload.size() == this->mpDistributedMesh->GetNumLocalNodes() );
373 dataPayload.resize( this->mpDistributedMesh->GetNumLocalNodes() + this->mpDistributedMesh->GetNumHaloNodes() );
374
375
376 // then do the communication
377 for ( unsigned rank_offset = 1; rank_offset < PetscTools::GetNumProcs(); rank_offset++ )
378 {
379 unsigned send_to = (PetscTools::GetMyRank() + rank_offset) % (PetscTools::GetNumProcs());
380 unsigned receive_from = (PetscTools::GetMyRank() + PetscTools::GetNumProcs()- rank_offset ) % (PetscTools::GetNumProcs());
381
382 unsigned number_of_nodes_to_send = mNodesToSendPerProcess[send_to].size();
383 unsigned number_of_nodes_to_receive = mNodesToReceivePerProcess[receive_from].size();
384
385 boost::scoped_array<double> send_data(new double[number_of_nodes_to_send]);
386 boost::scoped_array<double> receive_data(new double[number_of_nodes_to_receive]);
387 // Pack
388 for (unsigned node = 0; node < number_of_nodes_to_send; node++)
389 {
390 unsigned global_node_index = mNodesToSendPerProcess[send_to][node];
391 unsigned local_node_index = global_node_index
392 - this->mpDistributedMesh->GetDistributedVectorFactory()->GetLow();
393 send_data[node] = dataPayload[local_node_index];
394 }
395 {
396 // Send
397 int ret;
398 MPI_Status status;
399 ret = MPI_Sendrecv(send_data.get(), number_of_nodes_to_send,
400 MPI_DOUBLE,
401 send_to, 0,
402 receive_data.get(), number_of_nodes_to_receive,
403 MPI_DOUBLE,
404 receive_from, 0,
405 PETSC_COMM_WORLD, &status);
406 UNUSED_OPT(ret);
407 assert ( ret == MPI_SUCCESS );
408 }
409
410 // Unpack
411 for ( unsigned node = 0; node < number_of_nodes_to_receive; node++ )
412 {
413 unsigned global_node_index = mNodesToReceivePerProcess[receive_from][node];
414 unsigned halo_index = mGlobalToNodeIndexMap[global_node_index];
415 assert( halo_index >= this->mpDistributedMesh->GetNumLocalNodes() );
416 dataPayload[halo_index] = receive_data[node];
417 }
418
419 }
420 }
421
422 for (unsigned i=0; i<dataPayload.size(); i++)
423 {
424 p_scalars->InsertNextValue(dataPayload[i]);
425 }
426
427 vtkPointData* p_point_data = mpVtkUnstructedMesh->GetPointData();
428 p_point_data->AddArray(p_scalars);
429 p_scalars->Delete(); //Reference counted
430}
431
432
433template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
434void VtkMeshWriter<ELEMENT_DIM,SPACE_DIM>::AddPointData(std::string dataName, std::vector<c_vector<double, SPACE_DIM> > dataPayload)
435{
436 vtkDoubleArray* p_vectors = vtkDoubleArray::New();
437 p_vectors->SetName(dataName.c_str());
438
439 if (mWriteParallelFiles)
440 {
441 // In parallel, the vector we pass will only contain the values from the privately owned nodes.
442 // To get the values from the halo nodes (which will be inserted at the end of the vector we need to
443 // communicate with the equivalent vectors on other processes.
444
445 // resize the payload data to include halos
446 assert( dataPayload.size() == this->mpDistributedMesh->GetNumLocalNodes() );
447 dataPayload.resize( this->mpDistributedMesh->GetNumLocalNodes() + this->mpDistributedMesh->GetNumHaloNodes() );
448
449 // then do the communication
450 for ( unsigned rank_offset = 1; rank_offset < PetscTools::GetNumProcs(); rank_offset++ )
451 {
452 unsigned send_to = (PetscTools::GetMyRank() + rank_offset) % (PetscTools::GetNumProcs());
453 unsigned receive_from = (PetscTools::GetMyRank() + PetscTools::GetNumProcs()- rank_offset ) % (PetscTools::GetNumProcs());
454
455 unsigned number_of_nodes_to_send = mNodesToSendPerProcess[send_to].size();
456 unsigned number_of_nodes_to_receive = mNodesToReceivePerProcess[receive_from].size();
457
458 boost::scoped_array<double> send_data(new double[number_of_nodes_to_send * SPACE_DIM]);
459 boost::scoped_array<double> receive_data(new double[number_of_nodes_to_receive * SPACE_DIM]);
460
461 for (unsigned node = 0; node < number_of_nodes_to_send; node++)
462 {
463 unsigned global_node_index = mNodesToSendPerProcess[send_to][node];
464 unsigned local_node_index = global_node_index
465 - this->mpDistributedMesh->GetDistributedVectorFactory()->GetLow();
466 for (unsigned j=0; j<SPACE_DIM; j++)
467 {
468 send_data[ node*SPACE_DIM + j ] = dataPayload[local_node_index][j];
469 }
470 }
471
472 int ret;
473 MPI_Status status;
474 ret = MPI_Sendrecv(send_data.get(), number_of_nodes_to_send * SPACE_DIM,
475 MPI_DOUBLE,
476 send_to, 0,
477 receive_data.get(), number_of_nodes_to_receive * SPACE_DIM,
478 MPI_DOUBLE,
479 receive_from, 0,
480 PETSC_COMM_WORLD, &status);
481 UNUSED_OPT(ret);
482 assert ( ret == MPI_SUCCESS );
483
484 // Unpack
485 for ( unsigned node = 0; node < number_of_nodes_to_receive; node++ )
486 {
487 unsigned global_node_index = mNodesToReceivePerProcess[receive_from][node];
488 unsigned halo_index = mGlobalToNodeIndexMap[global_node_index];
489 assert( halo_index >= this->mpDistributedMesh->GetNumLocalNodes() );
490 for (unsigned j=0; j<SPACE_DIM; j++)
491 {
492 dataPayload[halo_index][j] = receive_data[ node*SPACE_DIM + j ];
493 }
494 }
495 }
496 }
497
498 p_vectors->SetNumberOfComponents(3);
499 for (unsigned i=0; i<dataPayload.size(); i++)
500 {
501 for (unsigned j=0; j<SPACE_DIM; j++)
502 {
503 p_vectors->InsertNextValue(dataPayload[i][j]);
504 }
505 //When SPACE_DIM<3, then pad
506 for (unsigned j=SPACE_DIM; j<3; j++)
507 {
508 p_vectors->InsertNextValue(0.0);
509 }
510 }
511
512 vtkPointData* p_point_data = mpVtkUnstructedMesh->GetPointData();
513 p_point_data->AddArray(p_vectors);
514 p_vectors->Delete(); //Reference counted
515}
516
517template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
518void VtkMeshWriter<ELEMENT_DIM,SPACE_DIM>::AddTensorPointData(std::string dataName, std::vector<c_matrix<double,SPACE_DIM,SPACE_DIM> > dataPayload)
519{
520 assert(SPACE_DIM != 1); // LCOV_EXCL_LINE
521
522 vtkDoubleArray* p_vectors = vtkDoubleArray::New();
523 p_vectors->SetName(dataName.c_str());
524 p_vectors->SetNumberOfComponents(SPACE_DIM*SPACE_DIM);
525 for (unsigned i=0; i<dataPayload.size(); i++)
526 {
527 if (SPACE_DIM == 2)
528 {
529 p_vectors->InsertNextValue(dataPayload[i](0,0)); //a11
530 p_vectors->InsertNextValue(dataPayload[i](0,1)); //a12
531 p_vectors->InsertNextValue(dataPayload[i](1,0)); //a21
532 p_vectors->InsertNextValue(dataPayload[i](1,1)); //a22
533 }
534 else if (SPACE_DIM == 3)
535 {
536 p_vectors->InsertNextValue(dataPayload[i](0,0)); //a11
537 p_vectors->InsertNextValue(dataPayload[i](0,1)); //a12
538 p_vectors->InsertNextValue(dataPayload[i](0,2)); //a13
539 p_vectors->InsertNextValue(dataPayload[i](1,0)); //a21
540 p_vectors->InsertNextValue(dataPayload[i](1,1)); //a22
541 p_vectors->InsertNextValue(dataPayload[i](1,2)); //a23
542 p_vectors->InsertNextValue(dataPayload[i](2,0)); //a31
543 p_vectors->InsertNextValue(dataPayload[i](2,1)); //a32
544 p_vectors->InsertNextValue(dataPayload[i](2,2)); //a33
545 }
546 }
547
548 vtkPointData* p_point_data = mpVtkUnstructedMesh->GetPointData();
549 p_point_data->AddArray(p_vectors);
550 p_vectors->Delete(); //Reference counted
551}
552
553template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
555{
556 //Have we got a distributed mesh?
557 this->mpDistributedMesh = dynamic_cast<DistributedTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* >(&rMesh);
558 mpNodesOnlyMesh = dynamic_cast<NodesOnlyMesh<SPACE_DIM>* >(&rMesh);
559
560 if (this->mpDistributedMesh == nullptr && mpNodesOnlyMesh == nullptr)
561 {
562 EXCEPTION("Cannot write parallel files using a sequential mesh");
563 }
564
566 {
567 return; // mWriteParallelFiles is not set sequentially (so we don't set up data exchange machinery)
568 }
569
570 mWriteParallelFiles = true;
571
572 // Populate the global to node index map (as this will be required to add point data)
573
574 //Node index that we are writing to VTK (index into mNodes and mHaloNodes as if they were concatenated)
575 unsigned index = 0;
576
577 // Owned nodes
579 node_iter != rMesh.GetNodeIteratorEnd();
580 ++node_iter)
581 {
582 mGlobalToNodeIndexMap[node_iter->GetIndex()] = index;
583 index++;
584 }
585
586 // Halo nodes
587 if (this->mpDistributedMesh)
588 {
589 for (typename DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::HaloNodeIterator halo_iter=this->mpDistributedMesh->GetHaloNodeIteratorBegin();
590 halo_iter != this->mpDistributedMesh->GetHaloNodeIteratorEnd();
591 ++halo_iter)
592 {
593 mGlobalToNodeIndexMap[(*halo_iter)->GetIndex()] = index;
594 index++;
595 }
596
597 //Calculate the halo exchange so that node-wise payloads can be communicated
598 this->mpDistributedMesh->CalculateNodeExchange( mNodesToSendPerProcess, mNodesToReceivePerProcess );
599 }
600}
601
603template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
606 bool keepOriginalElementIndexing)
607{
608 // Have we got a parallel mesh?
609 this->mpDistributedMesh = dynamic_cast<DistributedTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* >(&rMesh);
610 this->mpMixedMesh = dynamic_cast<MixedDimensionMesh<ELEMENT_DIM,SPACE_DIM>* >(&rMesh);
611
612 if (PetscTools::IsSequential() || !mWriteParallelFiles || (this->mpDistributedMesh == nullptr && mpNodesOnlyMesh == nullptr))
613 {
615 }
616 else
617 {
618 //Make the local mesh into a VtkMesh
619 vtkPoints* p_pts = vtkPoints::New(VTK_DOUBLE);
620 p_pts->GetData()->SetName("Vertex positions");
621
622 // Owned nodes
624 node_iter != rMesh.GetNodeIteratorEnd();
625 ++node_iter)
626 {
627 c_vector<double, SPACE_DIM> current_item = node_iter->rGetLocation();
628 if (SPACE_DIM == 3)
629 {
630 p_pts->InsertNextPoint(current_item[0], current_item[1], current_item[2]);
631 }
632 else if (SPACE_DIM == 2)
633 {
634 p_pts->InsertNextPoint(current_item[0], current_item[1], 0.0);
635 }
636 else // (SPACE_DIM == 1)
637 {
638 p_pts->InsertNextPoint(current_item[0], 0.0, 0.0);
639 }
640 }
641
642 // Halo nodes
643 if (this->mpDistributedMesh)
644 {
645 for (typename DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::HaloNodeIterator halo_iter=this->mpDistributedMesh->GetHaloNodeIteratorBegin();
646 halo_iter != this->mpDistributedMesh->GetHaloNodeIteratorEnd();
647 ++halo_iter)
648 {
649 c_vector<double, SPACE_DIM> current_item = (*halo_iter)->rGetLocation();
650 if (SPACE_DIM == 3)
651 {
652 p_pts->InsertNextPoint(current_item[0], current_item[1], current_item[2]);
653 }
654 else if (SPACE_DIM == 2)
655 {
656 p_pts->InsertNextPoint(current_item[0], current_item[1], 0.0);
657 }
658 else // (SPACE_DIM == 1)
659 {
660 p_pts->InsertNextPoint(current_item[0], 0.0, 0.0);
661 }
662 }
663 }
664
665 mpVtkUnstructedMesh->SetPoints(p_pts);
666 p_pts->Delete(); //Reference counted
667
669 elem_iter != rMesh.GetElementIteratorEnd();
670 ++elem_iter)
671 {
672
673 vtkCell* p_cell=nullptr;
675 if (ELEMENT_DIM == 3)
676 {
677 p_cell = vtkTetra::New();
678 }
679 else if (ELEMENT_DIM == 2)
680 {
681 p_cell = vtkTriangle::New();
682 }
683 else //(ELEMENT_DIM == 1)
684 {
685 p_cell = vtkLine::New();
686 }
687 vtkIdList* p_cell_id_list = p_cell->GetPointIds();
688 for (unsigned j = 0; j < ELEMENT_DIM+1; ++j)
689 {
690 unsigned global_node_index = elem_iter->GetNodeGlobalIndex(j);
691 p_cell_id_list->SetId(j, mGlobalToNodeIndexMap[global_node_index]);
692 }
693 mpVtkUnstructedMesh->InsertNextCell(p_cell->GetCellType(), p_cell_id_list);
694 p_cell->Delete(); //Reference counted
695 }
696 //If necessary, construct cables
697 if (this->mpMixedMesh )
698 {
699 AugmentCellData();
700 //Make a blank cell radius data for the regular elements
701 std::vector<double> radii(this->mpMixedMesh->GetNumLocalElements(), 0.0);
702 for (typename MixedDimensionMesh<ELEMENT_DIM,SPACE_DIM>::CableElementIterator elem_iter = this->mpMixedMesh->GetCableElementIteratorBegin();
703 elem_iter != this->mpMixedMesh->GetCableElementIteratorEnd();
704 ++elem_iter)
705 {
706 radii.push_back((*elem_iter)->GetAttribute());
707 vtkCell* p_cell=vtkLine::New();
708 vtkIdList* p_cell_id_list = p_cell->GetPointIds();
709 for (unsigned j = 0; j < 2; ++j)
710 {
711 unsigned global_node_index = (*elem_iter)->GetNodeGlobalIndex(j);
712 p_cell_id_list->SetId(j, mGlobalToNodeIndexMap[global_node_index]);
713 }
714 mpVtkUnstructedMesh->InsertNextCell(p_cell->GetCellType(), p_cell_id_list);
715 p_cell->Delete(); //Reference counted
716 }
717 AddCellData("Cable radius", radii);
718 }
719
720
721 //This block is to guard the mesh writers (vtkXMLPUnstructuredGridWriter) so that they
722 //go out of scope, flush buffers and close files
723 {
724 assert(mpVtkUnstructedMesh->CheckAttributes() == 0);
725 vtkXMLPUnstructuredGridWriter* p_writer = vtkXMLPUnstructuredGridWriter::New();
726
727 p_writer->SetDataModeToBinary();
728
729 p_writer->SetNumberOfPieces(PetscTools::GetNumProcs());
730 //p_writer->SetGhostLevel(-1);
731 p_writer->SetStartPiece(PetscTools::GetMyRank());
732 p_writer->SetEndPiece(PetscTools::GetMyRank());
733
734
735 p_writer->SetInputData(mpVtkUnstructedMesh);
736 std::string pvtk_file_name = this->mpOutputFileHandler->GetOutputDirectoryFullPath() + this->mBaseName+ ".pvtu";
737 p_writer->SetFileName(pvtk_file_name.c_str());
738 //p_writer->PrintSelf(std::cout, vtkIndent());
739 p_writer->Write();
740 p_writer->Delete(); //Reference counted
741 }
742
743 // Add provenance to the individual files
744 std::stringstream filepath;
745 filepath << this->mBaseName << "_" << PetscTools::GetMyRank() << ".vtu";
746 AddProvenance(filepath.str());
749 {
750 AddProvenance(this->mBaseName+ ".pvtu");
751 }
752 }
753}
754
755// Explicit instantiation
756template class VtkMeshWriter<1,1>;
757template class VtkMeshWriter<1,2>;
758template class VtkMeshWriter<1,3>;
759template class VtkMeshWriter<2,2>; // Actually used
760template class VtkMeshWriter<2,3>;
761template class VtkMeshWriter<3,3>; // Actually used
#define EXCEPTION(message)
#define UNUSED_OPT(var)
NodeIterator GetNodeIteratorEnd()
NodeIterator GetNodeIteratorBegin(bool skipDeletedNodes=true)
virtual void WriteFilesUsingMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, bool keepOriginalElementIndexing=true)
ElementIterator GetElementIteratorBegin(bool skipDeletedElements=true)
static std::string GetProvenanceString()
std::vector< Node< SPACE_DIM > * >::const_iterator HaloNodeIterator
std::vector< Element< 1, SPACE_DIM > * >::const_iterator CableElementIterator
static bool AmMaster()
static bool IsSequential()
static unsigned GetMyRank()
static unsigned GetNumProcs()
void AddPointData(std::string name, std::vector< double > data)
void SetParallelFiles(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
void AddTensorPointData(std::string name, std::vector< c_matrix< double, SPACE_DIM, SPACE_DIM > > data)
void SetWriteMeshCells(bool writeMeshCells)
void AddProvenance(std::string fileName)
void AddTensorCellData(std::string name, std::vector< c_vector< double, SPACE_DIM *(SPACE_DIM+1)/2 > > data)
void AddCellData(std::string name, std::vector< double > data)
VtkMeshWriter(const std::string &rDirectory, const std::string &rBaseName, const bool &rCleanDirectory=true)
void WriteFilesUsingMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, bool keepOriginalElementIndexing=true)
vtkUnstructuredGrid * mpVtkUnstructedMesh
virtual ~VtkMeshWriter()
std::vector< unsigned > NodeIndices