00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "VentilationProblem.hpp"
00037 #include "TrianglesMeshReader.hpp"
00038 #include "Warnings.hpp"
00039
00040
00041 VentilationProblem::VentilationProblem(const std::string& rMeshDirFilePath, unsigned rootIndex)
00042 : mOutletNodeIndex(rootIndex),
00043 mDynamicResistance(false),
00044 mRadiusOnEdge(false),
00045 mViscosity(1.92e-5),
00046 mDensity(1.51e-6),
00047 mFluxGivenAtInflow(false),
00048 mTerminalInteractionMatrix(NULL),
00049 mTerminalFluxChangeVector(NULL),
00050 mTerminalPressureChangeVector(NULL),
00051 mTerminalKspSolver(NULL)
00052 {
00053 TrianglesMeshReader<1,3> mesh_reader(rMeshDirFilePath);
00054 mMesh.ConstructFromMeshReader(mesh_reader);
00055
00056 if (mMesh.GetNode(mOutletNodeIndex)->IsBoundaryNode() == false)
00057 {
00058 EXCEPTION("Outlet node is not a boundary node");
00059 }
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070 double acinus_volume = 1.2e6/31000;
00071
00072 for (AbstractTetrahedralMesh<1,3>::BoundaryNodeIterator iter = mMesh.GetBoundaryNodeIteratorBegin();
00073 iter != mMesh.GetBoundaryNodeIteratorEnd();
00074 ++iter)
00075 {
00076 if ((*iter)->GetIndex() != mOutletNodeIndex)
00077 {
00079 Swan2012AcinarUnit* p_acinus = new Swan2012AcinarUnit;
00080
00081 p_acinus->SetStretchRatio(1.26);
00082 p_acinus->SetUndeformedVolume(acinus_volume);
00083 p_acinus->SetPleuralPressure(-0.49);
00084 p_acinus->SetAirwayPressure(0.0);
00085
00086
00087
00088 c_vector<double, 3> dummy;
00089 double length;
00090 unsigned edge_index = *( (*iter)->ContainingElementsBegin() );
00091 mMesh.GetWeightedDirectionForElement(edge_index, dummy, length);
00092
00093 double radius = (*iter)->rGetNodeAttributes()[0];
00094
00095 double resistance = 8.0*mViscosity*length/(M_PI*SmallPow(radius, 4));
00096 p_acinus->SetTerminalBronchioleResistance(resistance);
00097
00098 mAcinarUnits[(*iter)->GetIndex()] = p_acinus;
00099 }
00100 }
00101
00102 mFlux.resize(mMesh.GetNumElements());
00103 mPressure.resize(mMesh.GetNumNodes());
00104 }
00105
00106 VentilationProblem::~VentilationProblem()
00107 {
00108 for (unsigned i=0; i<mAcinarUnits.size(); i++)
00109 {
00110 delete mAcinarUnits[i];
00111 }
00112
00113
00114 if (mTerminalInteractionMatrix)
00115 {
00116 PetscTools::Destroy(mTerminalInteractionMatrix);
00117 PetscTools::Destroy(mTerminalFluxChangeVector);
00118 PetscTools::Destroy(mTerminalPressureChangeVector);
00119 KSPDestroy(PETSC_DESTROY_PARAM(mTerminalKspSolver));
00120 }
00121 }
00122 void VentilationProblem::SolveDirectFromFlux()
00123 {
00124
00125
00126
00127
00128
00129
00130
00131 for (unsigned node_index = mMesh.GetNumNodes() - 1; node_index > 0; --node_index)
00132 {
00133 Node<3>* p_node = mMesh.GetNode(node_index);
00134 if (p_node->IsBoundaryNode() == false)
00135 {
00136 Node<3>::ContainingElementIterator element_iterator = p_node->ContainingElementsBegin();
00137 unsigned parent_index = *element_iterator;
00138 ++element_iterator;
00139
00140 for (mFlux[parent_index]=0.0; element_iterator != p_node->ContainingElementsEnd(); ++element_iterator)
00141 {
00142 mFlux[parent_index] += mFlux[*element_iterator];
00143 }
00144 }
00145 }
00146
00147 for (AbstractTetrahedralMesh<1,3>::ElementIterator iter = mMesh.GetElementIteratorBegin();
00148 iter != mMesh.GetElementIteratorEnd();
00149 ++iter)
00150 {
00151
00152
00153
00154 double flux = mFlux[iter->GetIndex()];
00155 double resistance = CalculateResistance(*iter, mDynamicResistance, flux);
00156 unsigned pressure_index_parent = iter->GetNodeGlobalIndex(0);
00157 unsigned pressure_index_child = iter->GetNodeGlobalIndex(1);
00158 mPressure[pressure_index_child] = mPressure[pressure_index_parent] - resistance*flux;
00159 }
00160 }
00161
00162 void VentilationProblem::SetupIterativeSolver()
00163 {
00164 const unsigned num_non_zeroes = 100;
00165
00166 MatCreateSeqAIJ(PETSC_COMM_SELF, mMesh.GetNumBoundaryNodes()-1, mMesh.GetNumBoundaryNodes()-1, num_non_zeroes, NULL, &mTerminalInteractionMatrix);
00167 PetscMatTools::SetOption(mTerminalInteractionMatrix, MAT_SYMMETRIC);
00168 PetscMatTools::SetOption(mTerminalInteractionMatrix, MAT_SYMMETRY_ETERNAL);
00169
00170
00171
00172
00173 std::vector<std::set<unsigned> > descendant_nodes(mMesh.GetNumElements());
00174 unsigned terminal_index=0;
00175
00176 for (AbstractTetrahedralMesh<1,3>::BoundaryNodeIterator iter = mMesh.GetBoundaryNodeIteratorBegin();
00177 iter != mMesh.GetBoundaryNodeIteratorEnd();
00178 ++iter)
00179 {
00180 unsigned node_index = (*iter)->GetIndex();
00181 if (node_index != mOutletNodeIndex)
00182 {
00183 unsigned parent_index = *((*iter)->ContainingElementsBegin());
00184 mTerminalToNodeIndex[terminal_index] = node_index;
00185 mTerminalToEdgeIndex[terminal_index] = parent_index;
00186 descendant_nodes[parent_index].insert(terminal_index++);
00187 }
00188 }
00189
00190
00191 while (descendant_nodes[mOutletNodeIndex].size() != terminal_index)
00192 {
00193
00194 for (unsigned node_index = mMesh.GetNumNodes() - 1; node_index > 0; --node_index)
00195 {
00196 Node<3>* p_node = mMesh.GetNode(node_index);
00197 if (p_node->IsBoundaryNode() == false)
00198 {
00199 Node<3>::ContainingElementIterator element_iterator = p_node->ContainingElementsBegin();
00200 unsigned parent_index = *element_iterator;
00201 ++element_iterator;
00202 for (;element_iterator != p_node->ContainingElementsEnd(); ++element_iterator)
00203 {
00204 descendant_nodes[parent_index].insert(descendant_nodes[*element_iterator].begin(),descendant_nodes[*element_iterator].end());
00205 }
00206 }
00207 }
00208 }
00209
00210 for (AbstractTetrahedralMesh<1,3>::ElementIterator iter = mMesh.GetElementIteratorBegin();
00211 iter != mMesh.GetElementIteratorEnd();
00212 ++iter)
00213 {
00214 unsigned parent_index = iter->GetIndex();
00215 double parent_resistance = CalculateResistance(*iter);
00216 if (descendant_nodes[parent_index].size() <= num_non_zeroes)
00217 {
00218 std::vector<PetscInt> indices( descendant_nodes[parent_index].begin(), descendant_nodes[parent_index].end() );
00219 std::vector<double> resistance_to_add(indices.size()*indices.size(), parent_resistance);
00220
00221 MatSetValues(mTerminalInteractionMatrix,
00222 indices.size(), (PetscInt*) &indices[0],
00223 indices.size(), (PetscInt*) &indices[0], &resistance_to_add[0], ADD_VALUES);
00224 }
00226 }
00227 PetscMatTools::Finalise(mTerminalInteractionMatrix);
00228 assert( terminal_index == mMesh.GetNumBoundaryNodes()-1);
00229 VecCreateSeq(PETSC_COMM_SELF, terminal_index, &mTerminalFluxChangeVector);
00230 VecCreateSeq(PETSC_COMM_SELF, terminal_index, &mTerminalPressureChangeVector);
00231
00232 KSPCreate(PETSC_COMM_SELF, &mTerminalKspSolver);
00233 KSPSetOperators(mTerminalKspSolver, mTerminalInteractionMatrix, mTerminalInteractionMatrix, SAME_PRECONDITIONER);
00234 KSPSetFromOptions(mTerminalKspSolver);
00235 KSPSetUp(mTerminalKspSolver);
00236 }
00237 void VentilationProblem::SolveIterativelyFromPressure()
00238 {
00239 if (mTerminalInteractionMatrix == NULL)
00240 {
00241 SetupIterativeSolver();
00242 }
00243
00244
00245
00246 assert(mPressure[mOutletNodeIndex] == mPressureCondition[mOutletNodeIndex]);
00247
00248 unsigned max_iterations=1000;
00249 unsigned num_terminals = mMesh.GetNumBoundaryNodes()-1u;
00250 double pressure_tolerance = 1e-4;
00251 bool converged=false;
00252 double terminal_flux_correction = 0.0;
00253 double last_max_pressure_change;
00254 double last_norm_pressure_change;
00255 for (unsigned iteration = 0; iteration < max_iterations && converged==false; iteration++)
00256 {
00257 for (unsigned terminal=0; terminal<num_terminals; terminal++)
00258 {
00259 unsigned node_index = mTerminalToNodeIndex[terminal];
00260
00261 double delta_pressure = mPressure[node_index] - mPressureCondition[node_index];
00262
00263 if (iteration == 0)
00264 {
00265 delta_pressure += mPressureCondition[mOutletNodeIndex];
00266 }
00267 VecSetValue(mTerminalPressureChangeVector, terminal, delta_pressure, INSERT_VALUES);
00268 }
00269 double max_pressure_change, norm_pressure_change;
00270 PetscInt temp_index;
00271 VecMax(mTerminalPressureChangeVector, &temp_index, &last_max_pressure_change);
00272 VecNorm(mTerminalPressureChangeVector, NORM_2, &last_norm_pressure_change);
00273
00274 if (last_norm_pressure_change < pressure_tolerance)
00275 {
00276 converged = true;
00277 break;
00278 }
00279
00280 KSPSolve(mTerminalKspSolver, mTerminalPressureChangeVector, mTerminalFluxChangeVector);
00281 double* p_mTerminalFluxChangeVector;
00282 VecGetArray(mTerminalFluxChangeVector, &p_mTerminalFluxChangeVector);
00283
00284
00285
00286 for (unsigned terminal=0; terminal<num_terminals; terminal++)
00287 {
00288 double estimated_mTerminalFluxChangeVector=p_mTerminalFluxChangeVector[terminal];
00289 unsigned edge_index = mTerminalToEdgeIndex[terminal];
00290 mFlux[edge_index] += estimated_mTerminalFluxChangeVector;
00291 }
00292 SolveDirectFromFlux();
00293
00294
00295 for (unsigned terminal=0; terminal<num_terminals; terminal++)
00296 {
00297 unsigned node_index = mTerminalToNodeIndex[terminal];
00298
00299 double delta_pressure = mPressure[node_index] - mPressureCondition[node_index];
00300
00301 if (iteration == 0)
00302 {
00303 delta_pressure += mPressureCondition[mOutletNodeIndex];
00304 }
00305 VecSetValue(mTerminalPressureChangeVector, terminal, delta_pressure, INSERT_VALUES);
00306 }
00307 VecMax(mTerminalPressureChangeVector, &temp_index, &max_pressure_change);
00308 VecNorm(mTerminalPressureChangeVector, NORM_2, &norm_pressure_change);
00309 if (norm_pressure_change < pressure_tolerance)
00310 {
00311 converged = true;
00312 break;
00313 }
00314 bool rescale = true;
00315 if (max_pressure_change*last_max_pressure_change < 0.0)
00316 {
00317
00318
00319
00320
00321 terminal_flux_correction = last_norm_pressure_change / (last_norm_pressure_change + norm_pressure_change) - 1.0;
00322 }
00323 else if (last_norm_pressure_change > norm_pressure_change)
00324 {
00325
00326 rescale = false;
00327 }
00328 else
00329 {
00330
00331
00332 }
00333
00334 if (rescale)
00335 {
00336 for (unsigned terminal=0; terminal<num_terminals; terminal++)
00337 {
00338 double estimated_mTerminalFluxChangeVector=p_mTerminalFluxChangeVector[terminal];
00339 unsigned edge_index = mTerminalToEdgeIndex[terminal];
00340 mFlux[edge_index] += terminal_flux_correction*estimated_mTerminalFluxChangeVector;
00341 }
00342 SolveDirectFromFlux();
00343 }
00344 }
00345 if(!converged)
00346 {
00347 NEVER_REACHED;
00348 }
00349 }
00350
00351 double VentilationProblem::CalculateResistance(Element<1,3>& rElement, bool usePedley, double flux)
00352 {
00353
00354 double radius = 0.0;
00355 if (mRadiusOnEdge)
00356 {
00357 radius = rElement.GetAttribute();
00358 }
00359 else
00360 {
00361 radius = ( rElement.GetNode(0)->rGetNodeAttributes()[0] + rElement.GetNode(1)->rGetNodeAttributes()[0]) / 2.0;
00362 }
00363
00364 c_vector<double, 3> dummy;
00365 double length;
00366 mMesh.GetWeightedDirectionForElement(rElement.GetIndex(), dummy, length);
00367
00368 double resistance = 8.0*mViscosity*length/(M_PI*SmallPow(radius, 4));
00369 if ( usePedley )
00370 {
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390 double reynolds_number = fabs( 2.0 * mDensity * flux / (mViscosity * M_PI * radius) );
00391 double c = 1.85;
00392 double z = (c/4.0) * sqrt(reynolds_number * radius / length);
00393
00394 if (z > 1.0)
00395 {
00396 resistance *= z;
00397 }
00398 }
00399 return resistance;
00400 }
00401
00402 void VentilationProblem::SetOutflowPressure(double pressure)
00403 {
00404 SetPressureAtBoundaryNode(*(mMesh.GetNode(mOutletNodeIndex)), pressure);
00405 mPressure[mOutletNodeIndex] = pressure;
00406 }
00407
00408 void VentilationProblem::SetConstantInflowPressures(double pressure)
00409 {
00410 for (AbstractTetrahedralMesh<1,3>::BoundaryNodeIterator iter =mMesh.GetBoundaryNodeIteratorBegin();
00411 iter != mMesh.GetBoundaryNodeIteratorEnd();
00412 ++iter)
00413 {
00414 if ((*iter)->GetIndex() != mOutletNodeIndex)
00415 {
00416
00417 SetPressureAtBoundaryNode(*(*iter), pressure);
00418 }
00419 }
00420 }
00421
00422 void VentilationProblem::SetConstantInflowFluxes(double flux)
00423 {
00424 for (AbstractTetrahedralMesh<1,3>::BoundaryNodeIterator iter =mMesh.GetBoundaryNodeIteratorBegin();
00425 iter != mMesh.GetBoundaryNodeIteratorEnd();
00426 ++iter)
00427 {
00428 if ((*iter)->GetIndex() != mOutletNodeIndex)
00429 {
00430 SetFluxAtBoundaryNode(*(*iter), flux);
00431 }
00432 }
00433 }
00434
00435 void VentilationProblem::SetPressureAtBoundaryNode(const Node<3>& rNode, double pressure)
00436 {
00437 if (rNode.IsBoundaryNode() == false)
00438 {
00439 EXCEPTION("Boundary conditions cannot be set at internal nodes");
00440 }
00441 assert(mFluxGivenAtInflow == false);
00442
00443
00444 mPressureCondition[rNode.GetIndex()] = pressure;
00445 }
00446
00447 double VentilationProblem::GetPressureAtBoundaryNode(const Node<3>& rNode)
00448 {
00449
00450
00451
00452
00453
00454 return mPressure[rNode.GetIndex()];
00455 }
00456
00457 double VentilationProblem::GetFluxAtOutflow()
00458 {
00459 return mFlux[mOutletNodeIndex];
00460 }
00461
00462 void VentilationProblem::SetFluxAtBoundaryNode(const Node<3>& rNode, double flux)
00463 {
00464 if (rNode.IsBoundaryNode() == false)
00465 {
00466 EXCEPTION("Boundary conditions cannot be set at internal nodes");
00467 }
00468 mFluxGivenAtInflow = true;
00469
00470
00471 unsigned edge_index = *( rNode.ContainingElementsBegin() );
00472
00473
00474 mFlux[edge_index] = flux;
00475 }
00476
00477
00478
00479 void VentilationProblem::Solve()
00480 {
00481 if (mFluxGivenAtInflow)
00482 {
00483 SolveDirectFromFlux();
00484 }
00485 else
00486 {
00487 SolveIterativelyFromPressure();
00488 }
00489
00490 }
00491
00492
00493 void VentilationProblem::GetSolutionAsFluxesAndPressures(std::vector<double>& rFluxesOnEdges,
00494 std::vector<double>& rPressuresOnNodes)
00495 {
00496 rFluxesOnEdges = mFlux;
00497 rPressuresOnNodes = mPressure;
00498 }
00499
00500
00501 void VentilationProblem::SetRadiusOnEdge(bool isOnEdges)
00502 {
00503 mRadiusOnEdge = isOnEdges;
00504 }
00505
00506 TetrahedralMesh<1, 3>& VentilationProblem::rGetMesh()
00507 {
00508 return mMesh;
00509 }
00510
00511
00512 #ifdef CHASTE_VTK
00513
00514 void VentilationProblem::WriteVtk(const std::string& rDirName, const std::string& rFileBaseName)
00515 {
00516 VtkMeshWriter<1, 3> vtk_writer(rDirName, rFileBaseName, false);
00517 AddDataToVtk(vtk_writer, "");
00518 vtk_writer.WriteFilesUsingMesh(mMesh);
00519
00520 }
00521
00522 void VentilationProblem::AddDataToVtk(VtkMeshWriter<1, 3>& rVtkWriter,
00523 const std::string& rSuffix)
00524 {
00525 std::vector<double> pressures;
00526 std::vector<double> fluxes;
00527 GetSolutionAsFluxesAndPressures(fluxes, pressures);
00528 rVtkWriter.AddCellData("Flux"+rSuffix, fluxes);
00529 rVtkWriter.AddPointData("Pressure"+rSuffix, pressures);
00530
00531 std::vector<double> volumes(mMesh.GetNumNodes());
00532 std::vector<double> stretch_ratios(mMesh.GetNumNodes());
00533 for (AbstractTetrahedralMesh<1,3>::BoundaryNodeIterator iter = mMesh.GetBoundaryNodeIteratorBegin();
00534 iter != mMesh.GetBoundaryNodeIteratorEnd();
00535 ++iter)
00536 {
00537 if( (*iter)->GetIndex() != mOutletNodeIndex)
00538 {
00539 volumes[(*iter)->GetIndex()] = mAcinarUnits[(*iter)->GetIndex()]->GetVolume();
00540 stretch_ratios[(*iter)->GetIndex()] = mAcinarUnits[(*iter)->GetIndex()]->GetStretchRatio();
00541 }
00542 }
00543 rVtkWriter.AddPointData("Volume"+rSuffix, volumes);
00544 rVtkWriter.AddPointData("Stretch"+rSuffix, stretch_ratios);
00545 }
00546
00547 #endif // CHASTE_VTK
00548
00549
00550 void VentilationProblem::Solve(TimeStepper& rTimeStepper,
00551 void (*pBoundaryConditionFunction)(VentilationProblem*, TimeStepper& rTimeStepper, const Node<3>&),
00552 const std::string& rDirName, const std::string& rFileBaseName)
00553 {
00554 #ifdef CHASTE_VTK
00555 VtkMeshWriter<1, 3> vtk_writer(rDirName, rFileBaseName, false);
00556 #endif
00557
00558 bool first_step=true;
00559 while (!rTimeStepper.IsTimeAtEnd())
00560 {
00561 if (first_step)
00562 {
00563
00564 first_step = false;
00565 }
00566 else
00567 {
00568 rTimeStepper.AdvanceOneTimeStep();
00569 }
00570 for (AbstractTetrahedralMesh<1,3>::BoundaryNodeIterator iter = mMesh.GetBoundaryNodeIteratorBegin();
00571 iter != mMesh.GetBoundaryNodeIteratorEnd();
00572 ++iter )
00573 {
00574 if ((*iter)->GetIndex() != mOutletNodeIndex)
00575 {
00576
00577 pBoundaryConditionFunction(this, rTimeStepper, *(*iter));
00578 }
00579 }
00580
00581
00582 Solve();
00583
00584 std::ostringstream suffix_name;
00585 suffix_name << "_" << std::setw(6) << std::setfill('0') << rTimeStepper.GetTotalTimeStepsTaken();
00586 #ifdef CHASTE_VTK
00587 AddDataToVtk(vtk_writer, suffix_name.str());
00588 #endif
00589 }
00590
00591 #ifdef CHASTE_VTK
00592 vtk_writer.WriteFilesUsingMesh(mMesh);
00593 #endif
00594 }
00595
00596 void VentilationProblem::SolveProblemFromFile(const std::string& rInFilePath, const std::string& rOutFileDir, const std::string& rOutFileName)
00597 {
00598 std::ifstream file(FileFinder(rInFilePath).GetAbsolutePath().c_str(), std::ios::binary);
00599 if (!file.is_open())
00600 {
00601 EXCEPTION("Could not open file "+rInFilePath);
00602 }
00603 std::string key, unit;
00604 double value;
00605 while (!file.eof())
00606 {
00607 file >> key >> value >> unit;
00608 if (file.fail())
00609 {
00610 break;
00611 }
00612 if (key == "RHO_AIR")
00613 {
00614 SetDensity(value);
00615 }
00616 else if (key == "MU_AIR")
00617 {
00618 SetViscosity(value);
00619 }
00620 else if (key == "PRESSURE_OUT")
00621 {
00622 SetOutflowPressure(value);
00623 }
00624 else if (key == "PRESSURE_IN")
00625 {
00626 SetConstantInflowPressures(value);
00627 }
00628 else
00629 {
00630 WARNING("The key "+ key+ " is not recognised yet");
00631 }
00632 }
00633 Solve();
00634 #ifdef CHASTE_VTK
00635 WriteVtk(rOutFileDir, rOutFileName);
00636 #endif
00637 }
00638
00639
00640
00641
00642
00643