Chaste Commit::29c3c4ab2bee00ddd311a035a1e522c03cbe928a
ImmersedBoundaryMeshWriter.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 "ImmersedBoundaryMeshWriter.hpp"
37
38#include "ImmersedBoundaryArray.hpp"
41#include "Version.hpp"
42
46template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
56
58// Implementation
60
61template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
63 const std::string& rBaseName,
64 const bool clearOutputDir)
65 : AbstractMeshWriter<ELEMENT_DIM, SPACE_DIM>(rDirectory, rBaseName, clearOutputDir),
66 mpMesh(nullptr),
67 mpIters(new MeshWriterIterators<ELEMENT_DIM, SPACE_DIM>)
68{
69 mpIters->pNodeIter = nullptr;
70 mpIters->pElemIter = nullptr;
71 mpIters->pLamIter = nullptr;
72
73 switch (SPACE_DIM)
74 {
75 case 2:
76 {
77 geom_point corner_0(0.0, 0.0);
78 geom_point corner_1(1.0, 0.0);
79 geom_point corner_2(0.0, 1.0);
80 geom_point corner_3(1.0, 1.0);
81
82 mBoundaryEdges[0] = geom_segment(corner_0, corner_2); // left edge
83 mBoundaryEdges[1] = geom_segment(corner_1, corner_3); // right edge
84 mBoundaryEdges[2] = geom_segment(corner_0, corner_1); // bottom edge
85 mBoundaryEdges[3] = geom_segment(corner_2, corner_3); // top edge
86
87 break;
88 }
89
90 default:
92 }
93
94 // Dubious, since we shouldn't yet know what any details of the mesh are.
95 mpVtkUnstructedMesh = vtkUnstructuredGrid::New();
96}
97
98template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
100{
101 if (mpIters->pNodeIter)
102 {
103 delete mpIters->pNodeIter;
104 delete mpIters->pElemIter;
105 delete mpIters->pLamIter;
106 }
107
108 delete mpIters;
109
110// Dubious, since we shouldn't yet know what any details of the mesh are.
111 mpVtkUnstructedMesh->Delete(); // Reference counted
112}
113
114template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
116{
117 if (mpMesh)
118 {
119 // Sanity check
120 assert(this->mNumNodes == mpMesh->GetNumNodes());
121
122 std::vector<double> coordinates(SPACE_DIM+1);
123
124 // Get the node coordinates using the node iterator (thus skipping deleted nodes)
125 for (unsigned j=0; j<SPACE_DIM; j++)
126 {
127 coordinates[j] = (*(mpIters->pNodeIter))->GetPoint()[j];
128 }
129 coordinates[SPACE_DIM] = (*(mpIters->pNodeIter))->IsBoundaryNode();
130
131 ++(*(mpIters->pNodeIter));
132
133 return coordinates;
134 }
135 else
136 {
137 return AbstractMeshWriter<ELEMENT_DIM,SPACE_DIM>::GetNextNode(); //LCOV_EXCL_LINE fairly sure this is unreachable
138 }
139}
140
141template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
143{
144 // Can only be constructed in 2D currently
145 //LCOV_EXCL_START
146 if constexpr (SPACE_DIM != 2) {
147 EXCEPTION("ImmersedBoundaryMeshWriter::GetNextImmersedBoundaryElement is not yet implemetned in 3D!");
148 }
149 //LCOV_EXCL_STOP
150
151 assert(this->mNumElements == mpMesh->GetNumElements());
152
154 elem_data.NodeIndices.resize((*(mpIters->pElemIter))->GetNumNodes());
155 for (unsigned j=0; j<elem_data.NodeIndices.size(); j++)
156 {
157 elem_data.NodeIndices[j] = (*(mpIters->pElemIter))->GetNodeGlobalIndex(j);
158 }
159
160 // Set attribute
161 elem_data.AttributeValue = (*(mpIters->pElemIter))->GetAttribute();
162
163 // Immersed boundary specific data
164 auto& element = *(*mpIters->pElemIter);
165
166 if (element.GetFluidSource() != nullptr) {
167 elem_data.hasFluidSource = true;
168 elem_data.fluidSourceIndex = element.GetFluidSource()->GetIndex();
169 } else {
170 elem_data.hasFluidSource = false;
171 }
172
173 for (auto cornerNode : element.rGetCornerNodes()) {
174 elem_data.cornerNodeIndices.push_back(cornerNode->GetIndex());
175 }
176
177 elem_data.averageNodeSpacing = element.GetAverageNodeSpacing();
178
179 elem_data.isBoundaryElement = element.IsElementOnBoundary();
180
181 ++(*(mpIters->pElemIter));
182
183 return elem_data;
184} // LCOV_EXCL_LINE
185
186template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
188{
189 //LCOV_EXCL_START
190 if constexpr (SPACE_DIM != 2) {
191 EXCEPTION("ImmersedBoundaryMeshWriter::GetNextImmersedBoundaryLamina is not yet implemetned in 3D!");
192 }
193 //LCOV_EXCL_STOP
194
195 assert(mNumLaminas == mpMesh->GetNumLaminas());
196
197 ImmersedBoundaryElementData lamina_data;
198 lamina_data.NodeIndices.resize((*(mpIters->pLamIter))->GetNumNodes());
199 for (unsigned j=0; j<lamina_data.NodeIndices.size(); j++)
200 {
201 lamina_data.NodeIndices[j] = (*(mpIters->pLamIter))->GetNodeGlobalIndex(j);
202 }
203
204 // Set attribute
205 lamina_data.AttributeValue = (*(mpIters->pLamIter))->GetAttribute();
206
207 // Immersed boundary specific data
208 auto& lamina = *(*mpIters->pLamIter);
209
210 try {
211 lamina.GetFluidSource();
212 // Can't really support 2D laminas at the moment
213 //LCOV_EXCL_START
214 if (lamina.GetFluidSource() != nullptr) {
215 lamina_data.hasFluidSource = true;
216 lamina_data.fluidSourceIndex = lamina.GetFluidSource()->GetIndex();
217 } else {
218 lamina_data.hasFluidSource = false;
219 }
220 //LCOV_EXCL_STOP
221 } catch(Exception& e) {
222 lamina_data.hasFluidSource = false;
223 }
224
225 for (auto cornerNode : lamina.rGetCornerNodes()) {
226 lamina_data.cornerNodeIndices.push_back(cornerNode->GetIndex()); // LCOV_EXCL_LINE
227 }
228
229 lamina_data.averageNodeSpacing = lamina.GetAverageNodeSpacing();
230
231 lamina_data.isBoundaryElement = lamina.IsElementOnBoundary();
232
233 ++(*(mpIters->pLamIter));
234
235 return lamina_data;
236} // LCOV_EXCL_LINE
237
238template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
240{
241 if constexpr (SPACE_DIM == 2)
242 {
243 // Create VTK mesh
244 MakeVtkMesh(rMesh);
245 // Now write VTK mesh to file
246 //assert(mpVtkUnstructedMesh->CheckAttributes() == 0);
247 vtkXMLUnstructuredGridWriter* p_writer = vtkXMLUnstructuredGridWriter::New();
248 p_writer->SetInputData(mpVtkUnstructedMesh);
249 // Uninitialised stuff arises (see #1079), but you can remove valgrind problems by removing compression:
250 // **** REMOVE WITH CAUTION *****
251 p_writer->SetCompressor(NULL);
252 // **** REMOVE WITH CAUTION *****
253
254 std::string vtk_file_name = this->mpOutputFileHandler->GetOutputDirectoryFullPath() + this->mBaseName;
255 if (stamp != "")
256 {
257 vtk_file_name += "_" + stamp;
258 }
259 vtk_file_name += ".vtu";
260
261 p_writer->SetFileName(vtk_file_name.c_str());
262 p_writer->Write();
263 p_writer->Delete(); // Reference counted
264 }
265 else
266 {
268 }
269}
270
271template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
273{
287 // Assert mesh is 2D to simplify vtk creation
288 if constexpr (SPACE_DIM == 2)
289 {
290 FindElementOverlaps(rMesh);
291
292 // Make the Vtk mesh
293 vtkPoints* p_pts = vtkPoints::New(VTK_DOUBLE);
294 p_pts->GetData()->SetName("Vertex positions");
295
296 // Keep a track of the number of extra points that have been added to the purpose of visualisation
297 unsigned num_pts_added = 0;
298
299 // Next, we decide how to output the VTK data for elements depending on the type of overlap
300 for (auto iter = rMesh.GetElementIteratorBegin(); iter != rMesh.GetElementIteratorEnd(); ++iter)
301 {
302 unsigned elem_idx = iter->GetIndex();
303 unsigned num_nodes = iter->GetNumNodes();
304
305 // Case 1: no overlap
306 if (mElementParts.size() == 0 || mElementParts[elem_idx].empty())
307 {
308 vtkCell* p_cell = vtkPolygon::New();
309 vtkIdList* p_cell_id_list = p_cell->GetPointIds();
310 p_cell_id_list->SetNumberOfIds(num_nodes);
311
312 for (unsigned node_local_idx = 0; node_local_idx < num_nodes; ++node_local_idx)
313 {
314 // Get node, index and location
315 unsigned global_idx = iter->GetNode(node_local_idx)->GetIndex();
316 const auto& r_location = iter->GetNode(node_local_idx)->rGetLocation();
317
318 p_pts->InsertPoint(global_idx, r_location[0], r_location[1], 0.0);
319 p_cell_id_list->SetId(node_local_idx, global_idx);
320 }
321
322 mpVtkUnstructedMesh->InsertNextCell(p_cell->GetCellType(), p_cell_id_list);
323 p_cell->Delete(); // Reference counted
324 }
325 else // Case 2: at least one overlap
326 {
327 // Get the node index at the start of each part
328 const std::vector<unsigned>& start_idx_each_part = mElementParts[iter->GetIndex()];
329
330 // Get the number of parts, and put the first index onto the back of the vector for convenience
331 const auto num_parts = start_idx_each_part.size();
332
333 for (unsigned part = 0; part < num_parts; ++part)
334 {
335 const long this_start = start_idx_each_part[part];
336 const long next_start = start_idx_each_part[AdvanceMod(part, 1, num_parts)];
337
338 const long num_nodes_this_part = next_start > this_start ? next_start - this_start : num_nodes + next_start - this_start;
339
340 // Identify the extra points that need to be added
341 std::vector<c_vector<double, SPACE_DIM>> extra_locations;
342
343 // Start of the part
344 {
345 const auto& r_start_pos = iter->GetNode(this_start)->rGetLocation();
346 const auto& r_end_pos = iter->GetNode(AdvanceMod(this_start, -1, num_nodes))->rGetLocation();
347
348 const c_vector<double, SPACE_DIM> vec_a2b = rMesh.GetVectorFromAtoB(r_start_pos, r_end_pos);
349 extra_locations.emplace_back(GetIntersectionOfEdgeWithBoundary(r_start_pos, r_start_pos + vec_a2b));
350 }
351
352 // End of the part
353 {
354 const auto& r_start_pos = iter->GetNode(AdvanceMod(next_start, -1, num_nodes))->rGetLocation();
355 const auto& r_end_pos = iter->GetNode(next_start)->rGetLocation();
356
357 const c_vector<double, SPACE_DIM> vec_a2b = rMesh.GetVectorFromAtoB(r_start_pos, r_end_pos);
358 extra_locations.emplace_back(GetIntersectionOfEdgeWithBoundary(r_start_pos, r_start_pos + vec_a2b));
359 }
360
361 // If the two additional points are not on the same edge of the boundary, we also need to add a corner
362 if (std::fabs(extra_locations.front()[0] - extra_locations.back()[0]) > DBL_EPSILON &&
363 std::fabs(extra_locations.front()[1] - extra_locations.back()[1]) > DBL_EPSILON)
364 {
365 extra_locations.emplace_back(GetNearestCorner(extra_locations.front(), extra_locations.back()));
366 }
367
368 vtkCell* p_cell = vtkPolygon::New();
369 vtkIdList* p_cell_id_list = p_cell->GetPointIds();
370 p_cell_id_list->SetNumberOfIds(num_nodes_this_part + extra_locations.size());
371
372 for (long idx = 0; idx < num_nodes_this_part; ++idx)
373 {
374 unsigned node_idx = AdvanceMod(idx, this_start, num_nodes);
375
376 // Get index and location
377 unsigned global_idx = iter->GetNode(node_idx)->GetIndex();
378 const auto& r_location = iter->GetNode(node_idx)->rGetLocation();
379
380 p_pts->InsertPoint(global_idx, r_location[0], r_location[1], 0.0);
381 p_cell_id_list->SetId(idx, global_idx);
382 }
383
384 // Now add the extra locations
385 if (extra_locations.size() == 2)
386 {
387 const unsigned global_index = rMesh.GetNumNodes() + num_pts_added;
388
389 p_pts->InsertPoint(global_index, extra_locations[1][0], extra_locations[1][1], 0.0); // end
390 p_pts->InsertPoint(global_index + 1, extra_locations[0][0], extra_locations[0][1], 0.0); // start
391
392 p_cell_id_list->SetId(num_nodes_this_part, global_index);
393 p_cell_id_list->SetId(num_nodes_this_part + 1, global_index + 1);
394
395 num_pts_added += 2;
396 }
397 else if (extra_locations.size() == 3)
398 {
399 const unsigned global_index = rMesh.GetNumNodes() + num_pts_added;
400
401 p_pts->InsertPoint(global_index, extra_locations[1][0], extra_locations[1][1], 0.0); // end
402 p_pts->InsertPoint(global_index + 1, extra_locations[2][0], extra_locations[2][1], 0.0); // corner
403 p_pts->InsertPoint(global_index + 2, extra_locations[0][0], extra_locations[0][1], 0.0); // start
404
405 p_cell_id_list->SetId(num_nodes_this_part, global_index);
406 p_cell_id_list->SetId(num_nodes_this_part + 1, global_index + 1);
407 p_cell_id_list->SetId(num_nodes_this_part + 2, global_index + 2);
408
409 num_pts_added += 3;
410 }
411 else
412 {
414 }
415
416 mpVtkUnstructedMesh->InsertNextCell(p_cell->GetCellType(), p_cell_id_list);
417 p_cell->Delete(); // Reference counted
418 }
419 }
420 }
421
422 // Finally, output the VTK data for laminas
424 iter != rMesh.GetLaminaIteratorEnd();
425 ++iter)
426 {
427 unsigned num_nodes = iter->GetNumNodes();
428
429 vtkCell* p_cell = vtkPolygon::New();
430 vtkIdList* p_cell_id_list = p_cell->GetPointIds();
431 p_cell_id_list->SetNumberOfIds(num_nodes);
432
433 for (unsigned node_idx = 0; node_idx < num_nodes; node_idx++)
434 {
435 // Get node, index and location
436 Node<SPACE_DIM>* p_node = iter->GetNode(node_idx);
437 unsigned global_idx = p_node->GetIndex();
438
439 c_vector<double, SPACE_DIM> position = p_node->rGetLocation();
440 p_pts->InsertPoint(global_idx, position[0], position[1], 0.0);
441
442 p_cell_id_list->SetId(node_idx, global_idx);
443 }
444
445 mpVtkUnstructedMesh->InsertNextCell(3, p_cell_id_list);
446 p_cell->Delete(); // Reference counted
447 }
448
449 mpVtkUnstructedMesh->SetPoints(p_pts);
450 p_pts->Delete(); // Reference counted
451 }
452 else
453 {
455 }
456}
457
458//LCOV_EXCL_START
463template <>
467//LCOV_EXCL_STOP
468
469template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
470void ImmersedBoundaryMeshWriter<ELEMENT_DIM, SPACE_DIM>::AddCellData(std::string dataName, std::vector<double> dataPayload)
471{
472 vtkDoubleArray* p_scalars = vtkDoubleArray::New();
473 p_scalars->SetName(dataName.c_str());
474 for (unsigned i=0; i<dataPayload.size(); i++)
475 {
476 p_scalars->InsertNextValue(dataPayload[i]);
477 }
478
479 vtkCellData* p_cell_data = mpVtkUnstructedMesh->GetCellData();
480 p_cell_data->AddArray(p_scalars);
481 p_scalars->Delete(); // Reference counted
482}
483
484template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
485void ImmersedBoundaryMeshWriter<ELEMENT_DIM, SPACE_DIM>::AddPointData(std::string dataName, std::vector<double> dataPayload)
486{
487 vtkDoubleArray* p_scalars = vtkDoubleArray::New();
488 p_scalars->SetName(dataName.c_str());
489 for (double scalar : dataPayload)
490 {
491 p_scalars->InsertNextValue(scalar);
492 }
493
494 vtkPointData* p_point_data = mpVtkUnstructedMesh->GetPointData();
495 p_point_data->AddArray(p_scalars);
496 p_scalars->Delete(); // Reference counted
497}
498
499template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
501{
502 this->mpMeshReader = nullptr;
503 mpMesh = &rMesh;
504
505 this->mNumNodes = mpMesh->GetNumNodes();
506 this->mNumElements = mpMesh->GetNumElements();
507 mNumLaminas = mpMesh->GetNumLaminas();
508
509 typedef typename AbstractMesh<ELEMENT_DIM,SPACE_DIM>::NodeIterator NodeIterType;
510 mpIters->pNodeIter = new NodeIterType(mpMesh->GetNodeIteratorBegin());
511
513 mpIters->pElemIter = new ElemIterType(mpMesh->GetElementIteratorBegin());
514
516 mpIters->pLamIter = new LamIterType(mpMesh->GetLaminaIteratorBegin());
517
518 WriteFiles();
519}
520
521template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
523{
524 std::string comment = "# " + ChasteBuildInfo::GetProvenanceString();
525
526 // Write node file
527 std::string node_file_name = this->mBaseName + ".node";
528 out_stream p_node_file = this->mpOutputFileHandler->OpenOutputFile(node_file_name);
529
530 // Write the node header
531 unsigned num_attr = 0;
532 unsigned max_bdy_marker = 1; // as we include boundary node information in the node file
533 unsigned num_nodes = this->GetNumNodes();
534
535 *p_node_file << num_nodes << "\t";
536 *p_node_file << SPACE_DIM << "\t";
537 *p_node_file << num_attr << "\t";
538 *p_node_file << mpMesh->GetCharacteristicNodeSpacing() << "\t";
539 *p_node_file << max_bdy_marker << "\n";
540 *p_node_file << std::setprecision(6);
541
542 // Write each node's data
543 for (unsigned item_num=0; item_num<num_nodes; item_num++)
544 {
545 std::vector<double> current_item = this->GetNextNode();
546 *p_node_file << item_num;
547 for (unsigned i=0; i<SPACE_DIM+1; i++)
548 {
549 *p_node_file << "\t" << current_item[i];
550 }
551 *p_node_file << "\n";
552 }
553 *p_node_file << comment << "\n";
554 p_node_file->close();
555
556 // Write element file
557 std::string element_file_name = this->mBaseName + ".elem";
558 out_stream p_element_file = this->mpOutputFileHandler->OpenOutputFile(element_file_name);
559
560 // Write the element header
561 num_attr = 1; //Always write element attributes
562 unsigned num_elements = this->GetNumElements();
563 *p_element_file << num_elements << "\t" << num_attr << "\n";
564
565 // Write each element's data
566 for (unsigned item_num=0; item_num<num_elements; item_num++)
567 {
568 if (SPACE_DIM == 2) // In 2D, write the node indices owned by this element
569 {
570 // Get data for this element
571 ImmersedBoundaryElementData elem_data = this->GetNextImmersedBoundaryElement();
572
573 // Get the node indices owned by this element
574 std::vector<unsigned> node_indices = elem_data.NodeIndices;
575
576 // Write this element's index and the number of nodes owned by it to file
577 *p_element_file << item_num << "\t" << node_indices.size();
578
579 // Write the node indices owned by this element to file
580 for (unsigned node_index : node_indices)
581 {
582 *p_element_file << "\t" << node_index;
583 }
584
585 *p_element_file << "\t" << elem_data.AttributeValue;
586
587 // Has fluid source
588 *p_element_file << "\t" << elem_data.hasFluidSource;
589
590 // Fluid source index
591 if (elem_data.hasFluidSource) {
592 *p_element_file << "\t" << elem_data.fluidSourceIndex;
593 }
594
595 // Corner node indices
596 *p_element_file << "\t" << elem_data.cornerNodeIndices.size();
597
598 for (auto nodeIndex : elem_data.cornerNodeIndices) {
599 *p_element_file << "\t" << nodeIndex;
600 }
601
602 // Average node spacing
603 *p_element_file << "\t" << elem_data.averageNodeSpacing;
604
605 // Is boundary element
606 *p_element_file << "\t" << elem_data.isBoundaryElement;
607
608 // New line
609 *p_element_file << "\n";
610 }
611 else // 3D
612 {
613 }
614 }
615 *p_element_file << comment << "\n";
616 p_element_file->close();
617
618 // Write lamina file
619 std::string lamina_file_name = this->mBaseName + ".lam";
620 out_stream p_lamina_file = this->mpOutputFileHandler->OpenOutputFile(lamina_file_name);
621
622 // Write the lamina header
623 num_attr = 1; //Always write element attributes
624 unsigned num_laminas = mpMesh->GetNumLaminas();
625 *p_lamina_file << num_laminas << "\t" << num_attr << "\n";
626
627 // Write each lamina's data
628 for (unsigned item_num=0; item_num<num_laminas; item_num++)
629 {
630 if (SPACE_DIM == 2) // In 2D, write the node indices owned by this element
631 {
632 // Get data for this element
633 ImmersedBoundaryElementData lamina_data = this->GetNextImmersedBoundaryLamina();
634
635 // Get the node indices owned by this element
636 std::vector<unsigned> node_indices = lamina_data.NodeIndices;
637
638 // Write this element's index and the number of nodes owned by it to file
639 *p_lamina_file << item_num << "\t" << node_indices.size();
640
641 // Write the node indices owned by this element to file
642 for (unsigned i=0; i<node_indices.size(); i++)
643 {
644 *p_lamina_file << "\t" << node_indices[i];
645 }
646
647 *p_lamina_file << "\t" << lamina_data.AttributeValue;
648
649 // Has fluid source
650 *p_lamina_file << "\t" << lamina_data.hasFluidSource;
651
652 // Fluid source index
653 if (lamina_data.hasFluidSource) {
654 *p_lamina_file << "\t" << lamina_data.fluidSourceIndex; //LCOV_EXCL_LINE
655 }
656
657 // Corner node indices
658 *p_lamina_file << "\t" << lamina_data.cornerNodeIndices.size();
659
660 for (auto nodeIndex : lamina_data.cornerNodeIndices) {
661 *p_lamina_file << "\t" << nodeIndex; //LCOV_EXCL_LINE
662 }
663
664 // Average node spacing
665 *p_lamina_file << "\t" << lamina_data.averageNodeSpacing;
666
667 // Is boundary lamina
668 *p_lamina_file << "\t" << lamina_data.isBoundaryElement;
669
670
671 // New line
672 *p_lamina_file << "\n";
673 }
674 else // 3D
675 {
676 }
677 }
678 *p_lamina_file << comment << "\n";
679 p_lamina_file->close();
680
681 // Write grid file
682 std::string grid_file_name = this->mBaseName + ".grid";
683 out_stream p_grid_file = this->mpOutputFileHandler->OpenOutputFile(grid_file_name);
684
685 // Write the element header
686 unsigned num_gridpts_x = mpMesh->GetNumGridPtsX();
687 unsigned num_gridpts_y = mpMesh->GetNumGridPtsY();
688
689 *p_grid_file << num_gridpts_x << "\t" << num_gridpts_y << "\n";
690
691 // Write grid data
692 const multi_array<double, 3>& vel_grids = mpMesh->rGet2dVelocityGrids();
693
694 for (unsigned y_idx = 0; y_idx < num_gridpts_y; y_idx ++)
695 {
696 for (unsigned x_idx = 0; x_idx < num_gridpts_x; x_idx ++)
697 {
698 *p_grid_file << vel_grids[0][x_idx][y_idx] << "\t";
699 }
700 *p_grid_file << "\n";
701 }
702
703 for (unsigned y_idx = 0; y_idx < num_gridpts_y; y_idx ++)
704 {
705 for (unsigned x_idx = 0; x_idx < num_gridpts_x; x_idx ++)
706 {
707 *p_grid_file << vel_grids[1][x_idx][y_idx] << "\t";
708 }
709 *p_grid_file << "\n";
710 }
711
712 *p_grid_file << comment << "\n";
713 p_grid_file->close();
714}
715
716template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
718{
719 if constexpr (SPACE_DIM == 2)
720 {
721 // Resize and initialise the vector of overlaps (bools; whether each element has an overlap)
722 mElementParts.resize(rMesh.GetNumAllElements());
723 for (auto& parts : mElementParts)
724 {
725 parts.clear();
726 }
727
728 // We loop over each element and the node index at each discontinuity due to periodic boundaries
729 for (auto iter = rMesh.GetElementIteratorBegin(); iter != rMesh.GetElementIteratorEnd(); ++iter)
730 {
731 for (unsigned node_idx = 0; node_idx < iter->GetNumNodes(); ++node_idx)
732 {
733 const unsigned prev_idx = AdvanceMod(node_idx, -1, iter->GetNumNodes());
734
735 const auto& this_location = iter->GetNode(node_idx)->rGetLocation();
736 const auto& prev_location = iter->GetNode(prev_idx)->rGetLocation();
737
738 if (norm_inf(this_location - prev_location) > 0.5)
739 {
740 mElementParts[iter->GetIndex()].emplace_back(node_idx);
741 }
742 }
743 }
744 }
745 else
746 {
748 }
749}
750
751template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
753 const c_vector<double, SPACE_DIM>& rStart,
754 const c_vector<double, SPACE_DIM>& rEnd)
755{
756 // Note that this function relies on SPACE_DIM == 2
757
758 // Turn the c_vector start and end into a boost::geometry segment object
759 geom_point start(rStart[0], rStart[1]);
760 geom_point end(rEnd[0], rEnd[1]);
761 geom_segment edge(start, end);
762
763 // Identify which boundary edge the intersection is with
764 std::vector<geom_point> intersections;
765 for (const auto& boundary_edge : mBoundaryEdges)
766 {
767 if (boost::geometry::intersects(edge, boundary_edge))
768 {
769 boost::geometry::intersection(edge, boundary_edge, intersections);
770 break;
771 }
772 }
773
774 // There should be exactly one intersection
775 if (intersections.size() != 1)
776 {
778 }
779
780 return Create_c_vector(intersections.front().get<0>(), intersections.front().get<1>());
781}
782
783template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
785 const c_vector<double, SPACE_DIM>& rA, const c_vector<double, SPACE_DIM>& rB) const
786{
787 // Identify the nearest corner to the average location of the two vectors
788 const double x = 0.5 * (rA[0] + rB[0]) < 0.5 ? 0.0 : 1.0;
789 const double y = 0.5 * (rA[1] + rB[1]) < 0.5 ? 0.0 : 1.0;
790
791 return Create_c_vector(x, y);
792}
793
794template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
795const std::vector<std::vector<unsigned>>& ImmersedBoundaryMeshWriter<ELEMENT_DIM, SPACE_DIM>::rGetElementParts() const
796{
797 return mElementParts;
798}
799
800// Explicit instantiation
#define EXCEPTION(message)
#define NEVER_REACHED
virtual std::vector< double > GetNextNode()
static std::string GetProvenanceString()
ImmersedBoundaryMeshWriter(const std::string &rDirectory, const std::string &rBaseName, bool clearOutputDir=true)
boost::geometry::model::point< double, 2, boost::geometry::cs::cartesian > geom_point
void FindElementOverlaps(ImmersedBoundaryMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
ImmersedBoundaryElementData GetNextImmersedBoundaryElement()
c_vector< double, SPACE_DIM > GetNearestCorner(const c_vector< double, SPACE_DIM > &rA, const c_vector< double, SPACE_DIM > &rB) const
ImmersedBoundaryElementData GetNextImmersedBoundaryLamina()
const std::vector< std::vector< unsigned > > & rGetElementParts() const
void WriteVtkUsingMesh(ImmersedBoundaryMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, std::string stamp="")
void AddPointData(std::string dataName, std::vector< double > dataPayload)
void MakeVtkMesh(ImmersedBoundaryMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
std::array< geom_segment, 4 > mBoundaryEdges
void WriteFilesUsingMesh(ImmersedBoundaryMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
boost::geometry::model::segment< geom_point > geom_segment
c_vector< double, SPACE_DIM > GetIntersectionOfEdgeWithBoundary(const c_vector< double, SPACE_DIM > &rStart, const c_vector< double, SPACE_DIM > &rEnd)
void AddCellData(std::string dataName, std::vector< double > dataPayload)
MeshWriterIterators< ELEMENT_DIM, SPACE_DIM > * mpIters
ImmersedBoundaryElementIterator GetElementIteratorEnd()
ImmersedBoundaryLaminaIterator GetLaminaIteratorBegin(bool skipDeletedLaminas=true)
ImmersedBoundaryElementIterator GetElementIteratorBegin(bool skipDeletedElements=true)
ImmersedBoundaryLaminaIterator GetLaminaIteratorEnd()
virtual unsigned GetNumNodes() const
c_vector< double, SPACE_DIM > GetVectorFromAtoB(const c_vector< double, SPACE_DIM > &rLocation1, const c_vector< double, SPACE_DIM > &rLocation2)
unsigned GetNumAllElements() const
Definition Node.hpp:59
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition Node.cpp:139
unsigned GetIndex() const
Definition Node.cpp:158
ImmersedBoundaryMesh< ELEMENT_DIM, SPACE_DIM >::ImmersedBoundaryElementIterator * pElemIter
ImmersedBoundaryMesh< ELEMENT_DIM, SPACE_DIM >::ImmersedBoundaryLaminaIterator * pLamIter
AbstractMesh< ELEMENT_DIM, SPACE_DIM >::NodeIterator * pNodeIter