Chaste Commit::675f9facbe008c5eacb9006feaeb6423206579ea
NodeBasedCellPopulation.cpp
1/*
2
3Copyright (c) 2005-2025, 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 "NodeBasedCellPopulation.hpp"
38#include "VtkMeshWriter.hpp"
39
40template<unsigned DIM>
42 std::vector<CellPtr>& rCells,
43 const std::vector<unsigned> locationIndices,
44 bool deleteMesh,
45 bool validate)
46 : AbstractCentreBasedCellPopulation<DIM>(rMesh, rCells, locationIndices),
47 mDeleteMesh(deleteMesh),
48 mUseVariableRadii(false),
49 mLoadBalanceMesh(false),
50 mLoadBalanceFrequency(100)
51{
52 mpNodesOnlyMesh = static_cast<NodesOnlyMesh<DIM>* >(&(this->mrMesh));
53
54 if (validate)
55 {
56 Validate();
57 }
58}
59
60template<unsigned DIM>
63 mDeleteMesh(true),
64 mUseVariableRadii(false), // will be set by serialize() method
65 mLoadBalanceMesh(false),
66 mLoadBalanceFrequency(100)
67{
68 mpNodesOnlyMesh = static_cast<NodesOnlyMesh<DIM>* >(&(this->mrMesh));
69}
70
71template<unsigned DIM>
73{
74 Clear();
75 if (mDeleteMesh)
76 {
77 delete &this->mrMesh;
78 }
79}
80
81template<unsigned DIM>
83{
84 return *mpNodesOnlyMesh;
85}
86
87template<unsigned DIM>
89{
90 return *mpNodesOnlyMesh;
91}
92
93template<unsigned DIM>
95{
96 // Note that this code does not yet work in parallel.
98
99 std::vector<Node<DIM>*> temp_nodes;
100
101 // Get the nodes of mpNodesOnlyMesh
102 for (typename AbstractMesh<DIM,DIM>::NodeIterator node_iter = mpNodesOnlyMesh->GetNodeIteratorBegin();
103 node_iter != mpNodesOnlyMesh->GetNodeIteratorEnd();
104 ++node_iter)
105 {
106 temp_nodes.push_back(new Node<DIM>(node_iter->GetIndex(), node_iter->rGetLocation(), node_iter->IsBoundaryNode()));
107 }
108
109 return new MutableMesh<DIM,DIM>(temp_nodes);
110}
111
112template<unsigned DIM>
114{
115 this->mNodePairs.clear();
116}
117
118template<unsigned DIM>
120{
121 for (typename AbstractMesh<DIM,DIM>::NodeIterator node_iter = this->mrMesh.GetNodeIteratorBegin();
122 node_iter != this->mrMesh.GetNodeIteratorEnd();
123 ++node_iter)
124 {
125 try
126 {
127 this->GetCellUsingLocationIndex(node_iter->GetIndex());
128 }
129 catch (Exception&)
130 {
131 EXCEPTION("At time " << SimulationTime::Instance()->GetTime() << ", Node " << node_iter->GetIndex() << " does not appear to have a cell associated with it");
132 }
133 }
134}
135
136template<unsigned DIM>
138{
139 return mpNodesOnlyMesh->GetNodeOrHaloNode(index);
140}
141
142template<unsigned DIM>
143void NodeBasedCellPopulation<DIM>::SetNode(unsigned nodeIndex, ChastePoint<DIM>& rNewLocation)
144{
145 mpNodesOnlyMesh->SetNode(nodeIndex, rNewLocation, false);
146}
147
148template<unsigned DIM>
149void NodeBasedCellPopulation<DIM>::Update(bool hasHadBirthsOrDeaths)
150{
151 UpdateCellProcessLocation();
152
153 mpNodesOnlyMesh->UpdateBoxCollection();
154
155 if (mLoadBalanceMesh)
156 {
157 if ((SimulationTime::Instance()->GetTimeStepsElapsed() % mLoadBalanceFrequency) == 0)
158 {
159 mpNodesOnlyMesh->LoadBalanceMesh();
160
161 UpdateCellProcessLocation();
162
163 mpNodesOnlyMesh->UpdateBoxCollection();
164 }
165 }
166
167 RefreshHaloCells();
168
169 mpNodesOnlyMesh->CalculateInteriorNodePairs(this->mNodePairs);
170
171 AddReceivedHaloCells();
172
173 mpNodesOnlyMesh->CalculateBoundaryNodePairs(this->mNodePairs);
174
175 /*
176 * Update cell radii based on CellData
177 */
178 if (mUseVariableRadii)
179 {
180 for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = this->Begin();
181 cell_iter != this->End();
182 ++cell_iter)
183 {
184 double cell_radius = cell_iter->GetCellData()->GetItem("Radius");
185 unsigned node_index = this->GetLocationIndexUsingCell(*cell_iter);
186 this->GetNode(node_index)->SetRadius(cell_radius);
187 }
188 }
189
190 // Make sure that everyone exits update together so that all asynchronous communications are complete.
191 PetscTools::Barrier("Update");
192}
193
194template<unsigned DIM>
196{
197 if (!map.IsIdentityMap())
198 {
199 UpdateParticlesAfterReMesh(map);
200
201 // Update the mappings between cells and location indices
203 std::map<Cell*, unsigned> old_map = this->mCellLocationMap;
204
205 // Remove any dead pointers from the maps (needed to avoid archiving errors)
206 this->mLocationCellMap.clear();
207 this->mCellLocationMap.clear();
208
209 for (std::list<CellPtr>::iterator it = this->mCells.begin();
210 it != this->mCells.end();
211 ++it)
212 {
213 unsigned old_node_index = old_map[(*it).get()];
214
215 // This shouldn't ever happen, as the cell vector only contains living cells
216 assert(!map.IsDeleted(old_node_index));
217
218 unsigned new_node_index = map.GetNewIndex(old_node_index);
219 this->SetCellUsingLocationIndex(new_node_index,*it);
220 }
221
222 this->Validate();
223 }
224}
225
226template<unsigned DIM>
228{
229 unsigned num_removed = 0;
230 for (std::list<CellPtr>::iterator cell_iter = this->mCells.begin();
231 cell_iter != this->mCells.end();
232 )
233 {
234 if ((*cell_iter)->IsDead())
235 {
236 // Remove the node from the mesh
237 num_removed++;
238 unsigned location_index = mpNodesOnlyMesh->SolveNodeMapping(this->GetLocationIndexUsingCell((*cell_iter)));
239 mpNodesOnlyMesh->DeleteNodePriorToReMesh(location_index);
240
241 // Update mappings between cells and location indices
242 unsigned location_index_of_removed_node = this->GetLocationIndexUsingCell((*cell_iter));
243 this->RemoveCellUsingLocationIndex(location_index_of_removed_node, (*cell_iter));
244
245 // Update vector of cells
246 cell_iter = this->mCells.erase(cell_iter);
247 }
248 else
249 {
250 ++cell_iter;
251 }
252 }
253
254 return num_removed;
255}
256
257template<unsigned DIM>
259{
260 return mpNodesOnlyMesh->AddNode(pNewNode);
261}
262
263template<unsigned DIM>
265{
266 return mpNodesOnlyMesh->GetNumNodes();
267}
268
269template<unsigned DIM>
271{
272 std::map<unsigned, CellPtr>::iterator iter = mLocationHaloCellMap.find(index);
273 if (iter != mLocationHaloCellMap.end())
274 {
275 return iter->second;
276 }
277 else
278 {
280 }
281}
282
283template<unsigned DIM>
287
288template<unsigned DIM>
290{
291 *rParamsFile << "\t\t<MechanicsCutOffLength>" << mpNodesOnlyMesh->GetMaximumInteractionDistance() << "</MechanicsCutOffLength>\n";
292 *rParamsFile << "\t\t<UseVariableRadii>" << mUseVariableRadii << "</UseVariableRadii>\n";
293
294 // Call method on direct parent class
296}
297
298template<unsigned DIM>
300{
301 pPopulationWriter->Visit(this);
302}
303
304template<unsigned DIM>
306{
307 pPopulationCountWriter->Visit(this);
308}
309
310template<unsigned DIM>
312{
313 pPopulationEventWriter->Visit(this);
314}
315
316template<unsigned DIM>
317void NodeBasedCellPopulation<DIM>::AcceptCellWriter(boost::shared_ptr<AbstractCellWriter<DIM, DIM> > pCellWriter, CellPtr pCell)
318{
319 pCellWriter->VisitCell(pCell, this);
320}
321
322template<unsigned DIM>
324{
325 return mpNodesOnlyMesh->GetMaximumInteractionDistance();
326}
327
328template<unsigned DIM>
330{
331 return mUseVariableRadii;
332}
333
334template<unsigned DIM>
336{
337 mUseVariableRadii = useVariableRadii;
338}
339
340template<unsigned DIM>
342{
343 mLoadBalanceMesh = loadBalanceMesh;
344}
345
346template<unsigned DIM>
348{
349 mLoadBalanceFrequency = loadBalanceFrequency;
350}
351
352template<unsigned DIM>
353double NodeBasedCellPopulation<DIM>::GetWidth(const unsigned& rDimension)
354{
355 return mpNodesOnlyMesh->GetWidth(rDimension);
356}
357
358template<unsigned DIM>
360{
361 c_vector<double, DIM> local_size = AbstractCellPopulation<DIM, DIM>::GetSizeOfCellPopulation();
362 c_vector<double, DIM> global_size;
363
364 for (unsigned i=0; i<DIM; i++)
365 {
366 MPI_Allreduce(&local_size[i], &global_size[i], 1, MPI_DOUBLE, MPI_MAX, PetscTools::GetWorld());
367 }
368
369 return global_size;
370}
371
372template<unsigned DIM>
373std::set<unsigned> NodeBasedCellPopulation<DIM>::GetNodesWithinNeighbourhoodRadius(unsigned index, double neighbourhoodRadius)
374{
375 // Check neighbourhoodRadius is less than the interaction radius. If not you wont return all the correct nodes
376 if (neighbourhoodRadius > mpNodesOnlyMesh->GetMaximumInteractionDistance())
377 {
378 EXCEPTION("neighbourhoodRadius should be less than or equal to the the maximum interaction radius defined on the NodesOnlyMesh");
379 }
380
381 std::set<unsigned> neighbouring_node_indices;
382
383 // Get location
384 Node<DIM>* p_node_i = this->GetNode(index);
385 const c_vector<double, DIM>& r_node_i_location = p_node_i->rGetLocation();
386
387 // Check the mNodeNeighbours has been set up correctly
388 if (!(p_node_i->GetNeighboursSetUp()))
389 {
390 EXCEPTION("mNodeNeighbours not set up. Call Update() before GetNodesWithinNeighbourhoodRadius()");
391 }
392
393 // Get set of 'candidate' neighbours.
394 std::vector<unsigned>& near_nodes = p_node_i->rGetNeighbours();
395
396 // Find which ones are actually close
397 for (std::vector<unsigned>::iterator iter = near_nodes.begin();
398 iter != near_nodes.end();
399 ++iter)
400 {
401 // Be sure not to return the index itself.
402 if ((*iter) != index)
403 {
404 Node<DIM>* p_node_j = this->GetNode((*iter));
405
406 // Get the location of this node
407 const c_vector<double, DIM>& r_node_j_location = p_node_j->rGetLocation();
408
409 // Get the vector the two nodes (using GetVectorFromAtoB to catch periodicities etc.)
410 c_vector<double, DIM> node_to_node_vector = mpNodesOnlyMesh->GetVectorFromAtoB(r_node_j_location, r_node_i_location);
411
412 // Calculate the distance between the two nodes
413 double distance_between_nodes = norm_2(node_to_node_vector);
414
415 // If the cell j is within the neighbourhood of radius neighbourhoodRadius
416 // of cell i
417 if (distance_between_nodes <= neighbourhoodRadius)// + DBL_EPSILSON)
418 {
419 // ...then add this node index to the set of neighbouring node indices
420 neighbouring_node_indices.insert((*iter));
421 }
422 }
423 }
424
425 return neighbouring_node_indices;
426}
427
428template<unsigned DIM>
430{
431 std::set<unsigned> neighbouring_node_indices;
432
433 // Get location and radius of node
434 Node<DIM>* p_node_i = this->GetNode(index);
435 const c_vector<double, DIM>& r_node_i_location = p_node_i->rGetLocation();
436 double radius_of_cell_i = p_node_i->GetRadius();
437
438 // Check the mNodeNeighbours has been set up correctly
439 if (!(p_node_i->GetNeighboursSetUp()))
440 {
441 EXCEPTION("mNodeNeighbours not set up. Call Update() before GetNeighbouringNodeIndices()");
442 }
443
444 // Make sure that the max_interaction distance is smaller than or equal to the box collection size
445 if (!(radius_of_cell_i * 2.0 <= mpNodesOnlyMesh->GetMaximumInteractionDistance()))
446 {
447 EXCEPTION("mpNodesOnlyMesh::mMaxInteractionDistance is smaller than twice the radius of cell " << index << " (" << radius_of_cell_i << ") so interactions may be missed. Make the cut-off larger to avoid errors.");
448 }
449
450 // Get set of 'candidate' neighbours
451 std::vector<unsigned>& near_nodes = p_node_i->rGetNeighbours();
452
453 // Find which ones are actually close
454 for (std::vector<unsigned>::iterator iter = near_nodes.begin();
455 iter != near_nodes.end();
456 ++iter)
457 {
458 // Be sure not to return the index itself
459 if ((*iter) != index)
460 {
461 Node<DIM>* p_node_j = this->GetNode((*iter));
462
463 // Get the location of this node
464 const c_vector<double, DIM>& r_node_j_location = p_node_j->rGetLocation();
465
466 // Get the vector the two nodes (using GetVectorFromAtoB to catch periodicities etc.)
467 c_vector<double, DIM> node_to_node_vector = mpNodesOnlyMesh->GetVectorFromAtoB(r_node_j_location, r_node_i_location);
468
469 // Calculate the distance between the two nodes
470 double distance_between_nodes = norm_2(node_to_node_vector);
471
472 // Get the radius of the cell corresponding to this node
473 double radius_of_cell_j = p_node_j->GetRadius();
474
475 // If the cells are close enough to exert a force on each other...
476 double max_interaction_distance = radius_of_cell_i + radius_of_cell_j;
477
478 // Make sure that the max_interaction distance is smaller than or equal to the box collection size
479 if (!(max_interaction_distance <= mpNodesOnlyMesh->GetMaximumInteractionDistance()))
480 {
481 EXCEPTION("mpNodesOnlyMesh::mMaxInteractionDistance is smaller than the sum of radius of cell " << index << " (" << radius_of_cell_i << ") and cell " << (*iter) << " (" << radius_of_cell_j <<"). Make the cut-off larger to avoid errors.");
482 }
483 if (distance_between_nodes <= max_interaction_distance)// + DBL_EPSILSON) //Assumes that max_interaction_distance is of order 1
484 {
485 // ...then add this node index to the set of neighbouring node indices
486 neighbouring_node_indices.insert((*iter));
487 }
488 }
489 }
490
491 return neighbouring_node_indices;
492}
493
494template <unsigned DIM>
495double NodeBasedCellPopulation<DIM>::GetVolumeOfCell([[maybe_unused]] CellPtr pCell)
496{
497 // Not implemented or tested in 1D
498 if constexpr ((DIM == 2) || (DIM == 3))
499 {
500 // Get node index corresponding to this cell
501 unsigned node_index = this->GetLocationIndexUsingCell(pCell);
502 Node<DIM>* p_node = this->GetNode(node_index);
503
504 // Get cell radius
505 double cell_radius = p_node->GetRadius();
506
507 // Begin code to approximate cell volume
508 double averaged_cell_radius = 0.0;
509 unsigned num_cells = 0;
510
511 // Get the location of this node
512 const c_vector<double, DIM>& r_node_i_location = GetNode(node_index)->rGetLocation();
513
514 // Get the set of node indices corresponding to this cell's neighbours
515 std::set<unsigned> neighbouring_node_indices = GetNeighbouringNodeIndices(node_index);
516
517 // THe number of neighbours in equilibrium configuration, from sphere packing problem
518 unsigned num_neighbours_equil;
519 if (DIM == 2)
520 {
521 num_neighbours_equil = 6;
522 }
523 else // DIM == 3
524 {
525 num_neighbours_equil = 12;
526 }
527
528 // Loop over this set
529 for (std::set<unsigned>::iterator iter = neighbouring_node_indices.begin();
530 iter != neighbouring_node_indices.end();
531 ++iter)
532 {
533 Node<DIM>* p_node_j = this->GetNode(*iter);
534
535 // Get the location of the neighbouring node
536 const c_vector<double, DIM>& r_node_j_location = p_node_j->rGetLocation();
537
538 double neighbouring_cell_radius = p_node_j->GetRadius();
539
540 // If this throws then you may not be considering all cell interactions use a larger cut off length
541 assert(cell_radius+neighbouring_cell_radius<mpNodesOnlyMesh->GetMaximumInteractionDistance());
542
543 // Calculate the distance between the two nodes and add to cell radius
544 double separation = norm_2(mpNodesOnlyMesh->GetVectorFromAtoB(r_node_j_location, r_node_i_location));
545
546 if (separation < cell_radius+neighbouring_cell_radius)
547 {
548 // The effective radius is the mid point of the overlap
549 averaged_cell_radius = averaged_cell_radius + cell_radius - (cell_radius+neighbouring_cell_radius-separation)/2.0;
550 num_cells++;
551 }
552 }
553 if (num_cells < num_neighbours_equil)
554 {
555 averaged_cell_radius += (num_neighbours_equil-num_cells)*cell_radius;
556
557 averaged_cell_radius /= num_neighbours_equil;
558 }
559 else
560 {
561 averaged_cell_radius /= num_cells;
562 }
563 assert(averaged_cell_radius < mpNodesOnlyMesh->GetMaximumInteractionDistance()/2.0);
564
565 cell_radius = averaged_cell_radius;
566
567 // End code to approximate cell volume
568
569 // Calculate cell volume from radius of cell
570 double cell_volume = 0.0;
571 if (DIM == 2)
572 {
573 cell_volume = M_PI*cell_radius*cell_radius;
574 }
575 else // DIM == 3
576 {
577 cell_volume = (4.0/3.0)*M_PI*cell_radius*cell_radius*cell_radius;
578 }
579
580 return cell_volume;
581 }
582 else
583 {
585 }
586}
587
588template<unsigned DIM>
590{
591#ifdef CHASTE_VTK
592 // Store the present time as a string
593 std::stringstream time;
595
596 // Make sure the nodes are ordered contiguously in memory
597 NodeMap map(1 + this->mpNodesOnlyMesh->GetMaximumNodeIndex());
598 this->mpNodesOnlyMesh->ReMesh(map);
599
600 // Create mesh writer for VTK output
601 VtkMeshWriter<DIM, DIM> mesh_writer(rDirectory, "results_"+time.str(), false);
602 mesh_writer.SetParallelFiles(*mpNodesOnlyMesh);
603
604 auto num_nodes = GetNumNodes();
605
606 // For each cell that this process owns, find the corresponding node index, which we only want to calculate once
607 std::vector<unsigned> node_indices_in_cell_order;
608 for (auto cell_iter = this->Begin(); cell_iter != this->End(); ++cell_iter)
609 {
610 // Get the node index corresponding to this cell
611 unsigned global_index = this->GetLocationIndexUsingCell(*cell_iter);
612 unsigned node_index = this->rGetMesh().SolveNodeMapping(global_index);
613
614 node_indices_in_cell_order.emplace_back(node_index);
615 }
616
617 // Iterate over any cell writers that are present. This is in a separate loop to below, because the writer loop
618 // needs to the the outer loop.
619 for (auto&& p_cell_writer : this->mCellWriters)
620 {
621 // Add any scalar data
622 if (p_cell_writer->GetOutputScalarData())
623 {
624 std::vector<double> vtk_cell_data(num_nodes);
625
626 unsigned loop_it = 0;
627 for (auto cell_iter = this->Begin(); cell_iter != this->End(); ++cell_iter, ++loop_it)
628 {
629 unsigned node_idx = node_indices_in_cell_order[loop_it];
630 vtk_cell_data[node_idx] = p_cell_writer->GetCellDataForVtkOutput(*cell_iter, this);
631 }
632
633 mesh_writer.AddPointData(p_cell_writer->GetVtkCellDataName(), vtk_cell_data);
634 }
635
636 // Add any vector data
637 if (p_cell_writer->GetOutputVectorData())
638 {
639 std::vector<c_vector<double, DIM>> vtk_cell_data(num_nodes);
640
641 unsigned loop_it = 0;
642 for (auto cell_iter = this->Begin(); cell_iter != this->End(); ++cell_iter, ++loop_it)
643 {
644 unsigned node_idx = node_indices_in_cell_order[loop_it];
645 vtk_cell_data[node_idx] = p_cell_writer->GetVectorCellDataForVtkOutput(*cell_iter, this);
646 }
647
648 mesh_writer.AddPointData(p_cell_writer->GetVtkVectorCellDataName(), vtk_cell_data);
649 }
650 }
651
652 // Process rank and cell data can be collected on a cell-by-cell basis, and both occur in the following loop
653 // We assume the first cell is representative of all cells
654 if (this->Begin() != this->End()) // some processes may own no cells, so can't do this->Begin()->GetCellData()
655 {
656 auto num_cell_data_items = this->Begin()->GetCellData()->GetNumItems();
657 std::vector<std::string> cell_data_names = this->Begin()->GetCellData()->GetKeys();
658 std::vector<std::vector<double>> cell_data(num_cell_data_items, std::vector<double>(num_nodes));
659
660 std::vector<double> rank(num_nodes);
661
662 unsigned loop_it = 0;
663 for (auto cell_iter = this->Begin(); cell_iter != this->End(); ++cell_iter, ++loop_it)
664 {
665 unsigned node_idx = node_indices_in_cell_order[loop_it];
666
667 for (unsigned cell_data_idx = 0; cell_data_idx < num_cell_data_items; ++cell_data_idx)
668 {
669 cell_data[cell_data_idx][node_idx] = cell_iter->GetCellData()->GetItem(cell_data_names[cell_data_idx]);
670 }
671
672 rank[node_idx] = (PetscTools::GetMyRank());
673 }
674
675 // Add point data to writers
676 mesh_writer.AddPointData("Process rank", rank);
677 for (unsigned cell_data_idx = 0; cell_data_idx < num_cell_data_items; ++cell_data_idx)
678 {
679 mesh_writer.AddPointData(cell_data_names[cell_data_idx], cell_data[cell_data_idx]);
680 }
681 }
682
683 mesh_writer.WriteFilesUsingMesh(*mpNodesOnlyMesh);
684
685 *(this->mpVtkMetaFile) << " <DataSet timestep=\"";
686 *(this->mpVtkMetaFile) << SimulationTime::Instance()->GetTimeStepsElapsed();
687 *(this->mpVtkMetaFile) << R"(" group="" part="0" file="results_)";
688 *(this->mpVtkMetaFile) << SimulationTime::Instance()->GetTimeStepsElapsed();
690 {
691 *(this->mpVtkMetaFile) << ".vtu\"/>\n";
692 }
693 else
694 {
695 // Parallel vtu files .vtu -> .pvtu
696 *(this->mpVtkMetaFile) << ".pvtu\"/>\n";
697 }
698#endif //CHASTE_VTK
699}
700
701template<unsigned DIM>
702CellPtr NodeBasedCellPopulation<DIM>::AddCell(CellPtr pNewCell, CellPtr pParentCell)
703{
704 assert(pNewCell);
705
706 // Add new cell to population
707 CellPtr p_created_cell = AbstractCentreBasedCellPopulation<DIM>::AddCell(pNewCell, pParentCell);
708 assert(p_created_cell == pNewCell);
709
710 // Then set the new cell radius in the NodesOnlyMesh
711 unsigned parent_node_index = this->GetLocationIndexUsingCell(pParentCell);
712 Node<DIM>* p_parent_node = this->GetNode(parent_node_index);
713
714 unsigned new_node_index = this->GetLocationIndexUsingCell(p_created_cell);
715 Node<DIM>* p_new_node = this->GetNode(new_node_index);
716
717 p_new_node->SetRadius(p_parent_node->GetRadius());
718
719 // Return pointer to new cell
720 return p_created_cell;
721}
722
723template<unsigned DIM>
724void NodeBasedCellPopulation<DIM>::AddMovedCell(CellPtr pCell, boost::shared_ptr<Node<DIM> > pNode)
725{
726 // Create a new node
727 mpNodesOnlyMesh->AddMovedNode(pNode);
728
729 // Update cells vector
730 this->mCells.push_back(pCell);
731
732 // Update mappings between cells and location indices
733 this->AddCellUsingLocationIndex(pNode->GetIndex(), pCell);
734}
735
736template<unsigned DIM>
738{
739 mpNodesOnlyMesh->DeleteMovedNode(index);
740
741 // Update vector of cells
742 for (std::list<CellPtr>::iterator cell_iter = this->mCells.begin();
743 cell_iter != this->mCells.end();
744 ++cell_iter)
745 {
746 if (this->GetLocationIndexUsingCell(*cell_iter) == index)
747 {
748 // Update mappings between cells and location indices
749 this->RemoveCellUsingLocationIndex(index, (*cell_iter));
750 cell_iter = this->mCells.erase(cell_iter);
751
752 break;
753 }
754 }
755}
756
757template<unsigned DIM>
759{
760 MPI_Status status;
761
763 {
764 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter());
765 mpCellsRecvRight = mRightCommunicator.SendRecvObject(p_cells_right, PetscTools::GetMyRank() + 1, mCellCommunicationTag, PetscTools::GetMyRank() + 1, mCellCommunicationTag, status);
766 }
768 {
769 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter());
770 mpCellsRecvLeft = mLeftCommunicator.SendRecvObject(p_cells_left, PetscTools::GetMyRank() - 1, mCellCommunicationTag, PetscTools::GetMyRank() - 1, mCellCommunicationTag, status);
771 }
772 else if ( mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
773 {
774 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter());
775 mpCellsRecvLeft = mLeftCommunicator.SendRecvObject(p_cells_left, PetscTools::GetNumProcs() - 1, mCellCommunicationTag, PetscTools::GetNumProcs() - 1, mCellCommunicationTag, status);
776}
777
778 // We need to leave this to the end (rather than as an else if for AmTopMost())
779 // otherwise there will be a cyclic send-receive and it will stall
780 if ( PetscTools::AmTopMost() && mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
781 {
782 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter());
783 mpCellsRecvRight = mRightCommunicator.SendRecvObject(p_cells_right, 0, mCellCommunicationTag, 0, mCellCommunicationTag, status);
784 }
785}
786
787// helper function for NonBlockingSendCellsToNeighbourProcess() to calculate the tag
788template<unsigned DIM>
789unsigned NodeBasedCellPopulation<DIM>::CalculateMessageTag(unsigned senderI, unsigned receiverJ)
790{
791 /* Old function was overloading integer type for nProcs > 12
792 unsigned tag = SmallPow(2u, 1+ PetscTools::GetMyRank() ) * SmallPow (3u, 1 + PetscTools::GetMyRank() + 1);
793 Instead we use a Cantor pairing function which produces lower paired values
794 See: https://en.wikipedia.org/wiki/Pairing_function */
795 unsigned tag = 0.5*((senderI+receiverJ)*(senderI+receiverJ+1) + 2*receiverJ);
796 assert(tag < UINT_MAX); //Just make sure doesnt hit UINT_MAX as old method did.
797 return tag;
798}
799
800template<unsigned DIM>
802{
804 {
805 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter());
806 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank(), PetscTools::GetMyRank()+1 );
807 mRightCommunicator.ISendObject(p_cells_right, PetscTools::GetMyRank() + 1, tag);
808 }
810 {
811 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank(), PetscTools::GetMyRank()-1 );
812 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter());
813 mLeftCommunicator.ISendObject(p_cells_left, PetscTools::GetMyRank() - 1, tag);
814 }
815 else if ( mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
816 {
817 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank(), PetscTools::GetNumProcs()-1 );
818 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter());
819 mLeftCommunicator.ISendObject(p_cells_left, PetscTools::GetNumProcs()-1, tag);
820 }
821 if ( PetscTools::AmTopMost() && mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
822 {
823 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank(), 0 );
824 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter());
825 mRightCommunicator.ISendObject(p_cells_right, 0, tag);
826 }
827
828 // Now post receives to start receiving data before returning.
830 {
831 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank()+1, PetscTools::GetMyRank() );
832 mRightCommunicator.IRecvObject(PetscTools::GetMyRank() + 1, tag);
833 }
835 {
836 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank()-1, PetscTools::GetMyRank() );
837 mLeftCommunicator.IRecvObject(PetscTools::GetMyRank() - 1, tag);
838 }
839 else if ( mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
840 {
841 unsigned tag = CalculateMessageTag( PetscTools::GetNumProcs()-1, PetscTools::GetMyRank() );
842 mLeftCommunicator.IRecvObject(PetscTools::GetNumProcs() - 1, tag);
843}
844 if ( PetscTools::AmTopMost() && mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
845 {
846 unsigned tag = CalculateMessageTag( 0, PetscTools::GetMyRank() );
847 mRightCommunicator.IRecvObject(0, tag);
848}
849}
850
851template<unsigned DIM>
853{
855 {
856 mpCellsRecvRight = mRightCommunicator.GetRecvObject();
857 }
858 if (!PetscTools::AmMaster() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
859 {
860 mpCellsRecvLeft = mLeftCommunicator.GetRecvObject();
861 }
862 // Periodicity across processors has to be in this set order
863 if ( PetscTools::AmTopMost() && mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
864 {
865 mpCellsRecvRight = mRightCommunicator.GetRecvObject();
866}
867}
868
869template<unsigned DIM>
870std::pair<CellPtr, Node<DIM>* > NodeBasedCellPopulation<DIM>::GetCellNodePair(unsigned nodeIndex)
871{
872 Node<DIM>* p_node = this->GetNode(nodeIndex);
873
874 CellPtr p_cell = this->GetCellUsingLocationIndex(nodeIndex);
875
876 std::pair<CellPtr, Node<DIM>* > new_pair(p_cell, p_node);
877
878 return new_pair;
879}
880
881template<unsigned DIM>
883{
884 std::pair<CellPtr, Node<DIM>* > pair = GetCellNodePair(nodeIndex);
885
886 mCellsToSendRight.push_back(pair);
887}
888
889template<unsigned DIM>
891{
892 std::pair<CellPtr, Node<DIM>* > pair = GetCellNodePair(nodeIndex);
893
894 mCellsToSendLeft.push_back(pair);
895}
896
897template<unsigned DIM>
899{
900 if (!PetscTools::AmMaster() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
901 {
902 for (typename std::vector<std::pair<CellPtr, Node<DIM>* > >::iterator iter = mpCellsRecvLeft->begin();
903 iter != mpCellsRecvLeft->end();
904 ++iter)
905 {
906 // Make a shared pointer to the node to make sure it is correctly deleted.
907 boost::shared_ptr<Node<DIM> > p_node(iter->second);
908 AddMovedCell(iter->first, p_node);
909 }
910 }
911 if (!PetscTools::AmTopMost() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection())
912 {
913 for (typename std::vector<std::pair<CellPtr, Node<DIM>* > >::iterator iter = mpCellsRecvRight->begin();
914 iter != mpCellsRecvRight->end();
915 ++iter)
916 {
917 boost::shared_ptr<Node<DIM> > p_node(iter->second);
918 AddMovedCell(iter->first, p_node);
919 }
920 }
921}
922
923template<unsigned DIM>
925{
926 mpNodesOnlyMesh->ResizeBoxCollection();
927
928 mpNodesOnlyMesh->CalculateNodesOutsideLocalDomain();
929
930 std::vector<unsigned> nodes_to_send_right = mpNodesOnlyMesh->rGetNodesToSendRight();
931 AddCellsToSendRight(nodes_to_send_right);
932
933 std::vector<unsigned> nodes_to_send_left = mpNodesOnlyMesh->rGetNodesToSendLeft();
934 AddCellsToSendLeft(nodes_to_send_left);
935
936 // Post non-blocking send / receives so communication on both sides can start.
937 SendCellsToNeighbourProcesses();
938
939 // Post blocking receive calls that wait until communication complete.
940 //GetReceivedCells();
941
942 for (std::vector<unsigned>::iterator iter = nodes_to_send_right.begin();
943 iter != nodes_to_send_right.end();
944 ++iter)
945 {
946 DeleteMovedCell(*iter);
947 }
948
949 for (std::vector<unsigned>::iterator iter = nodes_to_send_left.begin();
950 iter != nodes_to_send_left.end();
951 ++iter)
952 {
953 DeleteMovedCell(*iter);
954 }
955
956 AddReceivedCells();
957
958 NodeMap map(1 + mpNodesOnlyMesh->GetMaximumNodeIndex());
959 mpNodesOnlyMesh->ReMesh(map);
960 UpdateMapsAfterRemesh(map);
961}
962
963template<unsigned DIM>
965{
966 mpNodesOnlyMesh->ClearHaloNodes();
967
968 mHaloCells.clear();
969 mHaloCellLocationMap.clear();
970 mLocationHaloCellMap.clear();
971
972 std::vector<unsigned> halos_to_send_right = mpNodesOnlyMesh->rGetHaloNodesToSendRight();
973 AddCellsToSendRight(halos_to_send_right);
974
975 std::vector<unsigned> halos_to_send_left = mpNodesOnlyMesh->rGetHaloNodesToSendLeft();
976 AddCellsToSendLeft(halos_to_send_left);
977
978 NonBlockingSendCellsToNeighbourProcesses();
979}
980
981template<unsigned DIM>
982void NodeBasedCellPopulation<DIM>::AddCellsToSendRight(std::vector<unsigned>& cellLocationIndices)
983{
984 mCellsToSendRight.clear();
985
986 for (unsigned i=0; i < cellLocationIndices.size(); i++)
987 {
988 AddNodeAndCellToSendRight(cellLocationIndices[i]);
989 }
990}
991
992template<unsigned DIM>
993void NodeBasedCellPopulation<DIM>::AddCellsToSendLeft(std::vector<unsigned>& cellLocationIndices)
994{
995 mCellsToSendLeft.clear();
996
997 for (unsigned i=0; i < cellLocationIndices.size(); i++)
998 {
999 AddNodeAndCellToSendLeft(cellLocationIndices[i]);
1000 }
1001}
1002
1003template<unsigned DIM>
1005{
1006 GetReceivedCells();
1007
1008 if (!PetscTools::AmMaster() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection())
1009 {
1010 for (typename std::vector<std::pair<CellPtr, Node<DIM>* > >::iterator iter = mpCellsRecvLeft->begin();
1011 iter != mpCellsRecvLeft->end();
1012 ++iter)
1013 {
1014 boost::shared_ptr<Node<DIM> > p_node(iter->second);
1015 AddHaloCell(iter->first, p_node);
1016
1017 }
1018 }
1019 if (!PetscTools::AmTopMost() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection())
1020 {
1021 for (typename std::vector<std::pair<CellPtr, Node<DIM>* > >::iterator iter = mpCellsRecvRight->begin();
1022 iter != mpCellsRecvRight->end();
1023 ++iter)
1024 {
1025 boost::shared_ptr<Node<DIM> > p_node(iter->second);
1026 AddHaloCell(iter->first, p_node);
1027 }
1028 }
1029
1030 mpNodesOnlyMesh->AddHaloNodesToBoxes();
1031}
1032
1033template<unsigned DIM>
1034void NodeBasedCellPopulation<DIM>::AddHaloCell(CellPtr pCell, boost::shared_ptr<Node<DIM> > pNode)
1035{
1036 mHaloCells.push_back(pCell);
1037
1038 mpNodesOnlyMesh->AddHaloNode(pNode);
1039
1040 mHaloCellLocationMap[pCell] = pNode->GetIndex();
1041
1042 mLocationHaloCellMap[pNode->GetIndex()] = pCell;
1043}
1044
1045// Explicit instantiation
1046template class NodeBasedCellPopulation<1>;
1047template class NodeBasedCellPopulation<2>;
1048template class NodeBasedCellPopulation<3>;
1049
1050// Serialization for Boost >= 1.36
#define EXCEPTION(message)
#define NEVER_REACHED
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
AbstractMesh< ELEMENT_DIM, SPACE_DIM > & mrMesh
virtual CellPtr GetCellUsingLocationIndex(unsigned index)
c_vector< double, SPACE_DIM > GetSizeOfCellPopulation()
virtual void OutputCellPopulationParameters(out_stream &rParamsFile)
CellPtr AddCell(CellPtr pNewCell, CellPtr pParentCell=CellPtr())
c_vector< double, DIM > GetSizeOfCellPopulation()
unsigned CalculateMessageTag(unsigned senderI, unsigned receiverJ)
void SetNode(unsigned nodeIndex, ChastePoint< DIM > &rNewLocation)
void AddCellsToSendRight(std::vector< unsigned > &cellLocationIndices)
void AddHaloCell(CellPtr pCell, boost::shared_ptr< Node< DIM > > pNode)
Node< DIM > * GetNode(unsigned index)
virtual void AcceptPopulationCountWriter(boost::shared_ptr< AbstractCellPopulationCountWriter< DIM, DIM > > pPopulationCountWriter)
void OutputCellPopulationParameters(out_stream &rParamsFile)
void AddNodeAndCellToSendLeft(unsigned nodeIndex)
virtual CellPtr GetCellUsingLocationIndex(unsigned index)
NodesOnlyMesh< DIM > & rGetMesh()
NodeBasedCellPopulation(NodesOnlyMesh< DIM > &rMesh, std::vector< CellPtr > &rCells, const std::vector< unsigned > locationIndices=std::vector< unsigned >(), bool deleteMesh=false, bool validate=true)
void SetUseVariableRadii(bool useVariableRadii=true)
std::pair< CellPtr, Node< DIM > * > GetCellNodePair(unsigned nodeIndex)
void SetLoadBalanceMesh(bool loadBalanceMesh)
virtual void WriteVtkResultsToFile(const std::string &rDirectory)
void AddCellsToSendLeft(std::vector< unsigned > &cellLocationIndices)
virtual void AcceptCellWriter(boost::shared_ptr< AbstractCellWriter< DIM, DIM > > pCellWriter, CellPtr pCell)
unsigned AddNode(Node< DIM > *pNewNode)
std::set< unsigned > GetNodesWithinNeighbourhoodRadius(unsigned index, double neighbourhoodRadius)
double GetWidth(const unsigned &rDimension)
virtual TetrahedralMesh< DIM, DIM > * GetTetrahedralMeshForPdeModifier()
NodesOnlyMesh< DIM > * mpNodesOnlyMesh
void SetLoadBalanceFrequency(unsigned loadBalanceFrequency)
double GetVolumeOfCell(CellPtr pCell)
void Update(bool hasHadBirthsOrDeaths=true)
virtual CellPtr AddCell(CellPtr pNewCell, CellPtr pParentCell)
void AddNodeAndCellToSendRight(unsigned nodeIndex)
void AddMovedCell(CellPtr pCell, boost::shared_ptr< Node< DIM > > pNode)
std::set< unsigned > GetNeighbouringNodeIndices(unsigned index)
virtual void UpdateParticlesAfterReMesh(NodeMap &rMap)
virtual void AcceptPopulationEventWriter(boost::shared_ptr< AbstractCellPopulationEventWriter< DIM, DIM > > pPopulationEventWriter)
virtual void AcceptPopulationWriter(boost::shared_ptr< AbstractCellPopulationWriter< DIM, DIM > > pPopulationWriter)
unsigned GetNewIndex(unsigned oldIndex) const
Definition NodeMap.cpp:87
bool IsIdentityMap()
Definition NodeMap.cpp:100
bool IsDeleted(unsigned index)
Definition NodeMap.cpp:82
Definition Node.hpp:59
void SetRadius(double radius)
Definition Node.cpp:256
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition Node.cpp:139
bool GetNeighboursSetUp()
Definition Node.cpp:368
std::vector< unsigned > & rGetNeighbours()
Definition Node.cpp:376
double GetRadius()
Definition Node.cpp:248
static MPI_Comm GetWorld()
static bool AmMaster()
static bool AmTopMost()
static void Barrier(const std::string callerId="")
static bool IsSequential()
static unsigned GetMyRank()
static unsigned GetNumProcs()
static SimulationTime * Instance()
unsigned GetTimeStepsElapsed() const
void AddPointData(std::string name, std::vector< double > data)
void SetParallelFiles(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
void WriteFilesUsingMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, bool keepOriginalElementIndexing=true)