Chaste  Build::
OffLatticeMigrationRule.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4  All rights reserved.
5 
6  University of Oxford means the Chancellor, Masters and Scholars of the
7  University of Oxford, having an administrative office at Wellington
8  Square, Oxford OX1 2JD, UK.
9 
10  This file is part of Chaste.
11 
12  Redistribution and use in source and binary forms, with or without
13  modification, 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 
23  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34  */
35 
36 #include "GeometryTools.hpp"
37 #include "OffLatticeMigrationRule.hpp"
38 #include "RandomNumberGenerator.hpp"
39 #include "BaseUnits.hpp"
40 
41 template<unsigned DIM>
43  : AbstractMigrationRule<DIM>(),
44  mGlobalX(unit_vector<double>(3,0)),
45  mGlobalY(unit_vector<double>(3,1)),
46  mGlobalZ(unit_vector<double>(3,2)),
47  mMeanAngles(std::vector<units::quantity<unit::plane_angle> >(3, 0.0*unit::radians)),
48  mSdvAngles(std::vector<units::quantity<unit::plane_angle> >(3, M_PI/18.0*unit::radians)),
49  mVelocity(20.0 *(1.e-6/3600.0) * unit::metre_per_second),
50  mChemotacticStrength(1.0),
51  mAttractionStrength(1.0),
52  mProbeLength(5.0 * 1.e-6 * unit::metres),
53  mCriticalMutualAttractionLength(100.0 * 1.e-6 *unit::metres)
54 {
55 
56 }
57 
58 template <unsigned DIM>
59 boost::shared_ptr<OffLatticeMigrationRule<DIM> > OffLatticeMigrationRule<DIM>::Create()
60 {
61  MAKE_PTR(OffLatticeMigrationRule<DIM>, pSelf);
62  return pSelf;
63 }
64 
65 template<unsigned DIM>
67 {
68 
69 }
70 
71 template<unsigned DIM>
72 void OffLatticeMigrationRule<DIM>::SetSproutingVelocity(units::quantity<unit::velocity> velocity)
73 {
74  mVelocity = velocity;
75 }
76 
77 template<unsigned DIM>
79 {
80  mChemotacticStrength = strength;
81 }
82 
83 template<unsigned DIM>
85 {
86  mAttractionStrength = strength;
87 }
88 
89 template<unsigned DIM>
90 std::vector<DimensionalChastePoint<DIM> > OffLatticeMigrationRule<DIM>::GetDirections(const std::vector<boost::shared_ptr<VesselNode<DIM> > >& rNodes)
91 {
92  if (this->mIsSprouting)
93  {
94  return GetDirectionsForSprouts(rNodes);
95  }
96  else
97  {
98  std::vector<DimensionalChastePoint<DIM> > movement_vectors;
99 
100  // We want to probe the PDE solution all at once first if needed, as this is an expensive operation if done node-by-node.
101  // Every node has 5 probes in 2D and 7 in 3D.
102  std::vector<units::quantity<unit::concentration> > probed_solutions;
103  unsigned probes_per_node = (2*DIM)+1;
104  std::vector<DimensionalChastePoint<DIM> > probe_locations(probes_per_node*rNodes.size(), DimensionalChastePoint<DIM>(0.0, 0.0, 0.0, 1.e-6*unit::metres));
105  std::vector<bool> candidate_locations_inside_domain(probes_per_node*rNodes.size(), true);
106 
107  if(this->mpSolver)
108  {
109  for(unsigned idx=0; idx<rNodes.size(); idx++)
110  {
111  std::vector<DimensionalChastePoint<DIM> > local_probe_locations = GetProbeLocationsExternalPoint<DIM>(rNodes[idx]->rGetLocation(), mProbeLength);
112  for(unsigned jdx=0;jdx<probes_per_node; jdx++)
113  {
114  probe_locations[idx*probes_per_node + jdx] = local_probe_locations[jdx];
115  }
116  }
117  if(probe_locations.size()>0)
118  {
119  probed_solutions = this->mpSolver->GetConcentrations(probe_locations);
120  }
121 
122  if (this->mpBoundingDomain)
123  {
124  candidate_locations_inside_domain = this->mpBoundingDomain->IsPointInPart(probe_locations);
125  }
126  }
127 
128  for(unsigned idx=0; idx<rNodes.size(); idx++)
129  {
130  // Persistent random walk
131  units::quantity<unit::plane_angle> angle_x =
132  RandomNumberGenerator::Instance()->NormalRandomDeviate(mMeanAngles[0]/unit::radians, mSdvAngles[0]/unit::radians)*unit::radians;
133  units::quantity<unit::plane_angle> angle_y =
134  RandomNumberGenerator::Instance()->NormalRandomDeviate(mMeanAngles[1]/unit::radians, mSdvAngles[1]/unit::radians)*unit::radians;
135  units::quantity<unit::plane_angle> angle_z =
136  RandomNumberGenerator::Instance()->NormalRandomDeviate(mMeanAngles[2]/unit::radians, mSdvAngles[2]/unit::radians)*unit::radians;
137  DimensionalChastePoint<DIM> currentDirection = rNodes[idx]->rGetLocation()-rNodes[idx]->GetSegments()[0]->GetOppositeNode(rNodes[idx])->rGetLocation();
138  c_vector<double, DIM> new_direction;
139  if(DIM==3)
140  {
141  c_vector<double, DIM> new_direction_z = RotateAboutAxis<DIM>(currentDirection.GetUnitVector(), mGlobalZ, angle_z);
142  c_vector<double, DIM> new_direction_y = RotateAboutAxis<DIM>(new_direction_z, mGlobalY, angle_y);
143  new_direction = RotateAboutAxis<DIM>(new_direction_y, mGlobalX, angle_x);
144  new_direction /= norm_2(new_direction);
145  }
146  else
147  {
148  new_direction = RotateAboutAxis<DIM>(currentDirection.GetUnitVector(), mGlobalZ, angle_z);
149  new_direction /= norm_2(new_direction);
150  }
151 
152  // Chemotaxis
153  if(this->mpSolver)
154  {
155  // Get the gradients
156  std::vector<units::quantity<unit::concentration_gradient> > gradients(probes_per_node-1, 0.0*unit::mole_per_metre_pow_4);
157  if(probed_solutions.size()>=idx*probes_per_node+probes_per_node)
158  {
159  for(unsigned jdx=1; jdx<probes_per_node; jdx++)
160  {
161  gradients[jdx-1] = ((probed_solutions[idx*probes_per_node+jdx] - probed_solutions[idx*probes_per_node]) / mProbeLength);
162  if(!candidate_locations_inside_domain[idx*probes_per_node+jdx])
163  {
164  gradients[jdx-1] = 0.0*unit::mole_per_metre_pow_4;
165  }
166  }
167  }
168 
169  // Get the index of the max viable gradient
170  units::quantity<unit::concentration_gradient> max_grad = 0.0 * unit::mole_per_metre_pow_4;
171  int my_index = -1;
172 
173  for(unsigned jdx = 0; jdx<gradients.size(); jdx++)
174  {
175  if(gradients[jdx]>max_grad)
176  {
177  max_grad = gradients[jdx];
178  my_index = int(jdx);
179  }
180  }
181 
182  if(my_index == 0)
183  {
184  new_direction += mChemotacticStrength* unit_vector<double>(DIM,0);
185  }
186  else if(my_index == 1)
187  {
188  new_direction += mChemotacticStrength*-unit_vector<double>(DIM,0);
189  }
190  else if(my_index == 2)
191  {
192  new_direction += mChemotacticStrength*unit_vector<double>(DIM,1);
193  }
194  else if(my_index == 3)
195  {
196  new_direction += mChemotacticStrength*-unit_vector<double>(DIM,1);
197  }
198  else if(my_index == 4)
199  {
200  new_direction += mChemotacticStrength*unit_vector<double>(DIM,2);
201  }
202  else if(my_index == 5)
203  {
204  new_direction += mChemotacticStrength*-unit_vector<double>(DIM,2);
205  }
206  }
207  new_direction /= norm_2(new_direction);
208 
209  // Mutual Attraction
210  std::vector<boost::shared_ptr<VesselNode<DIM> > > nodes = this->mpVesselNetwork->GetNodes();
211  units::quantity<unit::length> min_distance = 1.e12*unit::metres;
212  c_vector<double, DIM> min_direction = zero_vector<double>(DIM);
213  units::quantity<unit::length> reference_length = currentDirection.GetReferenceLengthScale();
214  for(unsigned jdx=0; jdx<nodes.size(); jdx++)
215  {
216  if(IsPointInCone<DIM>(nodes[jdx]->rGetLocation(), rNodes[idx]->rGetLocation(), rNodes[idx]->rGetLocation() +
217  DimensionalChastePoint<DIM>(currentDirection.GetLocation(reference_length) * double(mCriticalMutualAttractionLength/reference_length), reference_length), M_PI/3.0))
218  {
219  units::quantity<unit::length> distance = rNodes[idx]->rGetLocation().GetDistance(nodes[jdx]->rGetLocation());
220  if(distance < min_distance)
221  {
222  min_distance = distance;
223  DimensionalChastePoint<DIM> dim_min_direction = nodes[jdx]->rGetLocation() - rNodes[idx]->rGetLocation();
224  min_direction = dim_min_direction.GetUnitVector();
225  }
226  }
227  }
228 
229  double strength = 0.0;
230  if(min_distance < mCriticalMutualAttractionLength)
231  {
232  strength = mAttractionStrength * (1.0 - (min_distance * min_distance) / (mCriticalMutualAttractionLength * mCriticalMutualAttractionLength));
233  }
234  new_direction += strength * min_direction;
235  new_direction /= norm_2(new_direction);
236 
237  // Get the movement increment
238  units::quantity<unit::time> time_increment = SimulationTime::Instance()->GetTimeStep()*BaseUnits::Instance()->GetReferenceTimeScale();
239  units::quantity<unit::length> increment_length = time_increment* mVelocity;
240  movement_vectors.push_back(OffsetAlongVector<DIM>(new_direction, increment_length, reference_length));
241  }
242  return movement_vectors;
243  }
244 }
245 
246 template<unsigned DIM>
247 std::vector<DimensionalChastePoint<DIM> > OffLatticeMigrationRule<DIM>::GetDirectionsForSprouts(const std::vector<boost::shared_ptr<VesselNode<DIM> > >& rNodes)
248 {
249  std::vector<DimensionalChastePoint<DIM> > movement_vectors;
250  units::quantity<unit::length> reference_length = BaseUnits::Instance()->GetReferenceLengthScale();
251 
252  // Collect the probe locations for each node
253  std::vector<units::quantity<unit::concentration> > probed_solutions;
254  unsigned probes_per_node = 3;
255  if(DIM==3)
256  {
257  probes_per_node = 5;
258  }
259  std::vector<DimensionalChastePoint<DIM> > probe_locations(probes_per_node*rNodes.size(), DimensionalChastePoint<DIM>(0.0, 0.0, 0.0, reference_length));
260  std::vector<bool> candidate_locations_inside_domain(probes_per_node*rNodes.size(), true);
261 
262  // Get a normal to the segments, will depend on whether they are parallel
263  for(unsigned idx = 0; idx < rNodes.size(); idx++)
264  {
265  c_vector<double, DIM> sprout_direction;
266  c_vector<double, DIM> cross_product;
267  double sum = 0.0;
268 
269  if(DIM==3)
270  {
271  cross_product = VectorProduct(rNodes[idx]->GetSegments()[0]->GetUnitTangent(),
272  rNodes[idx]->GetSegments()[1]->GetUnitTangent());
273  for(unsigned jdx=0; jdx<DIM; jdx++)
274  {
275  sum += abs(cross_product[jdx]);
276  }
277  }
278  else
279  {
280  c_vector<double, DIM> tangent1 = rNodes[idx]->GetSegments()[0]->GetUnitTangent();
281  c_vector<double, DIM> tangent2 = rNodes[idx]->GetSegments()[1]->GetUnitTangent();
282  for(unsigned jdx=0; jdx<DIM; jdx++)
283  {
284  sum += abs(tangent1[jdx]-tangent2[jdx]);
285  }
286  }
287 
288  if (sum<=1.e-6)
289  {
290  // more or less parallel segments, chose any normal to the first tangent
291  c_vector<double, DIM> normal;
292  c_vector<double, DIM> tangent = rNodes[idx]->GetSegments()[0]->GetUnitTangent();
293  if(DIM==2 or tangent[2]==0.0)
294  {
295  if(tangent[1] == 0.0)
296  {
297  normal[0] = 0.0;
298  normal[1] = 1.0;
299  }
300  else
301  {
302  normal[0] = 1.0;
303  normal[1] = -tangent[0] /tangent[1];
304  }
305  }
306  else
307  {
308  if(std::abs(tangent[0]) + std::abs(tangent[1]) == 0.0)
309  {
310  normal[0] = 1.0;
311  normal[1] = 1.0;
312  }
313  else
314  {
315  normal[0] = 1.0;
316  normal[1] = 1.0;
317  normal[2] = -(tangent[0] + tangent[1])/tangent[2];
318  }
319  }
320  if(RandomNumberGenerator::Instance()->ranf()>=0.5)
321  {
322  sprout_direction = normal/norm_2(normal);
323  }
324  else
325  {
326  sprout_direction = -normal/norm_2(normal);
327  }
328  }
329  else
330  {
331  if(DIM==3)
332  {
333  // otherwise the direction is out of the plane of the segment tangents
334  if(RandomNumberGenerator::Instance()->ranf()>=0.5)
335  {
336  sprout_direction = cross_product/norm_2(cross_product);
337  }
338  else
339  {
340  sprout_direction = -cross_product/norm_2(cross_product);
341  }
342  }
343  else
344  {
345  c_vector<double, DIM> tangent1 = rNodes[idx]->GetSegments()[0]->GetUnitTangent();
346  c_vector<double, DIM> tangent2 = rNodes[idx]->GetSegments()[1]->GetUnitTangent();
347  c_vector<double, DIM> av_tangent = (tangent1 + tangent2)/2.0;
348  av_tangent/=norm_2(av_tangent);
349 
350  c_vector<double, DIM> normal;
351  if(av_tangent[1] == 0.0)
352  {
353  normal[0] = 0.0;
354  normal[1] = 1.0;
355  }
356  else
357  {
358  normal[0] = 1.0;
359  normal[1] = -av_tangent[0] /av_tangent[1];
360  }
361 
362  if(RandomNumberGenerator::Instance()->ranf()>=0.5)
363  {
364  sprout_direction = normal/norm_2(normal);
365  }
366  else
367  {
368  sprout_direction = -normal/norm_2(normal);
369  }
370  }
371  }
372 
373  units::quantity<unit::plane_angle> angle = RandomNumberGenerator::Instance()->NormalRandomDeviate(mMeanAngles[0]/unit::radians,
374  mSdvAngles[0]/unit::radians)*unit::radians;
375  std::vector<DimensionalChastePoint<DIM> > local_probes = GetProbeLocationsInternalPoint<DIM>(DimensionalChastePoint<DIM>(sprout_direction, reference_length),
376  rNodes[idx]->rGetLocation(),
377  DimensionalChastePoint<DIM>(rNodes[idx]->GetSegments()[0]->GetUnitTangent(), reference_length),
378  mProbeLength,
379  angle);
380  for(unsigned jdx=0;jdx<probes_per_node; jdx++)
381  {
382  probe_locations[idx*probes_per_node + jdx] = local_probes[jdx];
383  }
384  }
385 
386  if(this->mpSolver)
387  {
388  if(probe_locations.size()>0)
389  {
390  probed_solutions = this->mpSolver->GetConcentrations(probe_locations);
391  }
392 
393  if (this->mpBoundingDomain)
394  {
395  candidate_locations_inside_domain = this->mpBoundingDomain->IsPointInPart(probe_locations);
396  }
397  }
398 
399  // Decide on the sprout directions
400  for(unsigned idx=0; idx<rNodes.size(); idx++)
401  {
402  DimensionalChastePoint<DIM> new_direction(0.0, 0.0, 0.0, reference_length);
403 
404  // Solution dependent contribution
405  if(this->mpSolver)
406  {
407  // Get the gradients
408  std::vector<units::quantity<unit::concentration_gradient> > gradients(probes_per_node-1, 0.0*unit::mole_per_metre_pow_4);
409  if(probed_solutions.size()>=idx*probes_per_node+probes_per_node)
410  {
411  for(unsigned jdx=1; jdx<probes_per_node; jdx++)
412  {
413  gradients[jdx-1] = ((probed_solutions[idx*probes_per_node+jdx] - probed_solutions[idx*probes_per_node]) / mProbeLength);
414  if(!candidate_locations_inside_domain[idx*probes_per_node+jdx])
415  {
416  gradients[jdx-1] = 0.0*unit::mole_per_metre_pow_4;
417  }
418  }
419  }
420 
421  // Get the index of the max viable gradient
422  units::quantity<unit::concentration_gradient> max_grad = 0.0 * unit::mole_per_metre_pow_4;
423  int my_index = -1;
424  for(unsigned jdx = 0; jdx<gradients.size(); jdx++)
425  {
426  if(gradients[jdx]>max_grad)
427  {
428  max_grad = gradients[jdx];
429  my_index = int(jdx);
430  }
431  }
432  if(my_index == 0)
433  {
434  new_direction = probe_locations[idx*probes_per_node+1]-rNodes[idx]->rGetLocation();
435  }
436  else if(my_index == 1)
437  {
438  new_direction = probe_locations[idx*probes_per_node+2]-rNodes[idx]->rGetLocation();
439  }
440  else if(my_index == 2)
441  {
442  new_direction = probe_locations[idx*probes_per_node+3]-rNodes[idx]->rGetLocation();
443  }
444  else if(my_index == 3)
445  {
446  new_direction = probe_locations[idx*probes_per_node+4]-rNodes[idx]->rGetLocation();
447  }
448  }
449 
450  units::quantity<unit::time> time_increment = SimulationTime::Instance()->GetTimeStep()*BaseUnits::Instance()->GetReferenceTimeScale();
451  units::quantity<unit::length> increment_length = time_increment* mVelocity;
452  movement_vectors.push_back(OffsetAlongVector<DIM>(new_direction, increment_length));
453  }
454 
455  return movement_vectors;
456 }
457 
458 // Explicit instantiation
459 template class OffLatticeMigrationRule<2> ;
460 template class OffLatticeMigrationRule<3> ;
units::quantity< unit::length > mCriticalMutualAttractionLength
units::quantity< unit::velocity > mVelocity
units::quantity< unit::length > GetReferenceLengthScale() const
c_vector< double, 3 > mGlobalZ
std::vector< units::quantity< unit::plane_angle > > mMeanAngles
boost::shared_ptr< AbstractDiscreteContinuumSolver< DIM > > mpSolver
c_vector< double, 3 > mGlobalX
units::quantity< unit::time > GetReferenceTimeScale()
Definition: BaseUnits.cpp:72
units::quantity< unit::length > mProbeLength
void SetChemotacticStrength(double strength)
static boost::shared_ptr< OffLatticeMigrationRule< DIM > > Create()
std::vector< DimensionalChastePoint< DIM > > GetDirectionsForSprouts(const std::vector< boost::shared_ptr< VesselNode< DIM > > > &rNodes)
c_vector< double, DIM > GetUnitVector() const
c_vector< double, 3 > mGlobalY
c_vector< double, DIM > GetLocation(units::quantity< unit::length > scale)
boost::shared_ptr< Part< DIM > > mpBoundingDomain
static BaseUnits * Instance()
Definition: BaseUnits.cpp:53
std::vector< DimensionalChastePoint< DIM > > GetDirections(const std::vector< boost::shared_ptr< VesselNode< DIM > > > &rNodes)
std::vector< units::quantity< unit::plane_angle > > mSdvAngles
boost::shared_ptr< VesselNetwork< DIM > > mpVesselNetwork
void SetSproutingVelocity(units::quantity< unit::velocity > velocity)
void SetAttractionStrength(double strength)
units::quantity< unit::length > GetReferenceLengthScale()
Definition: BaseUnits.cpp:82