Chaste Commit::43b116bb45f11066c455b15e05c77f8bbe23ac85
ParabolicBoxDomainPdeModifier.cpp
1/*
2
3Copyright (c) 2005-2025, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, 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
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "ParabolicBoxDomainPdeModifier.hpp"
37#include "SimpleLinearParabolicSolver.hpp"
38#include "VtkMeshWriter.hpp"
39#include "MutableMesh.hpp"
40
41template<unsigned DIM>
43 boost::shared_ptr<AbstractBoundaryCondition<DIM> > pBoundaryCondition,
44 bool isNeumannBoundaryCondition,
45 boost::shared_ptr<ChasteCuboid<DIM> > pMeshCuboid,
46 double stepSize,
47 Vec solution)
49 pBoundaryCondition,
50 isNeumannBoundaryCondition,
51 pMeshCuboid,
52 stepSize,
53 solution),
54 mMoveSolutionWithCells(false)
55{
56}
57
58template<unsigned DIM>
60{
61 // Set up boundary conditions
62 std::shared_ptr<BoundaryConditionsContainer<DIM,DIM,1> > p_bcc = ConstructBoundaryConditionsContainer(rCellPopulation);
63
64 this->UpdateCellPdeElementMap(rCellPopulation);
65
66 // When using a PDE mesh which doesn't coincide with the cells, we must set up the source terms before solving the PDE.
67 // Pass in already updated CellPdeElementMap to speed up finding cells.
68 this->SetUpSourceTermsForAveragedSourcePde(this->mpFeMesh, &this->mCellPdeElementMap);
69
70 // Use SimpleLinearParabolicSolver as averaged Source PDE
71 SimpleLinearParabolicSolver<DIM,DIM> solver(this->mpFeMesh,
72 boost::static_pointer_cast<AbstractLinearParabolicPde<DIM,DIM> >(this->GetPde()).get(),
73 p_bcc.get());
74
76 SimulationTime* p_simulation_time = SimulationTime::Instance();
77 double current_time = p_simulation_time->GetTime();
78 double dt = p_simulation_time->GetTimeStep();
79 solver.SetTimes(current_time,current_time + dt);
80 solver.SetTimeStep(dt);
81
82
83 // Use previous solution as the initial condition
84 Vec previous_solution = this->mSolution;
85 if (mMoveSolutionWithCells)
86 {
87 // interpolate solution from cells movement onto fe mesh
88 previous_solution = InterpolateSolutionFromCellMovement(rCellPopulation);
89 }
90 solver.SetInitialCondition(previous_solution);
91
92 // Note that the linear solver creates a vector, so we have to keep a handle on the old one
93 // in order to destroy it
94 this->mSolution = solver.Solve();
95 PetscTools::Destroy(previous_solution);
96
97 // Now copy solution to cells
98 this->UpdateCellData(rCellPopulation);
99
100 // Finally, if needed store the locations of cells to be used as old locations in the next timestep
101 if (mMoveSolutionWithCells)
102 {
103 /*
104 * If required, store the current locations of cell centres. Note that we need to
105 * use a std::map between cells and locations, rather than (say) a std::vector with
106 * location indices corresponding to cells, since once we call UpdateCellLocations()
107 * the location index of each cell may change. This is especially true in the case
108 * of a CaBasedCellPopulation.
109 */
110 mOldCellLocations.clear();
111 for (typename AbstractCellPopulation<DIM, DIM>::Iterator cell_iter = rCellPopulation.Begin();
112 cell_iter != rCellPopulation.End();
113 ++cell_iter)
114 {
115 mOldCellLocations[*cell_iter] = rCellPopulation.GetLocationOfCellCentre(*cell_iter);
116 }
117 }
118}
119
120template<unsigned DIM>
122{
123 AbstractBoxDomainPdeModifier<DIM>::SetupSolve(rCellPopulation,outputDirectory);
124
125 // Copy the cell data to mSolution (this is the initial condition)
126 SetupInitialSolutionVector(rCellPopulation);
127
128 // Output the initial conditions on FeMesh
129 this->UpdateAtEndOfOutputTimeStep(rCellPopulation);
130
131 // If needed store the locations of cells to be used as old locations at the next timestep.
132 if (mMoveSolutionWithCells)
133 {
134 /*
135 * If required, store the current locations of cell centres. Note that we need to
136 * use a std::map between cells and locations, rather than (say) a std::vector with
137 * location indices corresponding to cells, since once we call UpdateCellLocations()
138 * the location index of each cell may change. This is especially true in the case
139 * of a CaBasedCellPopulation.
140 */
141 mOldCellLocations.clear();
142 for (typename AbstractCellPopulation<DIM, DIM>::Iterator cell_iter = rCellPopulation.Begin();
143 cell_iter != rCellPopulation.End();
144 ++cell_iter)
145 {
146 mOldCellLocations[*cell_iter] = rCellPopulation.GetLocationOfCellCentre(*cell_iter);
147 }
148 }
149}
150
151template<unsigned DIM>
152std::shared_ptr<BoundaryConditionsContainer<DIM,DIM,1> > ParabolicBoxDomainPdeModifier<DIM>::ConstructBoundaryConditionsContainer(AbstractCellPopulation<DIM,DIM>& rCellPopulation)
153{
154 std::shared_ptr<BoundaryConditionsContainer<DIM,DIM,1> > p_bcc(new BoundaryConditionsContainer<DIM,DIM,1>(false));
155
156 this->ConstructBoundaryConditionsContainerHelper(rCellPopulation,p_bcc);
157
158 return p_bcc;
159}
160
161template<unsigned DIM>
163{
164 // Specify homogeneous initial conditions based upon the values stored in CellData.
165 // Note need all the CellDataValues to be the same.
166
167 double initial_condition = rCellPopulation.Begin()->GetCellData()->GetItem(this->mDependentVariableName);
168
169 for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = rCellPopulation.Begin();
170 cell_iter != rCellPopulation.End();
171 ++cell_iter)
172 {
173 double initial_condition_at_cell = cell_iter->GetCellData()->GetItem(this->mDependentVariableName);
174 UNUSED_OPT(initial_condition_at_cell);
175 assert(fabs(initial_condition_at_cell - initial_condition)<1e-12);
176 }
177
178 // Initialise mSolution
179 this->mSolution = PetscTools::CreateAndSetVec(this->mpFeMesh->GetNumNodes(), initial_condition);
180}
181
182template<unsigned DIM>
184{
185 // Store the PDE solution in an accessible form
186 ReplicatableVector solution_repl(this->mSolution);
187
188 Vec interpolated_solution = PetscTools::CreateAndSetVec(this->mpFeMesh->GetNumNodes(), 0.0);
189
190 // Store max radius so can speed up interpolation.
191 // TODO replace this with more general exclusion based on dimensions of tissue.
192 double max_radius = 0.0;
193 for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = rCellPopulation.Begin();
194 cell_iter != rCellPopulation.End();
195 ++cell_iter)
196 {
197 const ChastePoint<DIM>& r_position_of_cell = rCellPopulation.GetLocationOfCellCentre(*cell_iter);
198
199 double radius = norm_2(r_position_of_cell.rGetLocation());
200
201 if (max_radius < radius)
202 {
203 max_radius = radius;
204 }
205 }
206
207 // Create mesh from cell centres so can interpolate tissue velocity onto mpFeMesh
208 std::vector<Node<DIM>*> temp_nodes;
209 std::vector<c_vector<double,DIM>> cell_displacements;
210 unsigned cell_index = 0;
211 for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = rCellPopulation.Begin();
212 cell_iter != rCellPopulation.End();
213 ++cell_iter)
214 {
215 c_vector<double,DIM> position_of_cell = rCellPopulation.GetLocationOfCellCentre(*cell_iter);
216 cell_index++;
217
218 // Only use cells that were in both this and the previous timestep.
219 if (mOldCellLocations.find(*cell_iter) != mOldCellLocations.end())
220 {
221 //PRINT_VARIABLE(mOldCellLocations[*cell_iter]);
222 c_vector<double,DIM> displacement = position_of_cell - mOldCellLocations[*cell_iter];
223
224 cell_displacements.push_back(displacement);
225 temp_nodes.push_back(new Node<DIM>(cell_index, position_of_cell));
226 }
227 }
228 MutableMesh<DIM,DIM> cell_mesh(temp_nodes);
229
230 // Make the deformed mesh. Based on the displacement of cells.
231 auto* p_deformed_mesh = new TetrahedralMesh<DIM,DIM>();
232 this->GenerateAndReturnFeMesh(this->mpMeshCuboid,this->mStepSize,p_deformed_mesh);
233
234 for (typename TetrahedralMesh<DIM, DIM>::NodeIterator node_iter = p_deformed_mesh->GetNodeIteratorBegin();
235 node_iter != p_deformed_mesh->GetNodeIteratorEnd();
236 ++node_iter)
237 {
238 c_vector<double, DIM> node_location = node_iter->rGetLocation();
239
240
241 c_vector<double, DIM> new_node_location = node_location;
242
243 if (norm_2(node_location) <= max_radius)
244 {
245 // Find the element in the cell mesh that contains this node.
246 try
247 {
248 unsigned elem_index = cell_mesh.GetContainingElementIndex(node_location, false);
249
250 // Now do the interpolation
251 Element<DIM,DIM>* p_element = cell_mesh.GetElement(elem_index);
252 c_vector<double,DIM+1> weights = p_element->CalculateInterpolationWeights(node_location);
253
254 c_vector<double,DIM> interpolated_cell_displacement = zero_vector<double>(DIM);
255 for (unsigned i=0; i<DIM+1; i++)
256 {
257 const c_vector<double,DIM>& nodal_value = cell_displacements[p_element->GetNodeGlobalIndex(i)];
258 interpolated_cell_displacement += nodal_value * weights(i);
259 }
260 new_node_location = node_location + interpolated_cell_displacement;
261 node_iter->rGetModifiableLocation() = new_node_location;
262 }
263 catch (Exception&) // not_in_mesh
264 {
265 // Don't do anything as these FE nodes are outside the Cell mesh, and it's fine to do nothing.
266 }
267 }
268 }
269
270 // Loop over nodes of the mpFeMesh and get solution values from the deformed mesh
271 for (typename TetrahedralMesh<DIM,DIM>::NodeIterator node_iter = this->mpFeMesh->GetNodeIteratorBegin();
272 node_iter != this->mpFeMesh->GetNodeIteratorEnd();
273 ++node_iter)
274 {
275 unsigned node_index = node_iter->GetIndex();
276
277 const ChastePoint<DIM>& node_location = node_iter->rGetLocation();
278
279 // Find the element in the deformed mesh that contains this FE node.
280 std::set<unsigned> elements_to_check = node_iter->rGetContainingElementIndices();
281 try
282 {
283 unsigned elem_index = p_deformed_mesh->GetContainingElementIndex(node_location, false, elements_to_check);
284
285 // Now do the interpolation
286 Element<DIM,DIM>* p_element = p_deformed_mesh->GetElement(elem_index);
287 c_vector<double,DIM+1> weights;
288
289 weights = p_element->CalculateInterpolationWeights(node_location);
290
291 double solution_at_node = 0.0;
292 for (unsigned i=0; i<DIM+1; i++)
293 {
294 double nodal_value = solution_repl[p_element->GetNodeGlobalIndex(i)];
295 solution_at_node += nodal_value * weights(i);
296 }
297 PetscVecTools::SetElement(interpolated_solution, node_index, solution_at_node);
298 }
299 catch (Exception &e)
300 {
301 // Handy debug code to work out why the node is not in any element
302
303 // // output the cell mesh
304 // std::ostringstream time_string1;
305 // time_string1 << SimulationTime::Instance()->GetTimeStepsElapsed();
306 // std::string results_file1 = "cell_mesh_" + this->mDependentVariableName + "_" + time_string1.str();
307 // VtkMeshWriter<DIM, DIM>* p_vtk_mesh_writer1 = new VtkMeshWriter<DIM, DIM>(this->mOutputDirectory, results_file1, false);
308 // p_vtk_mesh_writer1->AddPointData(this->mDependentVariableName, cell_displacements);
309 // p_vtk_mesh_writer1->WriteFilesUsingMesh(cell_mesh);
310 // delete p_vtk_mesh_writer1;
311
312 // // output the deformed mesh
313 // std::ostringstream time_string;
314 // time_string << SimulationTime::Instance()->GetTimeStepsElapsed();
315 // std::string results_file = "deformed_mesh_" + this->mDependentVariableName + "_" + time_string.str();
316 // VtkMeshWriter<DIM, DIM>* p_vtk_mesh_writer = new VtkMeshWriter<DIM, DIM>(this->mOutputDirectory, results_file, false);
317 // p_vtk_mesh_writer->WriteFilesUsingMesh(*p_deformed_mesh);
318 // delete p_vtk_mesh_writer;
319
320 assert(0);
321 }
322 }
323
324 // Tidy Up
325 delete p_deformed_mesh;
326
327 return interpolated_solution;
328}
329
330template<unsigned DIM>
332{
333 mMoveSolutionWithCells = moveSolutionWithCells;
334}
335
336template<unsigned DIM>
338{
339 return mMoveSolutionWithCells;
340}
341
342template<unsigned DIM>
344{
345 // No parameters to output, so just call method on direct parent class
347}
348
349// Explicit instantiation
353
354// Serialization for Boost >= 1.36
#define UNUSED_OPT(var)
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
void OutputSimulationModifierParameters(out_stream &rParamsFile) override
void SetupSolve(AbstractCellPopulation< DIM, DIM > &rCellPopulation, std::string outputDirectory) override
virtual c_vector< double, SPACE_DIM > GetLocationOfCellCentre(CellPtr pCell)=0
void SetTimes(double tStart, double tEnd)
unsigned GetNodeGlobalIndex(unsigned localIndex) const
unsigned GetContainingElementIndex(const ChastePoint< SPACE_DIM > &rTestPoint, bool strict=false, std::set< unsigned > testElements=std::set< unsigned >(), bool onlyTryWithTestElements=false)
Element< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
c_vector< double, DIM > & rGetLocation()
c_vector< double, SPACE_DIM+1 > CalculateInterpolationWeights(const ChastePoint< SPACE_DIM > &rTestPoint)
Definition Element.cpp:224
Definition Node.hpp:59
void SetupInitialSolutionVector(AbstractCellPopulation< DIM, DIM > &rCellPopulation)
virtual std::shared_ptr< BoundaryConditionsContainer< DIM, DIM, 1 > > ConstructBoundaryConditionsContainer(AbstractCellPopulation< DIM, DIM > &rCellPopulation)
void UpdateAtEndOfTimeStep(AbstractCellPopulation< DIM, DIM > &rCellPopulation) override
void SetupSolve(AbstractCellPopulation< DIM, DIM > &rCellPopulation, std::string outputDirectory) override
ParabolicBoxDomainPdeModifier(boost::shared_ptr< AbstractLinearPde< DIM, DIM > > pPde=boost::shared_ptr< AbstractLinearPde< DIM, DIM > >(), boost::shared_ptr< AbstractBoundaryCondition< DIM > > pBoundaryCondition=boost::shared_ptr< AbstractBoundaryCondition< DIM > >(), bool isNeumannBoundaryCondition=true, boost::shared_ptr< ChasteCuboid< DIM > > pMeshCuboid=boost::shared_ptr< ChasteCuboid< DIM > >(), double stepSize=1.0, Vec solution=nullptr)
void OutputSimulationModifierParameters(out_stream &rParamsFile) override
void SetMoveSolutionWithCells(bool moveSolutionWithCells)
Vec InterpolateSolutionFromCellMovement(AbstractCellPopulation< DIM, DIM > &rCellPopulation)
static Vec CreateAndSetVec(int size, double value)
static void Destroy(Vec &rVec)
static void SetElement(Vec vector, PetscInt row, double value)
double GetTime() const
double GetTimeStep() const
static SimulationTime * Instance()