Chaste  Build::
BetteridgeHaematocritSolver.cpp
1 //
3 //Copyright (c) 2005-2015, 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 unsignedERRUPTION)
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 #include "BetteridgeHaematocritSolver.hpp"
35 #include "LinearSystem.hpp"
36 #include "VesselNode.hpp"
37 #include "Vessel.hpp"
38 #include "Exception.hpp"
39 #include "ReplicatableVector.hpp"
40 
41 template<unsigned DIM>
43  mTHR(2.5),
44  mAlpha(0.5),
45  mHaematocrit(0.45)
46 {
47 
48 }
49 
50 template<unsigned DIM>
52 {
53 
54 }
55 
56 template<unsigned DIM>
57 void BetteridgeHaematocritSolver<DIM>::SetTHR(units::quantity<unit::dimensionless> THR)
58 {
59  mTHR = THR;
60 }
61 
62 template<unsigned DIM>
63 void BetteridgeHaematocritSolver<DIM>::SetAlpha(units::quantity<unit::dimensionless> Alpha)
64 {
65  mAlpha = Alpha;
66 }
67 
68 template<unsigned DIM>
69 void BetteridgeHaematocritSolver<DIM>::SetHaematocrit(units::quantity<unit::dimensionless> haematocrit)
70 {
71  mHaematocrit = haematocrit;
72 }
73 
74 template<unsigned DIM>
76 {
77  // Give the vessels unique Ids
78  std::vector<boost::shared_ptr<Vessel<DIM> > > vessels = this->mpNetwork->GetVessels();
79  for(unsigned idx=0; idx<vessels.size(); idx++)
80  {
81  vessels[idx]->SetId(idx);
82  }
83 
84  // Set up the linear system
85  PetscInt lhsVectorSize = vessels.size();
86  unsigned max_vessels_per_branch = 5;
87  if(vessels.size() < max_vessels_per_branch)
88  {
89  max_vessels_per_branch = unsigned(lhsVectorSize);
90  }
91  LinearSystem linearSystem(lhsVectorSize, max_vessels_per_branch);
92  if(lhsVectorSize > 6)
93  {
94  linearSystem.SetPcType("lu");
95  #ifdef PETSC_HAVE_HYPRE
96  linearSystem.SetPcType("hypre");
97  #endif //PETSC_HAVE_HYPRE
98  linearSystem.SetKspType("preonly");
99  }
100 
101  std::vector<std::vector<unsigned> > update_indices;
102  for(unsigned idx=0; idx<vessels.size(); idx++)
103  {
104  // Always have a diagonal entry for system, this sets zero haematocrit by default
105  linearSystem.SetMatrixElement(idx, idx, 1);
106  if(vessels[idx]->GetStartNode()->GetFlowProperties()->IsInputNode() or vessels[idx]->GetEndNode()->GetFlowProperties()->IsInputNode())
107  {
108  linearSystem.SetRhsVectorElement(idx, mHaematocrit);
109  }
110  // Set rhs to zero, it should already be zero but this explicitly captures the no flow case
111  else if(vessels[idx]->GetFlowProperties()->GetFlowRate()==0.0*unit::metre_cubed_per_second)
112  {
113  linearSystem.SetRhsVectorElement(idx, 0.0);
114  }
115  else
116  {
117  // Identify inflow node
118  boost::shared_ptr<VesselNode<DIM> > p_inflow_node;
119  units::quantity<unit::flow_rate> flow_rate= vessels[idx]->GetFlowProperties()->GetFlowRate();
120  if(flow_rate >0 * unit::metre_cubed_per_second)
121  {
122  p_inflow_node = vessels[idx]->GetStartNode();
123  }
124  else
125  {
126  p_inflow_node = vessels[idx]->GetEndNode();
127  }
128 
129  // Identify number of inflow and outflow vessels
130  if(p_inflow_node->GetNumberOfSegments()>1)
131  {
132  std::vector<boost::shared_ptr<Vessel<DIM> > > parent_vessels;
133  std::vector<boost::shared_ptr<Vessel<DIM> > > competitor_vessels;
134  for(unsigned jdx=0; jdx<p_inflow_node->GetSegments().size(); jdx++)
135  {
136  // if not this vessel
137  if(p_inflow_node->GetSegment(jdx)->GetVessel()!=vessels[idx])
138  {
139  units::quantity<unit::flow_rate> inflow_rate = p_inflow_node->GetSegment(jdx)->GetVessel()->GetFlowProperties()->GetFlowRate();
140  if(p_inflow_node->GetSegment(jdx)->GetVessel()->GetEndNode()==p_inflow_node)
141  {
142  if(inflow_rate>0.0 * unit::metre_cubed_per_second)
143  {
144  parent_vessels.push_back(p_inflow_node->GetSegment(jdx)->GetVessel());
145  }
146  else if(inflow_rate<0.0 * unit::metre_cubed_per_second)
147  {
148  competitor_vessels.push_back(p_inflow_node->GetSegment(jdx)->GetVessel());
149  }
150  }
151  if(p_inflow_node->GetSegment(jdx)->GetVessel()->GetStartNode()==p_inflow_node)
152  {
153  if(inflow_rate>0.0 * unit::metre_cubed_per_second)
154  {
155  competitor_vessels.push_back(p_inflow_node->GetSegment(jdx)->GetVessel());
156  }
157  else if(inflow_rate<0.0 * unit::metre_cubed_per_second)
158  {
159  parent_vessels.push_back(p_inflow_node->GetSegment(jdx)->GetVessel());
160  }
161  }
162  }
163  }
164 
165  // If there are no competitor vessels the haematocrit is just the sum of the parent values
166  if(competitor_vessels.size()==0 or units::fabs(competitor_vessels[0]->GetFlowProperties()->GetFlowRate()) == 0.0 * unit::metre_cubed_per_second)
167  {
168  for(unsigned jdx=0; jdx<parent_vessels.size();jdx++)
169  {
170  linearSystem.SetMatrixElement(idx, parent_vessels[jdx]->GetId(), -fabs(parent_vessels[jdx]->GetFlowProperties()->GetFlowRate()/flow_rate));
171  }
172  }
173  else
174  {
175  if(competitor_vessels.size()>1 or parent_vessels.size()>1)
176  {
177  EXCEPTION("This solver can only work with branches with connectivity 3");
178  }
179  units::quantity<unit::flow_rate> competitor0_flow_rate = competitor_vessels[0]->GetFlowProperties()->GetFlowRate();
180  units::quantity<unit::flow_rate> parent0_flow_rate = parent_vessels[0]->GetFlowProperties()->GetFlowRate();
181 
182  // There is a bifurcation, apply a haematocrit splitting rule
183  units::quantity<unit::length> my_radius = vessels[idx]->GetRadius();
184  units::quantity<unit::length> competitor_radius = competitor_vessels[0]->GetRadius();
185  units::quantity<unit::velocity> my_velocity = units::fabs(flow_rate)/(M_PI * my_radius * my_radius);
186  units::quantity<unit::velocity> competitor_velocity = fabs(competitor0_flow_rate)/(M_PI * competitor_radius * competitor_radius);
187 
188  units::quantity<unit::dimensionless> alpha = 1.0 - parent_vessels[0]->GetFlowProperties()->GetHaematocrit();
189  units::quantity<unit::dimensionless> flow_ratio_pm = fabs(parent0_flow_rate)/fabs(flow_rate);
190  units::quantity<unit::dimensionless> flow_ratio_cm = fabs(competitor0_flow_rate)/fabs(flow_rate);
191  double numer = flow_ratio_pm;
192 
193  // Apply fungs rule to faster vessel
194  if(my_velocity >= competitor_velocity)
195  {
196  units::quantity<unit::dimensionless> term = alpha * (my_velocity/competitor_velocity-1.0);
197  double denom = 1.0+flow_ratio_cm*(1.0/(1.0+term));
198  linearSystem.SetMatrixElement(idx, parent_vessels[0]->GetId(), -numer/denom);
199  }
200  else
201  {
202  units::quantity<unit::dimensionless> term = alpha * (competitor_velocity/my_velocity-1.0);
203  double denom = 1.0+flow_ratio_cm*(1.0+term);
204  linearSystem.SetMatrixElement(idx, parent_vessels[0]->GetId(), -numer/denom);
205  }
206 
207  // Save the indices for later updating
208  std::vector<unsigned> local_update_indics = std::vector<unsigned>(3);
209  local_update_indics[0] = idx;
210  local_update_indics[1] = parent_vessels[0]->GetId();
211  local_update_indics[2] = competitor_vessels[0]->GetId();
212  update_indices.push_back(local_update_indics);
213  }
214  }
215  }
216  }
217 
218  // Set the parameters for iteration
219  double tolerance = 1e-3;
220  double max_iterations = 1000;
221  double residual = DBL_MAX;
222  int iterations = 0;
223 
224  while(residual > tolerance && iterations < max_iterations)
225  {
226  if(iterations>0 and update_indices.size()>0)
227  {
228  // Update the system
229  linearSystem.SwitchWriteModeLhsMatrix();
230  for(unsigned idx=0; idx<update_indices.size();idx++)
231  {
232  units::quantity<unit::flow_rate> self_flow_rate = vessels[update_indices[idx][0]]->GetFlowProperties()->GetFlowRate();
233  units::quantity<unit::flow_rate> competitor0_flow_rate = vessels[update_indices[idx][2]]->GetFlowProperties()->GetFlowRate();
234  units::quantity<unit::flow_rate> parent0_flow_rate = vessels[update_indices[idx][1]]->GetFlowProperties()->GetFlowRate();
235 
236  units::quantity<unit::length> my_radius = vessels[update_indices[idx][0]]->GetRadius();
237  units::quantity<unit::length> competitor_radius = vessels[update_indices[idx][2]]->GetRadius();
238  units::quantity<unit::velocity> my_velocity = fabs(self_flow_rate)/(M_PI * my_radius * my_radius);
239  units::quantity<unit::velocity> competitor_velocity = fabs(competitor0_flow_rate)/(M_PI * competitor_radius * competitor_radius);
240  units::quantity<unit::dimensionless> alpha = 1.0 - vessels[update_indices[idx][1]]->GetFlowProperties()->GetHaematocrit();
241 
242  double flow_ratio_pm = fabs(parent0_flow_rate/self_flow_rate);
243  double flow_ratio_cm = fabs(competitor0_flow_rate/self_flow_rate);
244  double numer = flow_ratio_pm;
245 
246  // Apply fungs rule to faster vessel
247  if(my_velocity >= competitor_velocity)
248  {
249  double term = alpha * (my_velocity/competitor_velocity-1.0);
250  double denom = 1.0+flow_ratio_cm*(1.0/(1.0+term));
251  linearSystem.SetMatrixElement(update_indices[idx][0], update_indices[idx][1], -numer/denom);
252  }
253  else
254  {
255  double term = alpha * (competitor_velocity/my_velocity-1.0);
256  double denom = 1.0+flow_ratio_cm*(1.0+term);
257  linearSystem.SetMatrixElement(update_indices[idx][0], update_indices[idx][1], -numer/denom);
258  }
259  }
260  }
261 
262  Vec solution = PetscTools::CreateVec(vessels.size());
263  linearSystem.AssembleFinalLinearSystem();
264  solution = linearSystem.Solve();
265  ReplicatableVector a(solution);
266 
267  // Get the residual
268  residual = 0.0;
269  for (unsigned i = 0; i < vessels.size(); i++)
270  {
271  if(fabs(vessels[i]->GetFlowProperties()->GetHaematocrit() - a[i]) > residual)
272  {
273  residual = fabs(vessels[i]->GetFlowProperties()->GetHaematocrit() - a[i]);
274  }
275  }
276 
277  // assign haematocrit levels to vessels
278  for (unsigned idx = 0; idx < vessels.size(); idx++)
279  {
280  for (unsigned jdx = 0; jdx < vessels[idx]->GetNumberOfSegments(); jdx++)
281  {
282  vessels[idx]->GetSegments()[jdx]->GetFlowProperties()->SetHaematocrit(a[idx]);
283  }
284  }
285 
286  iterations++;
287  if(iterations == max_iterations)
288  {
289  EXCEPTION("Haematocrit calculation failed to converge.");
290  }
291 
292  PetscTools::Destroy(solution);
293  }
294 }
295 // Explicit instantiation
296 template class BetteridgeHaematocritSolver<2>;
297 template class BetteridgeHaematocritSolver<3>;
void SetHaematocrit(units::quantity< unit::dimensionless > haematocrit)
boost::shared_ptr< VesselNetwork< DIM > > mpNetwork
units::quantity< unit::dimensionless > mTHR
units::quantity< unit::dimensionless > mHaematocrit
void SetAlpha(units::quantity< unit::dimensionless > alpha)
units::quantity< unit::dimensionless > mAlpha
void SetTHR(units::quantity< unit::dimensionless > thr)