Chaste Commit::675f9facbe008c5eacb9006feaeb6423206579ea
GeneralisedLinearSpringForce.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 "GeneralisedLinearSpringForce.hpp"
37
38#include "AbstractCentreBasedCellPopulation.hpp"
39#include "MeshBasedCellPopulation.hpp"
40#include "NodeBasedCellPopulation.hpp"
41
42template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
44 : AbstractTwoBodyInteractionForce<ELEMENT_DIM,SPACE_DIM>(),
45 mMeinekeSpringStiffness(15.0), // denoted by mu in Meineke et al, 2001 (doi:10.1046/j.0960-7722.2001.00216.x)
46 mMeinekeDivisionRestingSpringLength(0.5),
47 mMeinekeSpringGrowthDuration(1.0)
48{
49 if (SPACE_DIM == 1)
50 {
52 }
53}
54
55template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
57 unsigned nodeBGlobalIndex,
59 bool isCloserThanRestLength)
60{
61 return 1.0;
62}
63
64template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
68
69template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
70c_vector<double, SPACE_DIM> GeneralisedLinearSpringForce<ELEMENT_DIM,SPACE_DIM>::CalculateForceBetweenNodes(unsigned nodeAGlobalIndex,
71 unsigned nodeBGlobalIndex,
73{
74 // We should only ever calculate the force between two distinct nodes
75 assert(nodeAGlobalIndex != nodeBGlobalIndex);
76
77 Node<SPACE_DIM>* p_node_a = rCellPopulation.GetNode(nodeAGlobalIndex);
78 Node<SPACE_DIM>* p_node_b = rCellPopulation.GetNode(nodeBGlobalIndex);
79
80 // Get the node locations
81 const c_vector<double, SPACE_DIM>& r_node_a_location = p_node_a->rGetLocation();
82 const c_vector<double, SPACE_DIM>& r_node_b_location = p_node_b->rGetLocation();
83
84 // Get the node radii for a NodeBasedCellPopulation
85 double node_a_radius = 0.0;
86 double node_b_radius = 0.0;
87
88 if (bool(dynamic_cast<NodeBasedCellPopulation<SPACE_DIM>*>(&rCellPopulation)))
89 {
90 node_a_radius = p_node_a->GetRadius();
91 node_b_radius = p_node_b->GetRadius();
92 }
93
94 // Get the unit vector parallel to the line joining the two nodes
95 c_vector<double, SPACE_DIM> unit_difference;
96 /*
97 * We use the mesh method GetVectorFromAtoB() to compute the direction of the
98 * unit vector along the line joining the two nodes, rather than simply subtract
99 * their positions, because this method can be overloaded (e.g. to enforce a
100 * periodic boundary in Cylindrical2dMesh).
101 */
102 unit_difference = rCellPopulation.rGetMesh().GetVectorFromAtoB(r_node_a_location, r_node_b_location);
103
104 // Calculate the distance between the two nodes
105 double distance_between_nodes = norm_2(unit_difference);
106 assert(distance_between_nodes > 0);
107 assert(!std::isnan(distance_between_nodes));
108
109 unit_difference /= distance_between_nodes;
110
111 /*
112 * If mUseCutOffLength has been set, then there is zero force between
113 * two nodes located a distance apart greater than mMechanicsCutOffLength in AbstractTwoBodyInteractionForce.
114 */
115 if (this->mUseCutOffLength)
116 {
117 if (distance_between_nodes >= this->GetCutOffLength())
118 {
119 return zero_vector<double>(SPACE_DIM); // c_vector<double,SPACE_DIM>() is not guaranteed to be fresh memory
120 }
122
123 /*
124 * Calculate the rest length of the spring connecting the two nodes with a default
125 * value of 1.0.
126 */
127 double rest_length_final = 1.0;
128
129 if (bool(dynamic_cast<MeshBasedCellPopulation<ELEMENT_DIM,SPACE_DIM>*>(&rCellPopulation)))
130 {
131 rest_length_final = static_cast<MeshBasedCellPopulation<ELEMENT_DIM,SPACE_DIM>*>(&rCellPopulation)->GetRestLength(nodeAGlobalIndex, nodeBGlobalIndex);
132 }
133 else if (bool(dynamic_cast<NodeBasedCellPopulation<SPACE_DIM>*>(&rCellPopulation)))
134 {
135 assert(node_a_radius > 0 && node_b_radius > 0);
136 rest_length_final = node_a_radius+node_b_radius;
137 }
138
139 double rest_length = rest_length_final;
140
141 CellPtr p_cell_A = rCellPopulation.GetCellUsingLocationIndex(nodeAGlobalIndex);
142 CellPtr p_cell_B = rCellPopulation.GetCellUsingLocationIndex(nodeBGlobalIndex);
143
144 double ageA = p_cell_A->GetAge();
145 double ageB = p_cell_B->GetAge();
146
147 assert(!std::isnan(ageA));
148 assert(!std::isnan(ageB));
149
150 /*
151 * If the cells are both newly divided, then the rest length of the spring
152 * connecting them grows linearly with time, until 1 hour after division.
153 */
154 if (ageA < mMeinekeSpringGrowthDuration && ageB < mMeinekeSpringGrowthDuration)
155 {
157
158 std::pair<CellPtr,CellPtr> cell_pair = p_static_cast_cell_population->CreateCellPair(p_cell_A, p_cell_B);
159
160 if (p_static_cast_cell_population->IsMarkedSpring(cell_pair))
161 {
162 // Spring rest length increases from a small value to the normal rest length over 1 hour
163 double lambda = mMeinekeDivisionRestingSpringLength;
164 rest_length = lambda + (rest_length_final - lambda) * ageA/mMeinekeSpringGrowthDuration;
165 }
166 if (ageA + SimulationTime::Instance()->GetTimeStep() >= mMeinekeSpringGrowthDuration)
167 {
168 // This spring is about to go out of scope
169 p_static_cast_cell_population->UnmarkSpring(cell_pair);
170 }
171 }
172
173 /*
174 * For apoptosis, progressively reduce the radius of the cell
175 */
176 double a_rest_length = rest_length*0.5;
177 double b_rest_length = a_rest_length;
178
179 if (bool(dynamic_cast<NodeBasedCellPopulation<SPACE_DIM>*>(&rCellPopulation)))
180 {
181 assert(node_a_radius > 0 && node_b_radius > 0);
182 a_rest_length = (node_a_radius/(node_a_radius+node_b_radius))*rest_length;
183 b_rest_length = (node_b_radius/(node_a_radius+node_b_radius))*rest_length;
184 }
185
186 /*
187 * If either of the cells has begun apoptosis, then the length of the spring
188 * connecting them decreases linearly with time.
189 */
190 if (p_cell_A->HasApoptosisBegun())
191 {
192 double time_until_death_a = p_cell_A->GetTimeUntilDeath();
193 a_rest_length = a_rest_length * time_until_death_a / p_cell_A->GetApoptosisTime();
194 }
195 if (p_cell_B->HasApoptosisBegun())
196 {
197 double time_until_death_b = p_cell_B->GetTimeUntilDeath();
198 b_rest_length = b_rest_length * time_until_death_b / p_cell_B->GetApoptosisTime();
199 }
200
201 rest_length = a_rest_length + b_rest_length;
202 //assert(rest_length <= 1.0+1e-12); ///\todo #1884 Magic number: would "<= 1.0" do?
203
204 // Although in this class the 'spring constant' is a constant parameter, in
205 // subclasses it can depend on properties of each of the cells
206 double overlap = distance_between_nodes - rest_length;
207 bool is_closer_than_rest_length = (overlap <= 0);
208 double multiplication_factor = VariableSpringConstantMultiplicationFactor(nodeAGlobalIndex, nodeBGlobalIndex, rCellPopulation, is_closer_than_rest_length);
209 double spring_stiffness = mMeinekeSpringStiffness;
210
211 if (bool(dynamic_cast<MeshBasedCellPopulation<ELEMENT_DIM,SPACE_DIM>*>(&rCellPopulation)))
212 {
213 return multiplication_factor * spring_stiffness * unit_difference * overlap;
214 }
215 else
216 {
217 // A reasonably stable simple force law
218 if (is_closer_than_rest_length) //overlap is negative
219 {
220 //log(x+1) is undefined for x<=-1
221 assert(overlap > -rest_length_final);
222 c_vector<double, SPACE_DIM> temp = multiplication_factor*spring_stiffness * unit_difference * rest_length_final* log(1.0 + overlap/rest_length_final);
223 return temp;
224 }
225 else
226 {
227 double alpha = 5.0;
228 c_vector<double, SPACE_DIM> temp = multiplication_factor*spring_stiffness * unit_difference * overlap * exp(-alpha * overlap/rest_length_final);
229 return temp;
230 }
231 }
232}
233
234template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
236{
237 return mMeinekeSpringStiffness;
238}
239
240template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
242{
243 return mMeinekeDivisionRestingSpringLength;
244}
245
246template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
248{
249 return mMeinekeSpringGrowthDuration;
250}
251
252template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
254{
255 assert(springStiffness > 0.0);
256 mMeinekeSpringStiffness = springStiffness;
257}
258
259template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
261{
262 assert(divisionRestingSpringLength <= 1.0);
263 assert(divisionRestingSpringLength >= 0.0);
264
265 mMeinekeDivisionRestingSpringLength = divisionRestingSpringLength;
266}
267
268template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
270{
271 assert(springGrowthDuration >= 0.0);
272
273 mMeinekeSpringGrowthDuration = springGrowthDuration;
274}
275
276template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
278{
279 *rParamsFile << "\t\t\t<MeinekeSpringStiffness>" << mMeinekeSpringStiffness << "</MeinekeSpringStiffness>\n";
280 *rParamsFile << "\t\t\t<MeinekeDivisionRestingSpringLength>" << mMeinekeDivisionRestingSpringLength << "</MeinekeDivisionRestingSpringLength>\n";
281 *rParamsFile << "\t\t\t<MeinekeSpringGrowthDuration>" << mMeinekeSpringGrowthDuration << "</MeinekeSpringGrowthDuration>\n";
282
283 // Call method on direct parent class
285}
286
287// Explicit instantiation
294
295// Serialization for Boost >= 1.36
#define EXPORT_TEMPLATE_CLASS_ALL_DIMS(CLASS)
virtual Node< SPACE_DIM > * GetNode(unsigned index)=0
virtual CellPtr GetCellUsingLocationIndex(unsigned index)
AbstractMesh< ELEMENT_DIM, SPACE_DIM > & rGetMesh()
void UnmarkSpring(std::pair< CellPtr, CellPtr > &rCellPair)
std::pair< CellPtr, CellPtr > CreateCellPair(CellPtr pCell1, CellPtr pCell2)
bool IsMarkedSpring(const std::pair< CellPtr, CellPtr > &rCellPair)
virtual void OutputForceParameters(out_stream &rParamsFile)
void SetMeinekeSpringGrowthDuration(double springGrowthDuration)
virtual double VariableSpringConstantMultiplicationFactor(unsigned nodeAGlobalIndex, unsigned nodeBGlobalIndex, AbstractCellPopulation< ELEMENT_DIM, SPACE_DIM > &rCellPopulation, bool isCloserThanRestLength)
void SetMeinekeSpringStiffness(double springStiffness)
void SetMeinekeDivisionRestingSpringLength(double divisionRestingSpringLength)
c_vector< double, SPACE_DIM > CalculateForceBetweenNodes(unsigned nodeAGlobalIndex, unsigned nodeBGlobalIndex, AbstractCellPopulation< ELEMENT_DIM, SPACE_DIM > &rCellPopulation)
virtual void OutputForceParameters(out_stream &rParamsFile)
Definition Node.hpp:59
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition Node.cpp:139
double GetRadius()
Definition Node.cpp:248
double GetTimeStep() const
static SimulationTime * Instance()