LBIBCell
 All Classes Functions Variables Friends Pages
tutorial_01_BioSolverCellDivision.cpp
1 /* Copyright (c) 2013 David Sichau <mail"at"sichau"dot"eu>
2  * 2013-2015 Simon Tanaka <tanakas"at"gmx"dot"ch>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 #include <LbmLib/include/solver/BioSolver/tutorial_01_BioSolverCellDivision.hpp>
23 #include <LbmLib/include/geometry/Connection.hpp>
24 #include <LbmLib/include/geometry/GeometryHandler.hpp>
25 #include <LbmLib/include/nodes/PhysicalNode.hpp>
26 #include <LbmLib/include/GlobalSimulationParameters.hpp>
27 #include <vector>
28 #include <string>
29 #include <iostream>
30 
31 namespace LbmLib {
32 namespace solver {
33 namespace {
34  const unsigned int FREQUENCY = 100;
35  const double MAXCELLSIZE = 380;
36 }
37 namespace bg = boost::geometry;
38 
39 tutorial_01_BioSolverCellDivision::tutorial_01_BioSolverCellDivision() : BioBaseSolver()
40 {}
41 
43  ForceSolver &forcesolver
44  ) {
45  std::map<unsigned int,double> areas;
46 
47  if (Parameters.getCurrentIteration()%FREQUENCY != 0) {
48  return;
49  }
50 
51  // step 1: update cell definition trackings
52  this->updateCellDefinition(geometryhandler);
53 
54  // step 1b: initialize cellTypeTrackereMap iff not yet done
55  if (geometryhandler.getCellTypeTrackerMap().size() == 0) {
56  geometryhandler.getCellTypeTrackerMap().clear();
57  geometryhandler.getCellTypeTrackerMap()[0] = 0; // fluid default
58  for (auto it : this->cellDefinition_) { // default all other cells to type 1
59  geometryhandler.getCellTypeTrackerMap()[it.first] = 1;
60  }
61  }
62 
63  // step 2: compute areas
64  areas = geometryhandler.computeAreas();
65  areas[0] = 0;
66 
67  // step 3: if required, divide cell
68  for (auto it = ++areas.begin(); // skip domainIdentifier=0
69  it != areas.end();
70  it++) {
71  if (it->second > MAXCELLSIZE) {
72 
73  // step 3a: divide the cell
74  this->divideCell(geometryhandler,it->first);
75 
76  // step 3b: update cell definition trackings
77  this->updateCellDefinition(geometryhandler);
78 
79  // step 3c: add the new cell to the cellTypeTrackerMap
80  geometryhandler.getCellTypeTrackerMap()[this->cellDefinition_.rbegin()->first] =
81  geometryhandler.getCellTypeTrackerMap()[it->first];
82 
83  // step 3d: cure the Lattice
84  geometryhandler.cureLattice();
85  }
86  }
87 
88  //step 4: copy the cell types to the PhysicalGrid
89  geometryhandler.copyCellTypeToPhysicalNodes(
90  geometryhandler.getCellTypeTrackerMap()
91  );
92 }
93 
94 void tutorial_01_BioSolverCellDivision::divideCell(geometry::GeometryHandler& geometryhandler,unsigned int domainidentifier)
95 {
96  const unsigned int newCellID = this->cellDefinition_.rbegin()->first + 1;
97  std::vector<std::shared_ptr<LbmLib::geometry::Connection> > affectedConnections;
98 
99  // step 1: find the two connections used for dividing the cell (choose the policy!)
100  affectedConnections = this->getTwoConnectionsLongestAxis(domainidentifier);
101  //affectedConnections = this->getTwoConnectionsRandomDirection(domainidentifier);
102 
103  // step : seal the cut edges:
104  affectedConnections[0]->getGeometryNodes().first->setConnection<1>(nullptr);
105  affectedConnections[0]->getGeometryNodes().second->setConnection<0>(nullptr);
106  affectedConnections[1]->getGeometryNodes().first->setConnection<1>(nullptr);
107  affectedConnections[1]->getGeometryNodes().second->setConnection<0>(nullptr);
108 
109  // step 2: create the new *Connection*s
110  const std::map<std::string, std::vector<std::string> > descriptor1 =
111  affectedConnections[0]->getBoundaryConditionDescriptor();
112  const std::map<std::string, std::vector<std::string> > descriptor2 =
113  affectedConnections[1]->getBoundaryConditionDescriptor();
114  if (descriptor1 != descriptor2) {
115  lbm_fail("The method cannot handle cell division cases with heterogeneous boundary conditions.");
116  }
117 
118  geometryhandler.createConnection(
119  (*affectedConnections[0]).getGeometryNodes().first,
120  (*affectedConnections[1]).getGeometryNodes().second,
121  descriptor1,
122  domainidentifier);
123 
124  geometryhandler.createConnection(
125  (*affectedConnections[1]).getGeometryNodes().first,
126  (*affectedConnections[0]).getGeometryNodes().second,
127  descriptor1,
128  domainidentifier);
129 
130  // step 3: erase old *Connection*s
131  for (auto it : affectedConnections) {
132  geometryhandler.eraseConnection(it);
133  }
134 
135  //step 4: bump domainID's of the connections
136  std::shared_ptr<LbmLib::geometry::Connection> CSTART = (*affectedConnections[1]).getGeometryNodes().second->getConnection<1>();
137  std::shared_ptr<LbmLib::geometry::Connection> C1 = CSTART;
138  std::shared_ptr<LbmLib::geometry::Connection> C2 = nullptr;
139  CSTART->setDomainIdentifier(newCellID);
140  while (C2 != CSTART) {
141  C2 = (*C1).getGeometryNodes().second->getConnection<1>();
142  C2->setDomainIdentifier(newCellID);
143  C1 = C2;
144  }
145 
146  LOG(UtilLib::logINFO) << "Cell with domainIdentifier=" <<
147  domainidentifier << " divided" <<
148  " at time " << Parameters.getCurrentIteration();
149 }
150 
151 const std::vector<std::shared_ptr<LbmLib::geometry::Connection> >
152 tutorial_01_BioSolverCellDivision::getTwoConnectionsLongestAxis(unsigned int domainidentifier)
153 {
154  typedef bg::model::point<double, 2, bg::cs::cartesian> point;
155  std::vector<std::shared_ptr<LbmLib::geometry::Connection> > affectedconnections;
156  double dist = 0.0;
157  double tempdist = 0.0;
158  double invslope = 0.0;
159  double intercept = 0.0;
160  std::shared_ptr<nodes::GeometryNode> N1 = nullptr;
161  std::shared_ptr<nodes::GeometryNode> N2 = nullptr;
162  point MIDPOINT;
163  bool C1 = 0;
164  bool C2 = 0;
165 
166  // step 1: find furthest point pair (brute force, O(N²) complexity)
167  for (auto it1 : this->cellDefinition_[domainidentifier]) {
168  for (auto it2 : this->cellDefinition_[domainidentifier]) {
169  tempdist = boost::geometry::distance(*((*it1).getGeometryNodes().first),
170  *((*it2).getGeometryNodes().first)
171  );
172  if (tempdist > dist) {
173  dist = tempdist;
174  N1 = (*it1).getGeometryNodes().first;
175  N2 = (*it2).getGeometryNodes().first;
176  }
177  }
178  }
179 
180  // step 2: compute perpendicular axis
181  MIDPOINT.set<0>(0.5*(N1->getXPos()+N2->getXPos()));
182  MIDPOINT.set<1>(0.5*(N1->getYPos()+N2->getYPos()));
183  invslope = -(N2->getXPos()-N1->getXPos()) / (N2->getYPos()-N1->getYPos());
184  intercept = MIDPOINT.get<1>() - invslope*MIDPOINT.get<0>();
185  assert(std::isfinite(invslope));
186  assert(std::isfinite(intercept));
187 
188  // step 3: find affected *Connection*s
189  for (auto it : this->cellDefinition_[domainidentifier]) {
190  C1 = (*it).getGeometryNodes().first->getYPos() > invslope*(*it).getGeometryNodes().first->getXPos() + intercept;
191  C2 = (*it).getGeometryNodes().second->getYPos() < invslope*(*it).getGeometryNodes().second->getXPos() + intercept;
192  if ( (C1 && C2) || (!C1 && !C2)) {
193  affectedconnections.push_back(it);
194  }
195  }
196  assert(affectedconnections.size()==2 && "More or less than 2 connections found in BioSolver::getTwoConnectionsLongestAxis().");
197  return affectedconnections;
198 }
199 
200 const std::vector<std::shared_ptr<LbmLib::geometry::Connection> >
201 tutorial_01_BioSolverCellDivision::getTwoConnectionsRandomDirection(unsigned int domainidentifier)
202 {
203  typedef bg::model::point<double, 2, bg::cs::cartesian> point;
204  std::vector<std::shared_ptr<LbmLib::geometry::Connection> > affectedconnections;
205  double invslope = 0.0;
206  double intercept = 0.0;
207  const int seed = 1; // for debugging
208  static std::mt19937 gen(seed); // for debugging
209  //static std::random_device rd;
210  //static std::mt19937 gen(rd());
211  std::uniform_real_distribution<> dis(0.0,2.0*M_PI); // [rad]
212  bool C1 = 0;
213  bool C2 = 0;
214  Field<double> cm(0,0);
215 
216  // step 1: find the center of mass
217  for (auto it : this->cellDefinition_[domainidentifier]) {
218  cm += (*it).getGeometryNodes().first->getPos();
219  }
220  cm /= this->cellDefinition_[domainidentifier].size();
221 
222  unsigned int counter = 0;
223  while (affectedconnections.size()!=2) {
224  // step 2: get a random direction and compute intercept
225  invslope = std::tan(dis(gen));
226  intercept = cm.y - invslope*cm.x;
227  assert(std::isfinite(invslope));
228  assert(std::isfinite(intercept));
229  affectedconnections.clear();
230 
231  // step 3: find affected *Connection*s
232  for (auto it : this->cellDefinition_[domainidentifier]) {
233  C1 = (*it).getGeometryNodes().first->getYPos() > invslope*(*it).getGeometryNodes().first->getXPos() + intercept;
234  C2 = (*it).getGeometryNodes().second->getYPos() < invslope*(*it).getGeometryNodes().second->getXPos() + intercept;
235  if ( (C1 && C2) || (!C1 && !C2)) {
236  affectedconnections.push_back(it);
237  }
238  }
239 
240  counter++;
241  if (counter > 10) {
242  lbm_fail("could not find cell division plane");
243  }
244  }
245 
246  assert(affectedconnections.size()==2 && "More or less than 2 connections found in BioSolver::getTwoConnectionsLongestAxis().");
247  return affectedconnections;
248 }
249 
250 void tutorial_01_BioSolverCellDivision::updateCellDefinition(geometry::GeometryHandler& geometryhandler)
251 {
252  this->cellDefinition_.clear();
253  for (auto it : geometryhandler.getGeometry().getConnections()) {
254  this->cellDefinition_[(*it).getDomainIdentifier()].push_back(it);
255  }
256 }
257 
258 const std::string tutorial_01_BioSolverCellDivision::name = "tutorial_01_BioSolverCellDivision";
259 }
260 } // end namespace
261 
void copyCellTypeToPhysicalNodes(std::map< unsigned int, unsigned int > &celltrackermap)
update the celltypes of all *PhysicalNode*s with domainid
void eraseConnection(std::shared_ptr< Connection > toErase)
Erases the Connection.
The actual force solver.
Definition: ForceSolver.hpp:43
void createConnection(std::shared_ptr< nodes::GeometryNode > const p1, std::shared_ptr< nodes::GeometryNode > const p2, const std::map< std::string, std::vector< std::string > > boundaryconditiondescriptor, const unsigned int domainidentifier)
Create a new connection.
void cureLattice()
Cure the Lattice: update *BoundaryNode*s, DomainIdentifier, and IB connections.
virtual void applyBioProcess(geometry::GeometryHandler &geometryhandler, solver::ForceSolver &forcesolver)
Applies biological processes.
const std::map< unsigned int, double > computeAreas() const
Compute the areas of the domains by using the domainIdentifiers.
std::map< unsigned int, unsigned int > & getCellTypeTrackerMap(void)
Returns a reference to the cellTypeTrackerMap.
class responsible for generating the internal geometry representation