Chaste Commit::f2ff7ee04e70ac9d06c57344df8d017dbb12b97b
VertexMeshOperationRecorder.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 "VertexMeshOperationRecorder.hpp"
37
38#include "EdgeRemapInfo.hpp"
39
40template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
45
46template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
50
51template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
53{
54 mpEdgeHelper = pEdgeHelper;
55}
56
57template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
59{
60 mT1Swaps.push_back(rSwapInfo);
61}
62
63template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
65{
66 return mT1Swaps;
67}
68
69template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
74
75template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
77{
78 mT2Swaps.push_back(rSwapInfo);
79}
80
81template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
83{
84 return mT2Swaps;
85}
86
87template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
92
93template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
95{
96 mT3Swaps.push_back(rSwapInfo);
97}
98
99template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
101{
102 return mT3Swaps;
103}
104
105template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
110
111template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
113{
114 mCellDivisions.push_back(rDivisionInfo);
115}
116
117template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
119{
120 return mCellDivisions;
121}
122
123template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
128
129template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
131{
132 return mEdgeOperations;
133}
134
135template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
140
141template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
144 const std::pair<unsigned, unsigned> merged_nodes_pair,
145 const bool elementIndexIsRemapped)
146{
147 const unsigned element_index = pElement->GetIndex();
148 const unsigned element_num_edges = pElement->GetNumEdges();
149 std::vector<long> edge_mapping(element_num_edges, -1);
150 std::vector<unsigned> edge_status(element_num_edges, 0);
151
152 // Marking unaffected edges
153 for (unsigned i = 0; i < oldIds.size(); ++i)
154 {
155 long index = pElement->GetLocalEdgeIndex((*mpEdgeHelper)[oldIds[i]]);
156 if (index >= 0)
157 {
158 edge_mapping[index] = i;
159 edge_status[index] = 0;
160 }
161 }
162 // Checking whether the deleted node is upper or lower node
163 const unsigned node_A_index = merged_nodes_pair.first;
164 const unsigned node_B_index = merged_nodes_pair.second;
165
166 // Node B is also considered upper node if the last two nodes are merged
167 bool is_B_upper = node_B_index > node_A_index;
168 if (node_A_index == 0)
169 {
170 is_B_upper = node_B_index == 1;
171 }
172 if (node_B_index == 0)
173 {
174 // This line is excluded from coverage, as it is very difficult to test this case
175 // This case becomes relevant in long time simulations of proliferating tissue
176 // and difficult to reproduce
177 is_B_upper = node_A_index != 1; // LCOV_EXCL_LINE
178 }
179
180 unsigned lower_node = node_A_index;
181 unsigned upper_node = node_B_index;
182 if (!is_B_upper)
183 {
184 lower_node = node_B_index;
185 upper_node = node_A_index;
186 }
187 // Previous edge denotes the edge below the lower node index
188 // and next_edge denotes the edge above the upper node index
189 unsigned prev_edge = 0;
190 if (is_B_upper)
191 {
192 if (upper_node == 0)
193 {
194 // This line is excluded from coverage, as it is very difficult to test this case
195 // This case becomes relevant in long time simulations of proliferating tissue
196 // and difficult to reproduce
197 prev_edge = element_num_edges - 2; // LCOV_EXCL_LINE
198 }
199 else if (upper_node == 1)
200 {
201 prev_edge = element_num_edges - 1;
202 }
203 else
204 {
205 prev_edge = upper_node - 2;
206 }
207 }
208 else
209 {
210 if (upper_node == 0 || upper_node == 1)
211 {
212 prev_edge = element_num_edges - 1;
213 }
214 else
215 {
216 prev_edge = upper_node - 2;
217 }
218 }
219 const unsigned next_edge = (prev_edge + 1) % element_num_edges;
220
221 // Marking edges below and above the deleted edge
222 edge_status[prev_edge] = 3;
223 edge_status[next_edge] = 3;
224
225 // The edge below node A is in the old element if node B is upper node, and marked with status 0 in the loop above
226 // Because node deletion during node merging also removes the pair of edges associated to it,
227 // we need to make sure the edges associated with nodes about to be merges correctly map to the old element edges
228 if (is_B_upper)
229 {
230 edge_mapping[next_edge] = node_B_index;
231 }
232 else
233 {
234 if (lower_node == 0)
235 {
236 // This line is excluded from coverage, as it is very difficult to test this case
237 // This case becomes relevant in long time simulations of proliferating tissue
238 // and difficult to reproduce
239 edge_mapping[prev_edge] = element_num_edges; // LCOV_EXCL_LINE
240 }
241 else
242 {
243 edge_mapping[prev_edge] = lower_node - 1;
244 }
245 }
246 // Sanity check
247 for (unsigned i = 0; i < edge_mapping.size(); ++i)
248 {
249 assert(edge_mapping[i] >= 0);
250 }
251
252 const EdgeRemapInfo remap_info(edge_mapping, edge_status);
253 mEdgeOperations.emplace_back(EDGE_OPERATION_NODE_MERGE, element_index, remap_info, elementIndexIsRemapped);
254}
255
256template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
258 const unsigned edgeIndex,
259 const double insertedNodeRelPosition,
260 const bool elementIndexIsRemapped)
261{
262 const unsigned element_index = pElement->GetIndex();
263 const unsigned element_num_edges = pElement->GetNumEdges();
264 std::vector<double> thetas(element_num_edges);
265 std::vector<long> edge_mapping(element_num_edges);
266 std::vector<unsigned> edge_status(element_num_edges, 0);
267
268 // Daughter edge indices
269 const unsigned split_1 = edgeIndex;
270 const unsigned split_2 = edgeIndex + 1;
271 edge_status[split_1] = 1;
272 edge_status[split_2] = 1;
273 thetas[split_1] = insertedNodeRelPosition;
274 thetas[split_2] = 1.0 - insertedNodeRelPosition;
275 unsigned count = 0;
276 for (unsigned i = 0; i < element_num_edges; ++i)
277 {
278 edge_mapping[i] = i - count;
279 if (edge_status[i] == 1)
280 {
281 count = 1;
282 }
283 }
284
285 EdgeRemapInfo remap_info(edge_mapping, edge_status);
286 remap_info.SetSplitProportions(thetas);
287 mEdgeOperations.emplace_back(EDGE_OPERATION_SPLIT, element_index, remap_info, elementIndexIsRemapped);
288}
289
290template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
294{
295 const unsigned num_edges_1 = pElement1->GetNumEdges();
296 const unsigned num_edges_2 = pElement2->GetNumEdges();
297 std::vector<long> edge_mapping_1(num_edges_1, -2);
298 std::vector<long> edge_mapping_2(num_edges_2, -2);
299 std::vector<unsigned> edge_status_1(num_edges_1);
300 std::vector<unsigned> edge_status_2(num_edges_2);
301
302 std::vector<unsigned> old_split_edges(rOldIds.size());
303 // Keeps track of parent edges that are NOT retained in daughter cells
304 for (unsigned i = 0; i < rOldIds.size(); ++i)
305 {
306 old_split_edges[i] = i;
307 }
308 unsigned counter_1 = 0;
309 unsigned counter_2 = 0;
310
311 // First find parent edges that correspond directly to daughter cells' edges
312 // At the end of the loop, old_split_edges contains parent edge indices that are split
313 for (unsigned i = 0; i < rOldIds.size(); ++i)
314 {
315 // Index of parent edge corresponding to daughter cell's edge.
316 //-1 if not found.
317 long index_1 = pElement1->GetLocalEdgeIndex((*mpEdgeHelper)[rOldIds[i]]);
318 long index_2 = pElement2->GetLocalEdgeIndex((*mpEdgeHelper)[rOldIds[i]]);
319 auto position = std::find(old_split_edges.begin(), old_split_edges.end(), i);
320
321 // Modify edge map and status
322 if (index_1 >= 0)
323 {
324 edge_mapping_1[index_1] = i;
325 edge_status_1[index_1] = 0;
326 old_split_edges.erase(position);
327 counter_1++;
328 }
329 if (index_2 >= 0)
330 {
331 edge_mapping_2[index_2] = i;
332 edge_status_2[index_2] = 0;
333 old_split_edges.erase(position);
334 counter_2++;
335 }
336 }
337 // Two parent edges are split
338 assert(old_split_edges.size() == 2);
339
340 // Three edges in daughter cells are unmapped
341 assert(counter_1 == num_edges_1 - 3);
342 assert(counter_2 == num_edges_2 - 3);
343
344 // Edge split proportions.
345 std::vector<double> thetas_1(num_edges_1);
346 std::vector<double> thetas_2(num_edges_2);
347
348 // Go through unmapped edges of daughter cell to find a mapping between parent split edge and
349 // daughter edge
350 std::vector<unsigned> old_split_edges_1(old_split_edges);
351 for (unsigned i = 0; i < num_edges_1; ++i)
352 {
353 if (edge_mapping_1[i] == -2)
354 {
355 auto p_node_1 = pElement1->GetEdge(i)->GetNode(0);
356 auto p_node_2 = pElement1->GetEdge(i)->GetNode(1);
357 bool split_edge_found = false;
358 for (unsigned j = 0; j < old_split_edges_1.size(); ++j)
359 {
360 auto old_edge = (*mpEdgeHelper)[rOldIds[old_split_edges_1[j]]];
361 if (old_edge->ContainsNode(p_node_1) || old_edge->ContainsNode(p_node_2))
362 {
363 edge_mapping_1[i] = old_split_edges_1[j];
364 edge_status_1[i] = 1;
365 counter_1++;
366 split_edge_found = true;
367 thetas_1[i] = pElement1->GetEdge(i)->rGetLength() / old_edge->rGetLength();
368 old_split_edges_1.erase(old_split_edges_1.begin() + j);
369 break;
370 }
371 }
372 if (!split_edge_found)
373 {
374 edge_mapping_1[i] = -1;
375 edge_status_1[i] = 2;
376 counter_1++;
377 }
378 }
379 }
380
381 for (unsigned i = 0; i < num_edges_2; ++i)
382 {
383 if (edge_mapping_2[i] == -2)
384 {
385 auto p_node_1 = pElement2->GetEdge(i)->GetNode(0);
386 auto p_node_2 = pElement2->GetEdge(i)->GetNode(1);
387 bool split_edge_found = false;
388 for (unsigned j = 0; j < old_split_edges.size(); ++j)
389 {
390 auto old_edge = (*mpEdgeHelper)[rOldIds[old_split_edges[j]]];
391 if (old_edge->ContainsNode(p_node_1) || old_edge->ContainsNode(p_node_2))
392 {
393 edge_mapping_2[i] = old_split_edges[j];
394 edge_status_2[i] = 1;
395 counter_2++;
396 split_edge_found = true;
397 thetas_2[i] = pElement2->GetEdge(i)->rGetLength() / old_edge->rGetLength();
398 old_split_edges.erase(old_split_edges.begin() + j);
399 break;
400 }
401 }
402 if (!split_edge_found)
403 {
404 edge_mapping_2[i] = -1;
405 edge_status_2[i] = 2;
406 counter_2++;
407 }
408 }
409 }
410 assert(old_split_edges_1.empty());
411 assert(old_split_edges.empty());
412
413 // Checking if all edges of daughter cells have been mapped.
414 assert(counter_1 == num_edges_1);
415 assert(counter_2 == num_edges_2);
416
417 EdgeRemapInfo remap_info_1(edge_mapping_1, edge_status_1);
418 EdgeRemapInfo remap_info_2(edge_mapping_2, edge_status_2);
419 remap_info_1.SetSplitProportions(thetas_1);
420 remap_info_2.SetSplitProportions(thetas_2);
421 mEdgeOperations.emplace_back(pElement1->GetIndex(), pElement2->GetIndex(), remap_info_1, remap_info_2);
422}
423
424template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
426 const unsigned edgeIndex)
427{
428 const unsigned element_index = pElement->GetIndex();
429 const unsigned num_edges = pElement->GetNumEdges();
430 std::vector<long> edge_mapping(num_edges, 0);
431 std::vector<unsigned> edge_status(num_edges);
432 for (unsigned i = 0; i < edgeIndex; ++i)
433 {
434 edge_mapping[i] = i;
435 edge_status[i] = 0;
436 }
437 edge_mapping[edgeIndex] = -1;
438 edge_status[edgeIndex] = 2;
439 for (unsigned i = edgeIndex + 1; i < num_edges; ++i)
440 {
441 edge_mapping[i] = i - 1;
442 edge_status[i] = 0;
443 }
444
445 const EdgeRemapInfo remap_info(edge_mapping, edge_status);
446 mEdgeOperations.emplace_back(EDGE_OPERATION_ADD, element_index, remap_info);
447}
448
449template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
451 const unsigned nodeIndex)
452{
453 const unsigned element_index = pElement->GetIndex();
454 const unsigned num_edges = pElement->GetNumEdges();
455 std::vector<long> edge_mapping(num_edges, 0);
456 std::vector<unsigned> edge_status(num_edges, 0);
457
458 // Here we find the edge with the lower index.
459 // High index edge is merged into low edge index
460 unsigned low_edge = (nodeIndex + num_edges) % (num_edges + 1);
461 unsigned high_edge = nodeIndex;
462
463 // If the first edge was merged into the last edge
464 if (low_edge > high_edge)
465 {
466 edge_status[low_edge - 1] = 4;
467 for (unsigned i = 0; i < num_edges; ++i)
468 {
469 edge_mapping[i] = i + 1;
470 }
471 edge_mapping[low_edge - 1] = low_edge;
472 }
473 else
474 {
475 edge_status[low_edge] = 4;
476 for (unsigned i = 0; i < high_edge; ++i)
477 {
478 edge_mapping[i] = i;
479 }
480 for (unsigned i = high_edge; i < num_edges; ++i)
481 {
482 edge_mapping[i] = i + 1;
483 }
484 }
485
486 const EdgeRemapInfo remap_info(edge_mapping, edge_status);
487 mEdgeOperations.emplace_back(EDGE_OPERATION_MERGE, element_index, remap_info);
488}
489
496
497// Serialization for Boost >= 1.36
#define EXPORT_TEMPLATE_CLASS_ALL_DIMS(CLASS)
unsigned GetIndex() const
void SetSplitProportions(const std::vector< double > proportions)
long GetLocalEdgeIndex(const Edge< SPACE_DIM > *pEdge) const
Edge< SPACE_DIM > * GetEdge(unsigned localIndex) const
unsigned GetNumEdges() const
void RecordNewEdgeOperation(VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement, const unsigned edgeIndex)
void SetEdgeHelper(EdgeHelper< SPACE_DIM > *pEdgeHelper)
void RecordT1Swap(T1SwapInfo< SPACE_DIM > &rSwapInfo)
std::vector< CellDivisionInfo< SPACE_DIM > > GetCellDivisionInfo() const
const std::vector< EdgeOperation > & GetEdgeOperations()
void RecordEdgeMergeOperation(VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement, const unsigned nodeIndex)
void RecordCellDivisionInfo(CellDivisionInfo< SPACE_DIM > &rDivisionInfo)
void RecordEdgeSplitOperation(VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement, const unsigned edgeIndex, const double insertedNodeRelPosition, const bool elementIndexIsRemapped=false)
std::vector< T1SwapInfo< SPACE_DIM > > GetT1SwapsInfo() const
void RecordCellDivideOperation(const std::vector< unsigned > &rOldIds, VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement1, VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement2)
std::vector< T3SwapInfo< SPACE_DIM > > GetT3SwapsInfo() const
void RecordT3Swap(T3SwapInfo< SPACE_DIM > &rSwapInfo)
void RecordNodeMergeOperation(const std::vector< unsigned > oldIds, VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement, const std::pair< unsigned, unsigned > mergedNodesPair, const bool elementIndexIsRemapped=false)
std::vector< T2SwapInfo< SPACE_DIM > > GetT2SwapsInfo() const
void RecordT2Swap(T2SwapInfo< SPACE_DIM > &rSwapInfo)