Chaste Commit::baa90ac2819b962188b7562f2326be23c47859a7
VertexBasedPopulationSrn.cpp
1/*
2
3Copyright (c) 2005-2024, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "VertexBasedPopulationSrn.hpp"
37#include "CellSrnModel.hpp"
38
39template <unsigned DIM>
41 : mpCellPopulation(nullptr)
42{
43}
44
45template <unsigned DIM>
49
50template <unsigned DIM>
52{
53 mpCellPopulation = pCellPopulation;
54}
55
56template <unsigned DIM>
58{
59 // Get recorded edge operations
60 const std::vector<EdgeOperation>& r_operations = mpCellPopulation->rGetMesh().GetOperationRecorder()->GetEdgeOperations();
61 for (auto operation : r_operations)
62 {
63 /*
64 * An operation with deleted element may be recorded, e.g. following a
65 * T2 swap, neighbouring element may become triangular due to node
66 * merging and may subsequently be marked for deletion, despite being
67 * marked to perform edge operations (e.g. node merging).
68 */
69 switch (operation.GetOperation())
70 {
71 // All operations, except cell division, require the same information
72 case EDGE_OPERATION_ADD:
73 case EDGE_OPERATION_NODE_MERGE:
74 case EDGE_OPERATION_SPLIT:
75 case EDGE_OPERATION_MERGE:
76 {
77 const unsigned stored_index = operation.GetElementIndex();
78 unsigned location_index = stored_index;
79
80 /*
81 * If operation is recorded before element indices are changed.
82 * For example, if the operations recorded during T2 swap.
83 */
84 if (operation.IsElementIndexRemapped())
85 {
86 // If the element is deleted, then ignore this operation
87 if (rElementMap.IsDeleted(location_index))
88 {
89 // LCOV_EXCL_START
90 break; // This line is difficult to test
91 // LCOV_EXCL_STOP
92 }
93 else
94 {
95 location_index = rElementMap.GetNewIndex(stored_index);
96 }
97 }
98 // Get the necessary information to perform Remap
99 const EdgeRemapInfo& r_edge_change = operation.rGetRemapInfo();
100 CellPtr p_cell = mpCellPopulation->GetCellUsingLocationIndex(location_index);
101 auto p_old_model = static_cast<CellSrnModel*>(p_cell->GetSrnModel());
102 std::vector<AbstractSrnModelPtr> old_srn_edges = p_old_model->GetEdges();
103 RemapCellSrn(old_srn_edges, p_old_model, r_edge_change);
104 old_srn_edges.clear();
105 break;
106 }
107 case EDGE_OPERATION_DIVIDE:
108 {
109 const unsigned location_index_1 = rElementMap.GetNewIndex(operation.GetElementIndex());
110 const unsigned location_index_2 = rElementMap.GetNewIndex(operation.GetElementIndex2());
111
112 const EdgeRemapInfo& r_edge_change_1 = operation.rGetRemapInfo();
113 const EdgeRemapInfo& r_edge_change_2 = operation.rGetRemapInfo2();
114
115 CellPtr p_cell_1 = mpCellPopulation->GetCellUsingLocationIndex(location_index_1);
116 CellPtr p_cell_2 = mpCellPopulation->GetCellUsingLocationIndex(location_index_2);
117
118 auto p_old_model_1 = static_cast<CellSrnModel*>(p_cell_1->GetSrnModel());
119 std::vector<AbstractSrnModelPtr> parent_srn_edges = p_old_model_1->GetEdges();
120 auto p_old_model_2 = static_cast<CellSrnModel*>(p_cell_2->GetSrnModel());
121
122 RemapCellSrn(parent_srn_edges, p_old_model_1, r_edge_change_1);
123 RemapCellSrn(parent_srn_edges, p_old_model_2, r_edge_change_2);
124 parent_srn_edges.clear();
125 break;
126 }
127 }
128 }
129 mpCellPopulation->rGetMesh().GetOperationRecorder()->ClearEdgeOperations();
130}
131
132template <unsigned DIM>
133void VertexBasedPopulationSrn<DIM>::RemapCellSrn(std::vector<AbstractSrnModelPtr> parentSrnEdges,
134 CellSrnModel* pCellSrn,
135 const EdgeRemapInfo& rEdgeChange)
136{
137 auto edge_mapping = rEdgeChange.GetEdgesMapping();
138 std::vector<AbstractSrnModelPtr> new_edge_srn(edge_mapping.size());
139 const std::vector<double> split_proportions = rEdgeChange.GetSplitProportions();
140 const unsigned num_edges = edge_mapping.size();
141 std::vector<unsigned> shrunk_edges;
142
143 /*
144 * Go through the edges, check its status and the index corresponding to the
145 * edge status before rearrangement. Go through the SRN model.
146 */
147 for (unsigned i = 0; i < num_edges; i++)
148 {
149 // The remap_index, if +ve refers to the SRN index of the oldModel, if -ve then it's a new edge
150 const long remap_index = edge_mapping[i];
151
152 /*
153 * remap_status can be the following:
154 * 0 - Direct remapping, the edge SRN of the oldModel can be transferred
155 * directly to the new model
156 * 1 - The edge is a split point between the dividing cells
157 * 2 - This is a new edge i.e. the dividing line in the middle of the
158 * old and new cells
159 * 3 - Edge below or above the edge that was deleted due to node merging
160 * 4 - Edge above was merged into this edge
161 */
162 const unsigned remap_status = rEdgeChange.GetEdgesStatus()[i];
163
164 // Remap index cannot be negative when it's a direct remap or an edge split
165 assert(!((remap_status == 0 || remap_status == 1) && remap_index < 0));
166 switch(remap_status)
167 {
168 // Edge SRN remains the same
169 case 0:
170 {
171 new_edge_srn[i] = boost::shared_ptr<AbstractSrnModel>(parentSrnEdges[remap_index]->CreateSrnModel());
172 break;
173 }
174 case 1:
175 {
176 /*
177 * Edge split depends on the relative splitting node position
178 * because currently we assume that edge concentrations are
179 * uniform on the edge.
180 */
181 new_edge_srn[i] = boost::shared_ptr<AbstractSrnModel>(parentSrnEdges[remap_index]->CreateSrnModel());
182 assert(split_proportions[i] >= 0);
183 new_edge_srn[i]->SplitEdgeSrn(split_proportions[i]);
184 break;
185 }
186 case 2:
187 {
188 // The edge is new
189 new_edge_srn[i] = boost::shared_ptr<AbstractSrnModel>(parentSrnEdges[0]->CreateSrnModel());
190 new_edge_srn[i]->SetCell(pCellSrn->GetCell());
191 new_edge_srn[i]->InitialiseDaughterCell();
192 break;
193 }
194 case 3:
195 {
196 // If the edge above or below this edge was deleted
197 new_edge_srn[i] = boost::shared_ptr<AbstractSrnModel>(parentSrnEdges[remap_index]->CreateSrnModel());
198
199 const bool is_prev_edge = rEdgeChange.GetEdgesStatus()[(i+1)%num_edges]==3;
200
201 // Find the shrunk edge
202 unsigned shrunk_edge = 0;
203
204 // If this edge is below the shrunk edge
205 if (is_prev_edge)
206 {
207 shrunk_edge = (remap_index+1)%(num_edges+1);
208 shrunk_edges.push_back(shrunk_edge);
209 }
210 else
211 {
212 shrunk_edge = remap_index==0 ? num_edges : remap_index-1;
213 }
214 new_edge_srn[i]->AddShrunkEdgeSrn(parentSrnEdges[shrunk_edge].get());
215 break;
216 }
217 case 4:
218 {
219 // Add SRNs from the edge above to this edge
220 new_edge_srn[i] = boost::shared_ptr<AbstractSrnModel>(parentSrnEdges[remap_index]->CreateSrnModel());
221 const unsigned next_edge_index = (remap_index+1)%(num_edges+1);
222 new_edge_srn[i]->AddMergedEdgeSrn(parentSrnEdges[next_edge_index].get());
223 break;
224 }
225 }
226 // Setting the new local edge index and the cell
227 new_edge_srn[i]->SetEdgeLocalIndex(i);
228 new_edge_srn[i]->SetCell(pCellSrn->GetCell());
229 }
230
231 // For the case when edge quantities are returned into interior when edge shrinks due to node merging
232 auto p_interior_srn = boost::shared_ptr<AbstractSrnModel>(pCellSrn->GetInteriorSrn());
233 if (p_interior_srn != nullptr)
234 {
235 for (unsigned shrunk_edge_index : shrunk_edges)
236 {
237 p_interior_srn->AddShrunkEdgeToInterior(parentSrnEdges[shrunk_edge_index].get());
238 }
239 }
240
241 pCellSrn->AddEdgeSrn(new_edge_srn);
242 assert(num_edges == pCellSrn->GetNumEdgeSrn());
243}
244
245template class VertexBasedPopulationSrn<1>;
246template class VertexBasedPopulationSrn<2>;
247template class VertexBasedPopulationSrn<3>;
248
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
void AddEdgeSrn(std::vector< AbstractSrnModelPtr > edgeSrns)
unsigned GetNumEdgeSrn() const
const std::vector< AbstractSrnModelPtr > & GetEdges() const
AbstractSrnModelPtr GetInteriorSrn() const
std::vector< unsigned > GetEdgesStatus() const
std::vector< long > GetEdgesMapping() const
std::vector< double > GetSplitProportions() const
VertexMeshOperationRecorder< ELEMENT_DIM, SPACE_DIM > * GetOperationRecorder()
MutableVertexMesh< DIM, DIM > & rGetMesh()
void RemapCellSrn(std::vector< AbstractSrnModelPtr > parentSrnEdges, CellSrnModel *pCellSrn, const EdgeRemapInfo &rEdgeChange)
void UpdateSrnAfterBirthOrDeath(VertexElementMap &rElementMap)
void SetVertexCellPopulation(VertexBasedCellPopulation< DIM > *pCellPopulation)
unsigned GetNewIndex(unsigned oldIndex) const
bool IsDeleted(unsigned index)