44 #include <trng/uniform01_dist.hpp>
59 void AssignExistingNodesToSplitCells(
Mesh* mesh,
Cell* this_cell,
60 vector<Node*>& parent_nodes, vector<Node*>& daughter_nodes,
61 const array<Node*, 2>& new_node_index)
63 const auto start = find(parent_nodes.begin(), parent_nodes.end(), new_node_index[0]);
64 assert(start != parent_nodes.end()
65 &&
"The new intersection should already be in the list of parent nodes.");
67 rotate(parent_nodes.begin(), start, parent_nodes.end());
69 const auto stop = find(parent_nodes.begin(), parent_nodes.end(), new_node_index[1]);
70 assert(stop != parent_nodes.end()
71 &&
"The new intersection should already be in the list of parent nodes.");
72 assert(stop != parent_nodes.begin()
73 &&
"The two intersections are the same.");
76 return (n.GetCell()->GetIndex() == this_cell->
GetIndex());
79 for (
auto it = parent_nodes.begin() + 1; it != stop; it++) {
81 auto nb_it = find_if(owners.begin(), owners.end(), is_nb);
82 assert((nb_it != owners.end()) &&
"Node manipulation failure!");
84 daughter_nodes.push_back(*it);
86 parent_nodes.erase(parent_nodes.begin(), stop + 1);
89 vector<Node*> ComputeIntersectNodes(
Cell* cell,
const array<double, 3>& axis)
91 const array<double, 3> centroid = cell->
GetCentroid();
92 const array<double, 3> cross_prod = CrossProduct(axis, centroid - *(cell->
GetNodes().back()));
93 double prev_cross_z = cross_prod[2];
94 vector<Node*> intersect_nodes;
95 vector<Node*>& nodes = cell->
GetNodes();
96 for (
unsigned int i = 0U; i < nodes.size(); i++) {
98 const array<double, 3> cross = CrossProduct(axis, centroid - *nodes.at(i));
101 if (cross[2] * prev_cross_z < 0) {
102 intersect_nodes.push_back(nodes.at(i));
104 prev_cross_z = cross[2];
106 return intersect_nodes;
109 vector<Node*> ComputeTwoIntersectNodes(
Cell* cell,
const array<double, 3>& axis, std::vector<Node*> intersect_nodes)
111 std::vector<Node*> new_intersect_nodes;
117 std::vector<Node*>& nodes = cell->
GetNodes();
119 std::array<double, 3> long_axis = std::get<1>(geo_dat.
GetEllipseAxes());
122 const auto to = from + axis;
124 std::map<Node*, double> intersect_points;
125 for (
Node* nn_ptr : intersect_nodes) {
131 const auto pr_it =
prev(nn_it);
132 const auto n_vec = Geom::Intersection(from, to, *(*nn_it), *(*pr_it));
137 double dist = sqrt(pow((cell->
GetCentroid()[0] - n_vec[0]), 2) +
143 const array<double, 3> cross = CrossProduct(long_axis, cell->
GetCentroid() - *nn_ptr);
150 intersect_points[nn_ptr] = dist;
157 double node1_dist = std::numeric_limits<double>::infinity();
159 double node2_dist = std::numeric_limits<double>::infinity();
160 for (
auto entry : intersect_points) {
161 if(entry.second >= 0) {
162 if(entry.second < node1_dist) {
164 node1_dist = entry.second;
167 if(abs(entry.second) < node2_dist) {
169 node2_dist = abs(entry.second);
174 new_intersect_nodes.push_back(node1);
175 new_intersect_nodes.push_back(node2);
176 return new_intersect_nodes;
190 CellDivider::CellDivider(
Mesh* mesh)
195 void CellDivider::AddSharedWallNodes(array<array<double, 3>, 2>& new_node_vector,
196 array<Node*, 2>& new_node_index, vector<Node*>& parent_nodes, vector<Node*>& daughter_nodes)
202 parent_nodes.push_back(new_node_index[0]);
203 daughter_nodes.push_back(new_node_index[1]);
208 if ( m_model_name !=
"Wortel"
209 && m_model_name !=
"Maize"
210 && m_model_name !=
"MaizeGRN"
211 && m_model_name !=
"WortelLight"
212 && m_model_name !=
"Blad"
213 && m_model_name !=
"WrapperModel") {
220 const double dist = Norm(new_node_vector[1] - new_node_vector[0]);
221 const double tnd = abs(m_target_node_distance);
223 const double new_dist = dist /
static_cast<double>(n + 1);
224 const auto nodevec = Normalize(new_node_vector[1] - new_node_vector[0]);
227 vector<Node*>::iterator ins_pos = daughter_nodes.end();
228 for (
int i = 1; i <= n; i++) {
229 const array<double, 3> vec = new_node_vector[0] + i * new_dist * nodevec;
230 auto node = m_mesh->BuildNode(vec,
false);
231 ins_pos = daughter_nodes.insert(ins_pos, node);
232 parent_nodes.push_back(node);
239 daughter_nodes.push_back(new_node_index[0]);
240 parent_nodes.push_back(new_node_index[1]);
243 void CellDivider::AddSplitWalls(
Cell* this_cell,
Cell* neighbor_cell,
int i,
244 array<Node*, 2>& new_node_ptr, array<Wall*, 4>& div_wall,
245 array<double, 2>& orig_length, array<double, 2>& orig_rest_length, array<double, 2>& orig_rest_length_init)
247 auto this_cell_walls = this_cell->
GetWalls();
248 assert((neighbor_cell !=
nullptr) &&
"There has to be a neighbor_cell!");
250 if (neighbor_cell && !this_cell_walls.empty() ) {
252 const auto is_of_nb =[neighbor_cell](
Wall* w) {
253 return (w->GetC1() == neighbor_cell) || (w->GetC2() == neighbor_cell);
261 auto w = this_cell_walls.begin();
264 w = find_if(w, this_cell_walls.end(), is_of_nb);
266 if (w == this_cell_walls.end()) {
270 auto nodes = (*w)->GetC1()->GetNodes();
271 rotate(nodes.begin(), find(nodes.begin(), nodes.end(), (*w)->GetN1()), nodes.end());
272 auto secondNode = find(nodes.begin(), nodes.end(), (*w)->GetN2());
274 for (
auto it = nodes.begin(); it !=
next(secondNode); it++) {
275 if (*it == new_node_ptr[i]) {
280 }
while (!ok && ++w != this_cell_walls.end());
281 assert((w != this_cell_walls.end()) &&
"No split wall element found!");
287 if ((new_node_ptr[i] != (*w)->GetN1()) && (new_node_ptr[i] != (*w)->GetN2())) {
295 orig_length[i] = (*w)->GetLength();
296 orig_rest_length[i] = (*w)->GetRestLength();
297 orig_rest_length_init[i] = (*w)->GetRestLengthInit();
298 if ((*w)->GetC1() == this_cell) {
299 new_wall = m_mesh->BuildWall((*w)->GetN1(),
300 new_node_ptr[i], this_cell, neighbor_cell);
302 new_wall = m_mesh->BuildWall((*w)->GetN1(),
303 new_node_ptr[i], neighbor_cell, this_cell);
305 (*w)->SetN1(new_node_ptr[i]);
306 (*w)->SetLengthDirty();
307 (*w)->SetRestLength((orig_rest_length[i] / orig_length[i])*(*w)->GetLength());
308 (*w)->SetRestLengthInit((orig_rest_length_init[i] / orig_length[i])*(*w)->GetLength());
313 (*w)->SetStrength((*w)->GetStrength());
314 new_wall->SetStrength((*w)->GetStrength());
315 assert(new_wall->GetN1() != new_wall->GetN2() &&
"What the hell!");
321 new_wall->WallAttributes::operator=(**w);
328 assert((m_cd.m_random_engine !=
nullptr) &&
"Random engine not initialized.");
329 if (m_uniform_generator() < 0.5) {
330 new_wall->WallAttributes::Swap(**w);
336 this_cell->AddWall(new_wall);
337 neighbor_cell->AddWall(new_wall);
338 div_wall[2 * i + 0] = *w;
339 div_wall[2 * i + 1] = new_wall;
345 m_mesh->UpdateNodeOwningWalls(*w);
346 m_mesh->UpdateNodeOwningWalls(new_wall);
351 void CellDivider::ComputeIntersection(
Cell* cell, vector<Node*> intersect_nodes,
352 const array<double, 3>& from,
const array<double, 3>& to,
353 array<array<double, 3>, 2>& new_node_vector, array<Node*, 2>& new_node_ptr,
354 array<int, 2>& new_node_flag, array<Edge, 2>& intersect_edge)
363 vector<Node*>& nodes = cell->
GetNodes();
364 for (
Node* nn_ptr : intersect_nodes) {
371 const auto pr_it =
prev(nn_it);
372 const auto n_vec = Geom::Intersection(from, to, *(*nn_it), *(*pr_it));
373 intersect_edge[nnc] =
Edge(*pr_it, *nn_it);
382 const array<double, 3> nn = *(*nn_it);
383 const array<double, 3> pr = *(*pr_it);
384 const double tol = m_collapse_node_threshold * Norm(nn - pr);
385 if (Norm(nn - n_vec) <= tol) {
386 new_node_flag[nnc] = 1;
387 new_node_vector[nnc] = nn;
388 new_node_ptr[nnc] = *nn_it;
389 }
else if (Norm(pr - n_vec) <= tol) {
390 new_node_flag[nnc] = 2;
391 new_node_vector[nnc] = pr;
392 new_node_ptr[nnc] = *pr_it;
394 new_node_flag[nnc] = 0;
395 new_node_vector[nnc] = n_vec;
396 new_node_ptr[nnc] =
nullptr;
403 unsigned int CellDivider::DivideCells()
405 unsigned int division_count = 0U;
409 const vector<Cell*> current_cells { m_mesh->GetCells() };
412 for (
auto const& cell : current_cells) {
413 auto tup = m_cell_split(cell);
416 DivideOverAxis(cell, get<2>(tup));
419 DivideOverAxis(cell, axis);
424 return division_count;
427 void CellDivider::DivideOverAxis(
Cell* cell,
const array<double, 3>& axis)
429 assert((m_cd.m_mesh !=
nullptr) &&
"Mesh not initialized.");
438 auto intersect_nodes = ComputeIntersectNodes(cell, axis);
439 assert((intersect_nodes.size() == 2) &&
"Can only handle two intersection points.");
451 if (intersect_nodes.size() != 2) {
457 && intersect_nodes.size() > 2)
459 intersect_nodes = ComputeTwoIntersectNodes(cell, axis, intersect_nodes);
461 const string msg = string(VL_HERE) +
" exception:\nToo many intersection points.";
477 const auto to = from + axis;
478 Cell* daughter = SplitCell(cell, intersect_nodes, from, to);
479 cell->IncrementDivisionCounter();
480 daughter->IncrementDivisionCounter();
485 daughter->CellAttributes::operator=(this_cell_old_attributes);
487 const auto new_solute = cell->GetSolute() / sqrt(2.0);
488 cell->SetSolute(new_solute);
489 daughter->SetSolute(new_solute);
491 const auto new_t_area = cell->GetTargetArea() / 2.0;
492 cell->SetTargetArea(new_t_area);
493 daughter->SetTargetArea(new_t_area);
495 const auto new_t_length = cell->GetTargetLength() / sqrt(2.0);
496 cell->SetTargetLength(new_t_length);
497 daughter->SetTargetLength(new_t_length);
499 m_cell_daughters(daughter, cell);
502 list<Cell*> CellDivider::GeometricSplitCell(
Cell* cell,
Node* node1,
Node* node2)
504 vector<Node*> intersect_nodes;
505 vector<Node*> nodes = cell->
GetNodes();
506 for (
unsigned int i = 0; i < nodes.size(); i++) {
507 if (nodes.at(i) == node1 || nodes.at(i) == node2) {
508 intersect_nodes.push_back(nodes.at(i));
512 Cell* newCell = SplitCell(cell, intersect_nodes, *node1, *node2);
514 newCell->SetGeoDirty();
515 newCell->CellAttributes::operator=(*cell);
517 list<Cell*> newCells;
518 newCells.push_back(cell);
519 newCells.push_back(newCell);
524 void CellDivider::Initialize(
Mesh* mesh)
527 assert( (mesh !=
nullptr) &&
"mesh pointer not ok.");
529 m_collapse_node_threshold = 10e-12;
533 m_target_node_distance = 0.0;
536 catch(exception& e) {
537 const string here = string(VL_HERE) +
" exception:\n";
545 assert( cd.
Check() &&
"CoreData not ok.");
547 auto& p = m_cd.m_parameters;
548 m_mesh = m_cd.m_mesh.get();
549 const auto model_group = m_cd.m_parameters->get<
string>(
"model.group",
"");
551 const auto factory = ComponentFactoryProxy::Create(model_group);
552 m_cell_daughters = factory->CreateCellDaughters(m_cd);
553 m_cell_split = factory->CreateCellSplit(m_cd);
555 m_collapse_node_threshold = 0.05;
556 m_copy_wall = p->get<
bool>(
"cell_mechanics.copy_wall");
557 m_model_name = p->get<
string>(
"model.name");
558 m_target_node_distance = p->get<
double>(
"cell_mechanics.target_node_distance");
560 const trng::uniform01_dist<double> dist;
561 m_uniform_generator = m_cd.m_random_engine->GetGenerator(dist);
563 catch(exception& e) {
564 const string here = string(VL_HERE) +
" exception:\n";
569 Node* CellDivider::InsertNewNodeAtIntersect(
Cell* this_cell,
Cell* neighbor_cell,
570 array<double, 3> node_vec,
const Edge& intrsct_edge)
572 auto& this_cell_nodes = this_cell->
GetNodes();
573 const bool at_boundary = m_mesh->IsAtBoundary(&intrsct_edge);
574 Node* node_ptr = m_mesh->BuildNode(node_vec, at_boundary);
579 const auto ins_pos_this_cell = find(this_cell_nodes.begin(), this_cell_nodes.end(), intrsct_edge.
GetSecond());
580 assert(((ins_pos_this_cell != this_cell_nodes.begin() && *
prev(ins_pos_this_cell) == intrsct_edge.
GetFirst())
581 || this_cell_nodes.back() == intrsct_edge.
GetFirst()) &&
"Error in insertion position!");
582 this_cell_nodes.insert(ins_pos_this_cell, node_ptr);
587 auto& nb_nodes = neighbor_cell->
GetNodes();
588 #if (defined(__clang__) && defined(__APPLE__))
589 const auto ins_pos_nb_cell = find(nb_nodes.cbegin(), nb_nodes.cend(), intrsct_edge.
GetFirst());
591 const auto ins_pos_nb_cell = find(nb_nodes.begin(), nb_nodes.end(), intrsct_edge.
GetFirst());
594 #if (defined(__clang__) && defined(__APPLE__))
605 nb_nodes.insert((
next(cit)).
get(), node_ptr);
607 assert((*
prev(cit) == intrsct_edge.
GetSecond()) &&
"Error in insertion position!");
608 nb_nodes.insert(cit.get(), node_ptr);
614 Cell* CellDivider::SplitCell(
Cell* this_cell, vector<Node*> intersect_nodes,
615 const array<double, 3>& from,
const array<double, 3>& to)
617 auto daughter = m_mesh->BuildCell();
618 auto& this_cell_nodes = this_cell->
GetNodes();
619 auto& daughter_nodes = daughter->GetNodes();
621 array<array<double, 3>, 2> new_node_vector;
622 array<Node*, 2> new_node_ptr;
623 array<int, 2> new_node_flag;
624 array<Edge, 2> intersect_edge;
625 array<Wall*, 4> div_wall {{
nullptr,
nullptr,
nullptr,
nullptr}};
626 array<double, 2> orig_length {{0.0, 0.0}};
627 array<double, 2> orig_rest_length {{0.0, 0.0}};
628 array<double, 2> orig_rest_length_init {{0.0, 0.0}};
636 ComputeIntersection(this_cell, intersect_nodes, from, to,
637 new_node_vector, new_node_ptr, new_node_flag, intersect_edge);
644 for (
int i = 0; i < 2; i++) {
645 Cell* neighbor_cell = m_mesh->FindEdgeNeighbor(this_cell, intersect_edge[i]);
646 assert(neighbor_cell !=
nullptr &&
"Where is the neighbor?");
648 if (new_node_flag[i] == 0) {
649 new_node_ptr[i] = InsertNewNodeAtIntersect(
650 this_cell, neighbor_cell, new_node_vector[i], intersect_edge[i]);
655 m_mesh->UpdateNodeOwningNeighbors(neighbor_cell);
661 (new_node_ptr[i])->SetFixed(intersect_edge[i].IsFixed());
664 AddSplitWalls(this_cell, neighbor_cell, i, new_node_ptr, div_wall, orig_length, orig_rest_length, orig_rest_length_init);
675 AssignExistingNodesToSplitCells(
676 m_mesh, this_cell, this_cell_nodes, daughter_nodes, new_node_ptr);
678 new_node_vector, new_node_ptr, this_cell_nodes, daughter_nodes);
679 m_mesh->UpdateNodeOwningNeighbors(this_cell);
680 m_mesh->UpdateNodeOwningNeighbors(daughter);
685 this_cell->SetGeoDirty();
686 daughter->SetGeoDirty();
693 auto copy_walls = this_cell->
GetWalls();
694 for (
Wall* w : copy_walls) {
695 auto first = find(this_cell_nodes.begin(), this_cell_nodes.end(), w->GetN1());
696 auto second = find(this_cell_nodes.begin(), this_cell_nodes.end(), w->GetN2());
698 if (first == this_cell_nodes.end() || second == this_cell_nodes.end()
699 || (w->GetN1() == new_node_ptr[0] && w->GetN2() == new_node_ptr[1])) {
701 this_cell->ReassignWall(w, daughter);
702 if (w->GetC1() == this_cell) {
714 if (m_mesh->GetCells().size() == 2) {
715 Wall* daughter_wall = m_mesh->BuildWall(
716 new_node_ptr[0], new_node_ptr[1], daughter,
717 m_mesh->GetBoundaryPolygon());
718 daughter->AddWall(daughter_wall);
719 m_mesh->GetBoundaryPolygon()->AddWall(daughter_wall);
721 m_mesh->UpdateNodeOwningWalls(daughter_wall);
723 Wall* parent_wall = m_mesh->BuildWall(
724 new_node_ptr[1], new_node_ptr[0], this_cell,
725 m_mesh->GetBoundaryPolygon());
726 this_cell->AddWall(parent_wall);
727 m_mesh->GetBoundaryPolygon()->AddWall(parent_wall);
729 m_mesh->UpdateNodeOwningWalls(parent_wall);
735 auto wall = m_mesh->BuildWall(
736 new_node_ptr[0], new_node_ptr[1], this_cell, daughter);
737 this_cell->AddWall(wall);
738 daughter->AddWall(wall);
740 m_mesh->UpdateNodeOwningWalls(wall);
742 if ( m_model_name ==
"Wortel" ) {
743 double wall_strength = 0.0;
744 std::array<double, 3> ref{{1., 0., 0.}};
745 std::array<double, 3> tot1 = *wall->GetN1();
746 std::array<double, 3> tot2 = *wall->GetN2();
747 std::array<double, 3> tot3 = tot1 - tot2;
748 double t2 = SignedAngle(tot3, ref);
749 if ( ( t2 > (-
pi() * 3 / 4) && t2 <= (-
pi() / 4) ) || ( t2 > (
pi() / 4) && t2 <= (
pi() * 3 / 4) ) )
755 wall->SetStrength(wall_strength);
761 for (
int i = 0; i < 4; i++) {
763 const double current_length = div_wall[i]->GetLength();
764 const double correction = current_length / orig_length[i/2];
766 div_wall[i]->SetRestLength(orig_rest_length[i/2] * correction);
767 div_wall[i]->SetRestLengthInit(orig_rest_length_init[i/2] * correction);
769 auto transporters1 = div_wall[i]->GetTransporters1();
770 for (
auto& t : transporters1) {
773 div_wall[i]->SetTransporters1(transporters1);
775 auto transporters2 = div_wall[i]->GetTransporters2();
776 for (
auto& t : transporters2) {
779 div_wall[i]->SetTransporters2(transporters2);
787 list<Cell*> broken_neighbors;
788 auto& this_neighbors = m_mesh->GetNeighbors(this_cell);
789 copy(this_neighbors.begin(), this_neighbors.end(), back_inserter(broken_neighbors));
790 broken_neighbors.push_back(this_cell);
791 broken_neighbors.push_back(daughter);
796 for (
auto const& bn : broken_neighbors) {
797 m_mesh->ConstructNeighborList(bn);
Core data with mesh, parameters, random engine and time data.
constexpr int signum(T x, std::false_type)
Overload for unsigned types.
constexpr double small_real()
Small real number.
Interface for CellDivider.
A cell contains walls and nodes.
Core data used during model execution.
Plain data structure with cell geometric data (such as area).
const std::list< Wall * > & GetWalls() const
Access the cell's walls.
Interface for NeighborNodes.
Interface of RandomEngine.
Extremely simple Exception root class.
Namespace for the core simulator.
An Edge connects two nodes and is ambidextrous.
Namespace for container related classes.
void SetLengthDirty() const
Sets the 'dirty bit' i.e. length will recalculated on first use.
Macro defs for debug and logging.
Structure of neighboring nodes: two neighboring nodes from standpoint of a given cell with an orderin...
Proxy for dealing with the factories.
Node * GetFirst() const
Get the first node of the edge.
const std::vector< Node * > & GetNodes() const
Access the nodes of cell's polygon.
ConstCircularIterator class and helper functions.
int GetIndex() const
Return the index.
std::list< NeighborNodes > & GetNodeOwningNeighbors(Node *n)
Get the neighbors associated with this node.
std::array< double, 3 > GetCentroid() const
Return the centroid position.
Extending std with arithmetic for std::array.
Structure of cells; key data structure.
std::tuple< double, std::array< double, 3 >, double > GetEllipseAxes() const
Calculate axes (length and direction) area moment of inertia ellipse.
GeoData GetGeoData() const
Return GeData (area, centroid, area moment of inertia).
CircularIterator< T > next(CircularIterator< T > i)
Helper yields the position the iterator would have if moved forward (in circular fashion) by 1 positi...
Interface for CellAttributes.
Header file for Exception class.
ConstCircularIterator< typename T::const_iterator > make_const_circular(const T &c)
Helper produces const circular iterator whose range corresponds to the begin and end iterators of a c...
CircularIterator< T > prev(CircularIterator< T > i)
Helper yields the position the iterator would have if moved backward (in circular fashion) by 1 posit...
CircularIterator class and helper functions.
constexpr double pi()
Math constant pi.
A cell wall, runs between cell corner points and consists of wall elements.
bool Check() const
Verify all pointers non-null.
Node * GetSecond() const
Get the second node of the edge.