Chaste Commit::baa90ac2819b962188b7562f2326be23c47859a7
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
309 // These are maybe unused because in release builds the asserts below will be compiled out
310 [[maybe_unused]] unsigned counter_1 = 0;
311 [[maybe_unused]] unsigned counter_2 = 0;
312
313 // First find parent edges that correspond directly to daughter cells' edges
314 // At the end of the loop, old_split_edges contains parent edge indices that are split
315 for (unsigned i = 0; i < rOldIds.size(); ++i)
316 {
317 // Index of parent edge corresponding to daughter cell's edge.
318 //-1 if not found.
319 long index_1 = pElement1->GetLocalEdgeIndex((*mpEdgeHelper)[rOldIds[i]]);
320 long index_2 = pElement2->GetLocalEdgeIndex((*mpEdgeHelper)[rOldIds[i]]);
321 auto position = std::find(old_split_edges.begin(), old_split_edges.end(), i);
322
323 // Modify edge map and status
324 if (index_1 >= 0)
325 {
326 edge_mapping_1[index_1] = i;
327 edge_status_1[index_1] = 0;
328 old_split_edges.erase(position);
329 counter_1++;
330 }
331 if (index_2 >= 0)
332 {
333 edge_mapping_2[index_2] = i;
334 edge_status_2[index_2] = 0;
335 old_split_edges.erase(position);
336 counter_2++;
337 }
338 }
339 // Two parent edges are split
340 assert(old_split_edges.size() == 2);
341
342 // Three edges in daughter cells are unmapped
343 assert(counter_1 == num_edges_1 - 3);
344 assert(counter_2 == num_edges_2 - 3);
345
346 // Edge split proportions.
347 std::vector<double> thetas_1(num_edges_1);
348 std::vector<double> thetas_2(num_edges_2);
349
350 // Go through unmapped edges of daughter cell to find a mapping between parent split edge and
351 // daughter edge
352 std::vector<unsigned> old_split_edges_1(old_split_edges);
353 for (unsigned i = 0; i < num_edges_1; ++i)
354 {
355 if (edge_mapping_1[i] == -2)
356 {
357 auto p_node_1 = pElement1->GetEdge(i)->GetNode(0);
358 auto p_node_2 = pElement1->GetEdge(i)->GetNode(1);
359 bool split_edge_found = false;
360 for (unsigned j = 0; j < old_split_edges_1.size(); ++j)
361 {
362 auto old_edge = (*mpEdgeHelper)[rOldIds[old_split_edges_1[j]]];
363 if (old_edge->ContainsNode(p_node_1) || old_edge->ContainsNode(p_node_2))
364 {
365 edge_mapping_1[i] = old_split_edges_1[j];
366 edge_status_1[i] = 1;
367 counter_1++;
368 split_edge_found = true;
369 thetas_1[i] = pElement1->GetEdge(i)->rGetLength() / old_edge->rGetLength();
370 old_split_edges_1.erase(old_split_edges_1.begin() + j);
371 break;
372 }
373 }
374 if (!split_edge_found)
375 {
376 edge_mapping_1[i] = -1;
377 edge_status_1[i] = 2;
378 counter_1++;
379 }
380 }
381 }
382
383 for (unsigned i = 0; i < num_edges_2; ++i)
384 {
385 if (edge_mapping_2[i] == -2)
386 {
387 auto p_node_1 = pElement2->GetEdge(i)->GetNode(0);
388 auto p_node_2 = pElement2->GetEdge(i)->GetNode(1);
389 bool split_edge_found = false;
390 for (unsigned j = 0; j < old_split_edges.size(); ++j)
391 {
392 auto old_edge = (*mpEdgeHelper)[rOldIds[old_split_edges[j]]];
393 if (old_edge->ContainsNode(p_node_1) || old_edge->ContainsNode(p_node_2))
394 {
395 edge_mapping_2[i] = old_split_edges[j];
396 edge_status_2[i] = 1;
397 counter_2++;
398 split_edge_found = true;
399 thetas_2[i] = pElement2->GetEdge(i)->rGetLength() / old_edge->rGetLength();
400 old_split_edges.erase(old_split_edges.begin() + j);
401 break;
402 }
403 }
404 if (!split_edge_found)
405 {
406 edge_mapping_2[i] = -1;
407 edge_status_2[i] = 2;
408 counter_2++;
409 }
410 }
411 }
412 assert(old_split_edges_1.empty());
413 assert(old_split_edges.empty());
414
415 // Checking if all edges of daughter cells have been mapped.
416 assert(counter_1 == num_edges_1);
417 assert(counter_2 == num_edges_2);
418
419 EdgeRemapInfo remap_info_1(edge_mapping_1, edge_status_1);
420 EdgeRemapInfo remap_info_2(edge_mapping_2, edge_status_2);
421 remap_info_1.SetSplitProportions(thetas_1);
422 remap_info_2.SetSplitProportions(thetas_2);
423 mEdgeOperations.emplace_back(pElement1->GetIndex(), pElement2->GetIndex(), remap_info_1, remap_info_2);
424}
425
426template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
428 const unsigned edgeIndex)
429{
430 const unsigned element_index = pElement->GetIndex();
431 const unsigned num_edges = pElement->GetNumEdges();
432 std::vector<long> edge_mapping(num_edges, 0);
433 std::vector<unsigned> edge_status(num_edges);
434 for (unsigned i = 0; i < edgeIndex; ++i)
435 {
436 edge_mapping[i] = i;
437 edge_status[i] = 0;
438 }
439 edge_mapping[edgeIndex] = -1;
440 edge_status[edgeIndex] = 2;
441 for (unsigned i = edgeIndex + 1; i < num_edges; ++i)
442 {
443 edge_mapping[i] = i - 1;
444 edge_status[i] = 0;
445 }
446
447 const EdgeRemapInfo remap_info(edge_mapping, edge_status);
448 mEdgeOperations.emplace_back(EDGE_OPERATION_ADD, element_index, remap_info);
449}
450
451template<unsigned ELEMENT_DIM,unsigned SPACE_DIM>
453 const unsigned nodeIndex)
454{
455 const unsigned element_index = pElement->GetIndex();
456 const unsigned num_edges = pElement->GetNumEdges();
457 std::vector<long> edge_mapping(num_edges, 0);
458 std::vector<unsigned> edge_status(num_edges, 0);
459
460 // Here we find the edge with the lower index.
461 // High index edge is merged into low edge index
462 unsigned low_edge = (nodeIndex + num_edges) % (num_edges + 1);
463 unsigned high_edge = nodeIndex;
464
465 // If the first edge was merged into the last edge
466 if (low_edge > high_edge)
467 {
468 edge_status[low_edge - 1] = 4;
469 for (unsigned i = 0; i < num_edges; ++i)
470 {
471 edge_mapping[i] = i + 1;
472 }
473 edge_mapping[low_edge - 1] = low_edge;
474 }
475 else
476 {
477 edge_status[low_edge] = 4;
478 for (unsigned i = 0; i < high_edge; ++i)
479 {
480 edge_mapping[i] = i;
481 }
482 for (unsigned i = high_edge; i < num_edges; ++i)
483 {
484 edge_mapping[i] = i + 1;
485 }
486 }
487
488 const EdgeRemapInfo remap_info(edge_mapping, edge_status);
489 mEdgeOperations.emplace_back(EDGE_OPERATION_MERGE, element_index, remap_info);
490}
491
498
499// 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)