Chaste Commit::29c3c4ab2bee00ddd311a035a1e522c03cbe928a
NodeBasedCellPopulation.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 "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 // Store the present time as a string
592 std::stringstream time;
594
595 // Make sure the nodes are ordered contiguously in memory
596 NodeMap map(1 + this->mpNodesOnlyMesh->GetMaximumNodeIndex());
597 this->mpNodesOnlyMesh->ReMesh(map);
598
599 // Create mesh writer for VTK output
600 VtkMeshWriter<DIM, DIM> mesh_writer(rDirectory, "results_"+time.str(), false);
601 mesh_writer.SetParallelFiles(*mpNodesOnlyMesh);
602
603 auto num_nodes = GetNumNodes();
604
605 // For each cell that this process owns, find the corresponding node index, which we only want to calculate once
606 std::vector<unsigned> node_indices_in_cell_order;
607 for (auto cell_iter = this->Begin(); cell_iter != this->End(); ++cell_iter)
608 {
609 // Get the node index corresponding to this cell
610 unsigned global_index = this->GetLocationIndexUsingCell(*cell_iter);
611 unsigned node_index = this->rGetMesh().SolveNodeMapping(global_index);
612
613 node_indices_in_cell_order.emplace_back(node_index);
614 }
615
616 // Iterate over any cell writers that are present. This is in a separate loop to below, because the writer loop
617 // needs to the the outer loop.
618 for (auto&& p_cell_writer : this->mCellWriters)
619 {
620 // Add any scalar data
621 if (p_cell_writer->GetOutputScalarData())
622 {
623 std::vector<double> vtk_cell_data(num_nodes);
624
625 unsigned loop_it = 0;
626 for (auto cell_iter = this->Begin(); cell_iter != this->End(); ++cell_iter, ++loop_it)
627 {
628 unsigned node_idx = node_indices_in_cell_order[loop_it];
629 vtk_cell_data[node_idx] = p_cell_writer->GetCellDataForVtkOutput(*cell_iter, this);
630 }
631
632 mesh_writer.AddPointData(p_cell_writer->GetVtkCellDataName(), vtk_cell_data);
633 }
634
635 // Add any vector data
636 if (p_cell_writer->GetOutputVectorData())
637 {
638 std::vector<c_vector<double, DIM>> vtk_cell_data(num_nodes);
639
640 unsigned loop_it = 0;
641 for (auto cell_iter = this->Begin(); cell_iter != this->End(); ++cell_iter, ++loop_it)
642 {
643 unsigned node_idx = node_indices_in_cell_order[loop_it];
644 vtk_cell_data[node_idx] = p_cell_writer->GetVectorCellDataForVtkOutput(*cell_iter, this);
645 }
646
647 mesh_writer.AddPointData(p_cell_writer->GetVtkVectorCellDataName(), vtk_cell_data);
648 }
649 }
650
651 // Process rank and cell data can be collected on a cell-by-cell basis, and both occur in the following loop
652 // We assume the first cell is representative of all cells
653 if (this->Begin() != this->End()) // some processes may own no cells, so can't do this->Begin()->GetCellData()
654 {
655 auto num_cell_data_items = this->Begin()->GetCellData()->GetNumItems();
656 std::vector<std::string> cell_data_names = this->Begin()->GetCellData()->GetKeys();
657 std::vector<std::vector<double>> cell_data(num_cell_data_items, std::vector<double>(num_nodes));
658
659 std::vector<double> rank(num_nodes);
660
661 unsigned loop_it = 0;
662 for (auto cell_iter = this->Begin(); cell_iter != this->End(); ++cell_iter, ++loop_it)
663 {
664 unsigned node_idx = node_indices_in_cell_order[loop_it];
665
666 for (unsigned cell_data_idx = 0; cell_data_idx < num_cell_data_items; ++cell_data_idx)
667 {
668 cell_data[cell_data_idx][node_idx] = cell_iter->GetCellData()->GetItem(cell_data_names[cell_data_idx]);
669 }
670
671 rank[node_idx] = (PetscTools::GetMyRank());
672 }
673
674 // Add point data to writers
675 mesh_writer.AddPointData("Process rank", rank);
676 for (unsigned cell_data_idx = 0; cell_data_idx < num_cell_data_items; ++cell_data_idx)
677 {
678 mesh_writer.AddPointData(cell_data_names[cell_data_idx], cell_data[cell_data_idx]);
679 }
680 }
681
682 mesh_writer.WriteFilesUsingMesh(*mpNodesOnlyMesh);
683
684 *(this->mpVtkMetaFile) << " <DataSet timestep=\"";
685 *(this->mpVtkMetaFile) << SimulationTime::Instance()->GetTimeStepsElapsed();
686 *(this->mpVtkMetaFile) << R"(" group="" part="0" file="results_)";
687 *(this->mpVtkMetaFile) << SimulationTime::Instance()->GetTimeStepsElapsed();
689 {
690 *(this->mpVtkMetaFile) << ".vtu\"/>\n";
691 }
692 else
693 {
694 // Parallel vtu files .vtu -> .pvtu
695 *(this->mpVtkMetaFile) << ".pvtu\"/>\n";
696 }
697}
698
699template<unsigned DIM>
700CellPtr NodeBasedCellPopulation<DIM>::AddCell(CellPtr pNewCell, CellPtr pParentCell)
701{
702 assert(pNewCell);
703
704 // Add new cell to population
705 CellPtr p_created_cell = AbstractCentreBasedCellPopulation<DIM>::AddCell(pNewCell, pParentCell);
706 assert(p_created_cell == pNewCell);
707
708 // Then set the new cell radius in the NodesOnlyMesh
709 unsigned parent_node_index = this->GetLocationIndexUsingCell(pParentCell);
710 Node<DIM>* p_parent_node = this->GetNode(parent_node_index);
711
712 unsigned new_node_index = this->GetLocationIndexUsingCell(p_created_cell);
713 Node<DIM>* p_new_node = this->GetNode(new_node_index);
714
715 p_new_node->SetRadius(p_parent_node->GetRadius());
716
717 // Return pointer to new cell
718 return p_created_cell;
719}
720
721template<unsigned DIM>
722void NodeBasedCellPopulation<DIM>::AddMovedCell(CellPtr pCell, boost::shared_ptr<Node<DIM> > pNode)
723{
724 // Create a new node
725 mpNodesOnlyMesh->AddMovedNode(pNode);
726
727 // Update cells vector
728 this->mCells.push_back(pCell);
729
730 // Update mappings between cells and location indices
731 this->AddCellUsingLocationIndex(pNode->GetIndex(), pCell);
732}
733
734template<unsigned DIM>
736{
737 mpNodesOnlyMesh->DeleteMovedNode(index);
738
739 // Update vector of cells
740 for (std::list<CellPtr>::iterator cell_iter = this->mCells.begin();
741 cell_iter != this->mCells.end();
742 ++cell_iter)
743 {
744 if (this->GetLocationIndexUsingCell(*cell_iter) == index)
745 {
746 // Update mappings between cells and location indices
747 this->RemoveCellUsingLocationIndex(index, (*cell_iter));
748 cell_iter = this->mCells.erase(cell_iter);
749
750 break;
751 }
752 }
753}
754
755template<unsigned DIM>
757{
758 MPI_Status status;
759
761 {
762 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter());
763 mpCellsRecvRight = mRightCommunicator.SendRecvObject(p_cells_right, PetscTools::GetMyRank() + 1, mCellCommunicationTag, PetscTools::GetMyRank() + 1, mCellCommunicationTag, status);
764 }
766 {
767 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter());
768 mpCellsRecvLeft = mLeftCommunicator.SendRecvObject(p_cells_left, PetscTools::GetMyRank() - 1, mCellCommunicationTag, PetscTools::GetMyRank() - 1, mCellCommunicationTag, status);
769 }
770 else if ( mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
771 {
772 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter());
773 mpCellsRecvLeft = mLeftCommunicator.SendRecvObject(p_cells_left, PetscTools::GetNumProcs() - 1, mCellCommunicationTag, PetscTools::GetNumProcs() - 1, mCellCommunicationTag, status);
774}
775
776 // We need to leave this to the end (rather than as an else if for AmTopMost())
777 // otherwise there will be a cyclic send-receive and it will stall
778 if ( PetscTools::AmTopMost() && mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
779 {
780 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter());
781 mpCellsRecvRight = mRightCommunicator.SendRecvObject(p_cells_right, 0, mCellCommunicationTag, 0, mCellCommunicationTag, status);
782 }
783}
784
785// helper function for NonBlockingSendCellsToNeighbourProcess() to calculate the tag
786template<unsigned DIM>
787unsigned NodeBasedCellPopulation<DIM>::CalculateMessageTag(unsigned senderI, unsigned receiverJ)
788{
789 /* Old function was overloading integer type for nProcs > 12
790 unsigned tag = SmallPow(2u, 1+ PetscTools::GetMyRank() ) * SmallPow (3u, 1 + PetscTools::GetMyRank() + 1);
791 Instead we use a Cantor pairing function which produces lower paired values
792 See: https://en.wikipedia.org/wiki/Pairing_function */
793 unsigned tag = 0.5*((senderI+receiverJ)*(senderI+receiverJ+1) + 2*receiverJ);
794 assert(tag < UINT_MAX); //Just make sure doesnt hit UINT_MAX as old method did.
795 return tag;
796}
797
798template<unsigned DIM>
800{
802 {
803 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter());
804 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank(), PetscTools::GetMyRank()+1 );
805 mRightCommunicator.ISendObject(p_cells_right, PetscTools::GetMyRank() + 1, tag);
806 }
808 {
809 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank(), PetscTools::GetMyRank()-1 );
810 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter());
811 mLeftCommunicator.ISendObject(p_cells_left, PetscTools::GetMyRank() - 1, tag);
812 }
813 else if ( mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
814 {
815 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank(), PetscTools::GetNumProcs()-1 );
816 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter());
817 mLeftCommunicator.ISendObject(p_cells_left, PetscTools::GetNumProcs()-1, tag);
818 }
819 if ( PetscTools::AmTopMost() && mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
820 {
821 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank(), 0 );
822 boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter());
823 mRightCommunicator.ISendObject(p_cells_right, 0, tag);
824 }
825
826 // Now post receives to start receiving data before returning.
828 {
829 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank()+1, PetscTools::GetMyRank() );
830 mRightCommunicator.IRecvObject(PetscTools::GetMyRank() + 1, tag);
831 }
833 {
834 unsigned tag = CalculateMessageTag( PetscTools::GetMyRank()-1, PetscTools::GetMyRank() );
835 mLeftCommunicator.IRecvObject(PetscTools::GetMyRank() - 1, tag);
836 }
837 else if ( mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
838 {
839 unsigned tag = CalculateMessageTag( PetscTools::GetNumProcs()-1, PetscTools::GetMyRank() );
840 mLeftCommunicator.IRecvObject(PetscTools::GetNumProcs() - 1, tag);
841}
842 if ( PetscTools::AmTopMost() && mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
843 {
844 unsigned tag = CalculateMessageTag( 0, PetscTools::GetMyRank() );
845 mRightCommunicator.IRecvObject(0, tag);
846}
847}
848
849template<unsigned DIM>
851{
853 {
854 mpCellsRecvRight = mRightCommunicator.GetRecvObject();
855 }
856 if (!PetscTools::AmMaster() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
857 {
858 mpCellsRecvLeft = mLeftCommunicator.GetRecvObject();
859 }
860 // Periodicity across processors has to be in this set order
861 if ( PetscTools::AmTopMost() && mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
862 {
863 mpCellsRecvRight = mRightCommunicator.GetRecvObject();
864}
865}
866
867template<unsigned DIM>
868std::pair<CellPtr, Node<DIM>* > NodeBasedCellPopulation<DIM>::GetCellNodePair(unsigned nodeIndex)
869{
870 Node<DIM>* p_node = this->GetNode(nodeIndex);
871
872 CellPtr p_cell = this->GetCellUsingLocationIndex(nodeIndex);
873
874 std::pair<CellPtr, Node<DIM>* > new_pair(p_cell, p_node);
875
876 return new_pair;
877}
878
879template<unsigned DIM>
881{
882 std::pair<CellPtr, Node<DIM>* > pair = GetCellNodePair(nodeIndex);
883
884 mCellsToSendRight.push_back(pair);
885}
886
887template<unsigned DIM>
889{
890 std::pair<CellPtr, Node<DIM>* > pair = GetCellNodePair(nodeIndex);
891
892 mCellsToSendLeft.push_back(pair);
893}
894
895template<unsigned DIM>
897{
898 if (!PetscTools::AmMaster() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection() )
899 {
900 for (typename std::vector<std::pair<CellPtr, Node<DIM>* > >::iterator iter = mpCellsRecvLeft->begin();
901 iter != mpCellsRecvLeft->end();
902 ++iter)
903 {
904 // Make a shared pointer to the node to make sure it is correctly deleted.
905 boost::shared_ptr<Node<DIM> > p_node(iter->second);
906 AddMovedCell(iter->first, p_node);
907 }
908 }
909 if (!PetscTools::AmTopMost() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection())
910 {
911 for (typename std::vector<std::pair<CellPtr, Node<DIM>* > >::iterator iter = mpCellsRecvRight->begin();
912 iter != mpCellsRecvRight->end();
913 ++iter)
914 {
915 boost::shared_ptr<Node<DIM> > p_node(iter->second);
916 AddMovedCell(iter->first, p_node);
917 }
918 }
919}
920
921template<unsigned DIM>
923{
924 mpNodesOnlyMesh->ResizeBoxCollection();
925
926 mpNodesOnlyMesh->CalculateNodesOutsideLocalDomain();
927
928 std::vector<unsigned> nodes_to_send_right = mpNodesOnlyMesh->rGetNodesToSendRight();
929 AddCellsToSendRight(nodes_to_send_right);
930
931 std::vector<unsigned> nodes_to_send_left = mpNodesOnlyMesh->rGetNodesToSendLeft();
932 AddCellsToSendLeft(nodes_to_send_left);
933
934 // Post non-blocking send / receives so communication on both sides can start.
935 SendCellsToNeighbourProcesses();
936
937 // Post blocking receive calls that wait until communication complete.
938 //GetReceivedCells();
939
940 for (std::vector<unsigned>::iterator iter = nodes_to_send_right.begin();
941 iter != nodes_to_send_right.end();
942 ++iter)
943 {
944 DeleteMovedCell(*iter);
945 }
946
947 for (std::vector<unsigned>::iterator iter = nodes_to_send_left.begin();
948 iter != nodes_to_send_left.end();
949 ++iter)
950 {
951 DeleteMovedCell(*iter);
952 }
953
954 AddReceivedCells();
955
956 NodeMap map(1 + mpNodesOnlyMesh->GetMaximumNodeIndex());
957 mpNodesOnlyMesh->ReMesh(map);
958 UpdateMapsAfterRemesh(map);
959}
960
961template<unsigned DIM>
963{
964 mpNodesOnlyMesh->ClearHaloNodes();
965
966 mHaloCells.clear();
967 mHaloCellLocationMap.clear();
968 mLocationHaloCellMap.clear();
969
970 std::vector<unsigned> halos_to_send_right = mpNodesOnlyMesh->rGetHaloNodesToSendRight();
971 AddCellsToSendRight(halos_to_send_right);
972
973 std::vector<unsigned> halos_to_send_left = mpNodesOnlyMesh->rGetHaloNodesToSendLeft();
974 AddCellsToSendLeft(halos_to_send_left);
975
976 NonBlockingSendCellsToNeighbourProcesses();
977}
978
979template<unsigned DIM>
980void NodeBasedCellPopulation<DIM>::AddCellsToSendRight(std::vector<unsigned>& cellLocationIndices)
981{
982 mCellsToSendRight.clear();
983
984 for (unsigned i=0; i < cellLocationIndices.size(); i++)
985 {
986 AddNodeAndCellToSendRight(cellLocationIndices[i]);
987 }
988}
989
990template<unsigned DIM>
991void NodeBasedCellPopulation<DIM>::AddCellsToSendLeft(std::vector<unsigned>& cellLocationIndices)
992{
993 mCellsToSendLeft.clear();
994
995 for (unsigned i=0; i < cellLocationIndices.size(); i++)
996 {
997 AddNodeAndCellToSendLeft(cellLocationIndices[i]);
998 }
999}
1000
1001template<unsigned DIM>
1003{
1004 GetReceivedCells();
1005
1006 if (!PetscTools::AmMaster() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection())
1007 {
1008 for (typename std::vector<std::pair<CellPtr, Node<DIM>* > >::iterator iter = mpCellsRecvLeft->begin();
1009 iter != mpCellsRecvLeft->end();
1010 ++iter)
1011 {
1012 boost::shared_ptr<Node<DIM> > p_node(iter->second);
1013 AddHaloCell(iter->first, p_node);
1014
1015 }
1016 }
1017 if (!PetscTools::AmTopMost() || mpNodesOnlyMesh->GetIsPeriodicAcrossProcsFromBoxCollection())
1018 {
1019 for (typename std::vector<std::pair<CellPtr, Node<DIM>* > >::iterator iter = mpCellsRecvRight->begin();
1020 iter != mpCellsRecvRight->end();
1021 ++iter)
1022 {
1023 boost::shared_ptr<Node<DIM> > p_node(iter->second);
1024 AddHaloCell(iter->first, p_node);
1025 }
1026 }
1027
1028 mpNodesOnlyMesh->AddHaloNodesToBoxes();
1029}
1030
1031template<unsigned DIM>
1032void NodeBasedCellPopulation<DIM>::AddHaloCell(CellPtr pCell, boost::shared_ptr<Node<DIM> > pNode)
1033{
1034 mHaloCells.push_back(pCell);
1035
1036 mpNodesOnlyMesh->AddHaloNode(pNode);
1037
1038 mHaloCellLocationMap[pCell] = pNode->GetIndex();
1039
1040 mLocationHaloCellMap[pNode->GetIndex()] = pCell;
1041}
1042
1043// Explicit instantiation
1044template class NodeBasedCellPopulation<1>;
1045template class NodeBasedCellPopulation<2>;
1046template class NodeBasedCellPopulation<3>;
1047
1048// 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)