void CryptProjectionForce::UpdateNode3dLocationMap(AbstractCellPopulation<2>& rCellPopulation) { mNode3dLocationMap.clear(); c_vector<double, 2> node_location_2d; c_vector<double, 3> node_location_3d; // Only consider nodes corresponding to real cells for (AbstractCellPopulation<2>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { // Get node index unsigned node_index = rCellPopulation.GetLocationIndexUsingCell(*cell_iter); // Get 3D location node_location_2d = rCellPopulation.GetLocationOfCellCentre(*cell_iter); node_location_3d[0] = node_location_2d[0]; node_location_3d[1] = node_location_2d[1]; node_location_3d[2] = CalculateCryptSurfaceHeightAtPoint(node_location_2d); // Add to map mNode3dLocationMap[node_index] = node_location_3d; } }
void VolumeTrackingModifier<DIM>::UpdateCellData(AbstractCellPopulation<DIM,DIM>& rCellPopulation) { // Make sure the cell population is updated rCellPopulation.Update(); /** * This hack is needed because in the case of a MeshBasedCellPopulation in which * multiple cell divisions have occurred over one time step, the Voronoi tessellation * (while existing) is out-of-date. Thus, if we did not regenerate the Voronoi * tessellation here, an assertion may trip as we try to access a Voronoi element * whose index exceeds the number of elements in the out-of-date tessellation. * * \todo work out how to properly fix this (#1986) */ if (bool(dynamic_cast<MeshBasedCellPopulation<DIM>*>(&rCellPopulation))) { static_cast<MeshBasedCellPopulation<DIM>*>(&(rCellPopulation))->CreateVoronoiTessellation(); } // Iterate over cell population for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { // Get the volume of this cell double cell_volume = rCellPopulation.GetVolumeOfCell(*cell_iter); // Store the cell's volume in CellData cell_iter->GetCellData()->SetItem("volume", cell_volume); } }
void GonadArmPositionTrackerModifier<DIM>::UpdateAtEndOfTimeStep(AbstractCellPopulation<DIM,DIM>& rCellPopulation) { if((SimulationTime::Instance()->GetTime())-(int)SimulationTime::Instance()->GetTime()==(1/250)) for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { double id = cell_iter->GetCellId(); double position = cell_iter->GetCellData()->GetItem("DistanceAwayFromDTC"); double prolif = cell_iter->GetCellData()->GetItem("Proliferating"); fprintf(OutputPositionFile,"%e\t",SimulationTime::Instance()->GetTime()); fprintf(OutputPositionFile,"%f\t",id); fprintf(OutputPositionFile,"%e\t",position); fprintf(OutputPositionFile,"%e\t",prolif); fprintf(OutputPositionFile,"\n"); } if(SimulationTime::Instance()->IsFinished()){ //Close output file fclose(OutputPositionFile); } }
/* * Next, we define the {{{UpdateCellData()}}} method itself. This is a helper * method that computes the height (y coordinate) of each cell in the population * and stores this in the {{{CellData}}} property. */ void UpdateCellData(AbstractCellPopulation<2,2>& rCellPopulation) { /* * We begin by calling {{{Update()}}} on the cell population, which ensures that * it is in a coherent state. */ rCellPopulation.Update(); /* * Next, we iterate over the cell population... */ for (AbstractCellPopulation<2>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { /* * ...find its height... */ double cell_height = rCellPopulation.GetLocationOfCellCentre(*cell_iter)[1]; /* * ...and store this in the {{{CellData}}} item "height". */ cell_iter->GetCellData()->SetItem("height", cell_height); } }
void TCellDiffusionForce<DIM>::AddForceContribution(AbstractCellPopulation<DIM>& rCellPopulation) { double dt = SimulationTime::Instance()->GetTimeStep(); // Iterate over the nodes for (typename AbstractMesh<DIM, DIM>::NodeIterator node_iter = rCellPopulation.rGetMesh().GetNodeIteratorBegin(); node_iter != rCellPopulation.rGetMesh().GetNodeIteratorEnd(); ++node_iter) { // Get the radius of this node unsigned node_index = node_iter->GetIndex(); double node_radius = node_iter->GetRadius(); // Get cell associated with this index CellPtr p_cell = rCellPopulation.GetCellUsingLocationIndex(node_index); // Reject if no radius has been set if (node_radius == 0.0) { EXCEPTION("SetRadius() must be called on each Node before calling TCellDiffusionForce::AddForceContribution() to avoid a division by zero error"); } // If the selected cell is a Unlabelled Differentiated T Cell, apply diffusion force contribution. if ( (p_cell->GetMutationState()->IsType<TCellMutationState>()) && (p_cell->GetCellProliferativeType()->IsType<DifferentiatedCellProliferativeType>()) && !(p_cell->HasCellProperty<CellLabel>()) ) { double nu = dynamic_cast<AbstractOffLatticeCellPopulation<DIM>*>(&rCellPopulation)->GetDampingConstant(node_index); /* Compute the diffusion coefficient D as D = k*T/(6*pi*eta*r), where * * k = Boltzmann's constant, * T = absolute temperature, * eta = dynamic viscosity, * r = cell radius. */ double diffusion_const_scaling = GetDiffusionScalingConstant(); double diffusion_constant = diffusion_const_scaling/node_radius; c_vector<double, DIM> force_contribution; for (unsigned i=0; i<DIM; i++) { /* The force on this cell is scaled with the timestep such that when it is * used in the discretised equation of motion for the cell, we obtain the * correct formula * * x_new = x_old + sqrt(2*D*dt)*W * * where W is a standard normal random variable. */ double xi = RandomNumberGenerator::Instance()->StandardNormalRandomDeviate(); force_contribution[i] = mStrengthParameter * ((nu*sqrt(2.0*diffusion_constant*dt)/dt)*xi); } node_iter->AddAppliedForceContribution(force_contribution); } } }
void AbstractTargetAreaModifier<DIM>::UpdateTargetAreas(AbstractCellPopulation<DIM,DIM>& rCellPopulation) { // Assume that SetupSolve() has already been called, so we must be using a VertexBasedCellPopulation // Loop over the list of cells, rather than using the population iterator, so as to include dead cells for (std::list<CellPtr>::iterator cell_iter = rCellPopulation.rGetCells().begin(); cell_iter != rCellPopulation.rGetCells().end(); ++cell_iter) { UpdateTargetAreaOfCell(*cell_iter); } }
void CryptProjectionForce::AddForceContribution(AbstractCellPopulation<2>& rCellPopulation) { // First work out the 3D location of each cell UpdateNode3dLocationMap(rCellPopulation); // Throw an exception message if not using a MeshBasedCellPopulation if (dynamic_cast<MeshBasedCellPopulation<2>*>(&rCellPopulation) == NULL) { EXCEPTION("CryptProjectionForce is to be used with a subclass of MeshBasedCellPopulation only"); } MeshBasedCellPopulation<2>* p_static_cast_cell_population = static_cast<MeshBasedCellPopulation<2>*>(&rCellPopulation); for (MeshBasedCellPopulation<2>::SpringIterator spring_iterator = p_static_cast_cell_population->SpringsBegin(); spring_iterator != p_static_cast_cell_population->SpringsEnd(); ++spring_iterator) { unsigned nodeA_global_index = spring_iterator.GetNodeA()->GetIndex(); unsigned nodeB_global_index = spring_iterator.GetNodeB()->GetIndex(); c_vector<double, 2> force = CalculateForceBetweenNodes(nodeA_global_index, nodeB_global_index, rCellPopulation); c_vector<double, 2> negative_force = -1.0 * force; spring_iterator.GetNodeB()->AddAppliedForceContribution(negative_force); spring_iterator.GetNodeA()->AddAppliedForceContribution(force); } if (mIncludeWntChemotaxis) { assert(WntConcentration<2>::Instance()->IsWntSetUp()); for (AbstractCellPopulation<2>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { if (cell_iter->GetCellProliferativeType()->IsType<StemCellProliferativeType>()) { c_vector<double, 2> wnt_chemotactic_force = mWntChemotaxisStrength*WntConcentration<2>::Instance()->GetWntGradient(*cell_iter); unsigned index = rCellPopulation.GetLocationIndexUsingCell(*cell_iter); rCellPopulation.GetNode(index)->AddAppliedForceContribution(wnt_chemotactic_force); } } } }
/* The second public method overrides {{{AddForceContribution()}}}. * This method takes in one argument, a reference to the cell population itself. */ void AddForceContribution(AbstractCellPopulation<2>& rCellPopulation) { /* Inside the method, we loop over cells, and add a vector to * each node associated with cells with the {{{MotileCellProperty}}}, which is proportional (with constant {{{mStrength}}}) to the negative of the position. Causing * cells to move inwards towards the origin. Note that this will currently only work with subclasses of {{{AbstractCentreBasedCellPopulation}}}s as * we associate cells with nodes in the force calculation. However, this could easily be modified to make it work for {{{VertexBasedCellPopulation}}}s. */ for (AbstractCellPopulation<2>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { if (cell_iter->HasCellProperty<MotileCellProperty>()) { unsigned node_index = rCellPopulation.GetLocationIndexUsingCell(*cell_iter); c_vector<double, 2> location = rCellPopulation.GetLocationOfCellCentre(*cell_iter); c_vector<double, 2> force = -1.0 * mStrength * location; rCellPopulation.GetNode(node_index)->AddAppliedForceContribution(force); } } }
void PopulationTestingForce<ELEMENT_DIM, SPACE_DIM>::AddForceContribution(AbstractCellPopulation<ELEMENT_DIM,SPACE_DIM>& rCellPopulation) { for (unsigned i=0; i<rCellPopulation.GetNumNodes(); i++) { c_vector<double, SPACE_DIM> force; for (unsigned j=0; j<SPACE_DIM; j++) { if (mWithPositionDependence) { force[j] = (j+1)*i*0.01*rCellPopulation.GetNode(i)->rGetLocation()[j]; } else { force[j] = (j+1)*i*0.01; } } rCellPopulation.GetNode(i)->ClearAppliedForce(); rCellPopulation.GetNode(i)->AddAppliedForceContribution(force); } }
c_vector<double, DIM> BuskeAdhesiveForce<DIM>::CalculateForceBetweenNodes(unsigned nodeAGlobalIndex, unsigned nodeBGlobalIndex, AbstractCellPopulation<DIM>& rCellPopulation) { // This force class is defined for NodeBasedCellPopulations only assert(dynamic_cast<NodeBasedCellPopulation<DIM>*>(&rCellPopulation) != NULL); // We should only ever calculate the force between two distinct nodes assert(nodeAGlobalIndex != nodeBGlobalIndex); Node<DIM>* p_node_a = rCellPopulation.GetNode(nodeAGlobalIndex); Node<DIM>* p_node_b = rCellPopulation.GetNode(nodeBGlobalIndex); // Get the node locations c_vector<double, DIM> node_a_location = p_node_a->rGetLocation(); c_vector<double, DIM> node_b_location = p_node_b->rGetLocation(); // Get the unit vector parallel to the line joining the two nodes (assuming no periodicities etc.) c_vector<double, DIM> unit_vector = node_b_location - node_a_location; // Calculate the distance between the two nodes double distance_between_nodes = norm_2(unit_vector); // Account for any cutoff in the force law if (this->mUseCutOffLength) { if (distance_between_nodes >= this->GetCutOffLength()) { return zero_vector<double>(DIM); } } // Assert that the nodes are a finite, non-zero distance apart assert(distance_between_nodes > 0); assert(!std::isnan(distance_between_nodes)); // Normalize the unit vector unit_vector /= distance_between_nodes; double radius_of_cell_one = p_node_a->GetRadius(); double radius_of_cell_two = p_node_b->GetRadius(); // Compute the force vector c_vector<double, DIM> force_between_nodes = GetMagnitudeOfForce(distance_between_nodes,radius_of_cell_one,radius_of_cell_two) * unit_vector; return force_between_nodes; }
void DeltaNotchTrackingModifier<DIM>::UpdateCellData(AbstractCellPopulation<DIM,DIM>& rCellPopulation) { // Make sure the cell population is updated rCellPopulation.Update(); // First recover each cell's Notch and Delta concentrations from the ODEs and store in CellData for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { DeltaNotchCellCycleModel* p_model = static_cast<DeltaNotchCellCycleModel*>(cell_iter->GetCellCycleModel()); double this_delta = p_model->GetDelta(); double this_notch = p_model->GetNotch(); // Note that the state variables must be in the same order as listed in DeltaNotchOdeSystem cell_iter->GetCellData()->SetItem("notch", this_notch); cell_iter->GetCellData()->SetItem("delta", this_delta); } // Next iterate over the population to compute and store each cell's neighbouring Delta concentration in CellData for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { // Get the set of neighbouring location indices std::set<unsigned> neighbour_indices = rCellPopulation.GetNeighbouringLocationIndices(*cell_iter); // Compute this cell's average neighbouring Delta concentration and store in CellData if (!neighbour_indices.empty()) { double mean_delta = 0.0; for (std::set<unsigned>::iterator iter = neighbour_indices.begin(); iter != neighbour_indices.end(); ++iter) { CellPtr p_cell = rCellPopulation.GetCellUsingLocationIndex(*iter); double this_delta = p_cell->GetCellData()->GetItem("delta"); mean_delta += this_delta/neighbour_indices.size(); } cell_iter->GetCellData()->SetItem("mean delta", mean_delta); } else { // If this cell has no neighbours, such as an isolated cell in a CaBasedCellPopulation, store 0.0 for the cell data cell_iter->GetCellData()->SetItem("mean delta", 0.0); } } }
void BuskeCompressionForce<DIM>::AddForceContribution(AbstractCellPopulation<DIM>& rCellPopulation) { // This force class is defined for NodeBasedCellPopulations only assert(dynamic_cast<NodeBasedCellPopulation<DIM>*>(&rCellPopulation) != NULL); NodeBasedCellPopulation<DIM>* p_static_cast_cell_population = static_cast<NodeBasedCellPopulation<DIM>*>(&rCellPopulation); c_vector<double, DIM> unit_vector; // Loop over cells in the population for (typename AbstractCellPopulation<DIM>::Iterator cell_iter = rCellPopulation.Begin(); cell_iter != rCellPopulation.End(); ++cell_iter) { // Get the node index corresponding to this cell unsigned node_index = rCellPopulation.GetLocationIndexUsingCell(*cell_iter); Node<DIM>* p_node_i = rCellPopulation.GetNode(node_index); // Get the location of this node c_vector<double, DIM> node_i_location = p_node_i->rGetLocation(); // Get the radius of this cell double radius_of_cell_i = p_node_i->GetRadius(); double delta_V_c = 0.0; c_vector<double, DIM> dVAdd_vector = zero_vector<double>(DIM); // Get the set of node indices corresponding to this cell's neighbours std::set<unsigned> neighbouring_node_indices = p_static_cast_cell_population->GetNeighbouringNodeIndices(node_index); // Loop over this set for (std::set<unsigned>::iterator iter = neighbouring_node_indices.begin(); iter != neighbouring_node_indices.end(); ++iter) { Node<DIM>* p_node_j = rCellPopulation.GetNode(*iter); // Get the location of this node c_vector<double, DIM> node_j_location = p_node_j->rGetLocation(); // Get the unit vector parallel to the line joining the two nodes (assuming no periodicities etc.) unit_vector = node_j_location - node_i_location; // Calculate the distance between the two nodes double dij = norm_2(unit_vector); unit_vector /= dij; // Get the radius of the cell corresponding to this node double radius_of_cell_j = p_node_j->GetRadius(); // If the cells are close enough to exert a force on each other... if (dij < radius_of_cell_i + radius_of_cell_j) { // ...then compute the adhesion force and add it to the vector of forces... double xij = 0.5*(radius_of_cell_i*radius_of_cell_i - radius_of_cell_j*radius_of_cell_j + dij*dij)/dij; double dxijdd = 1.0 - xij/dij; double dVAdd = M_PI*dxijdd*(5.0*pow(radius_of_cell_i,2.0) + 3.0*pow(xij,2.0) - 8.0*radius_of_cell_i*xij)/3.0; dVAdd_vector += dVAdd*unit_vector; // ...and add the contribution to the compression force acting on cell i delta_V_c += M_PI*pow(radius_of_cell_i - xij,2.0)*(2*radius_of_cell_i - xij)/3.0; } } double V_A = 4.0/3.0*M_PI*pow(radius_of_cell_i,3.0) - delta_V_c; /** * Target volume of the cell * \todo Doesn't say in the Buske paper how they calculate this, so * we need to look at this to be sure it's what we want (#1764) */ double V_T = 5.0; // Note: the sign in force_magnitude is different from the one in equation (A3) in the Buske paper c_vector<double, DIM> applied_force = -mCompressionEnergyParameter/V_T*(V_T - V_A)*dVAdd_vector; p_node_i->AddAppliedForceContribution(applied_force); } }
void TCellTumorSpringForce<DIM>::AddForceContribution(AbstractCellPopulation<DIM>& rCellPopulation) { // Throw an exception message if not using a NodeBasedCellPopulation if (dynamic_cast<NodeBasedCellPopulation<DIM>*>(&rCellPopulation) == NULL) { EXCEPTION("TCellTumorSpringForce is to be used with a NodeBasedCellPopulation only"); } std::vector< std::pair<Node<DIM>*, Node<DIM>* > >& r_node_pairs = (static_cast<NodeBasedCellPopulation<DIM>*>(&rCellPopulation))->rGetNodePairs(); // Iterate over all pairs of nodes for (typename std::vector< std::pair<Node<DIM>*, Node<DIM>* > >::iterator iter = r_node_pairs.begin(); iter != r_node_pairs.end(); iter++) { std::pair<Node<DIM>*, Node<DIM>* > pair = *iter; Node<DIM>* p_node_a = pair.first; Node<DIM>* p_node_b = pair.second; // Get the node locations, radii and indices c_vector<double, DIM> node_a_location = p_node_a->rGetLocation(); c_vector<double, DIM> node_b_location = p_node_b->rGetLocation(); double node_a_radius = p_node_a->GetRadius(); double node_b_radius = p_node_b->GetRadius(); unsigned node_a_index = p_node_a->GetIndex(); unsigned node_b_index = p_node_b->GetIndex(); // Get the cells associated with these node indices CellPtr p_cell_a = rCellPopulation.GetCellUsingLocationIndex(node_a_index); CellPtr p_cell_b = rCellPopulation.GetCellUsingLocationIndex(node_b_index); /* --==-- 01: T Cell - T Cell Repulsion segment --==-- */ /* Applies RepulsionForce between Differentiated T-Cells (labelled and unlabelled) */ if ( (p_cell_a->GetMutationState()->IsType<TCellMutationState>()) && (p_cell_b->GetMutationState()->IsType<TCellMutationState>()) && (p_cell_a->GetCellProliferativeType()->IsType<DifferentiatedCellProliferativeType>()) && (p_cell_b->GetCellProliferativeType()->IsType<DifferentiatedCellProliferativeType>()) ) { c_vector<double, DIM> unit_difference; unit_difference = (static_cast<NodeBasedCellPopulation<DIM>*>(&rCellPopulation))->rGetMesh().GetVectorFromAtoB(node_a_location, node_b_location); double rest_length = node_a_radius+node_b_radius; // Only apply force if the cells are close (repulsion force) if (norm_2(unit_difference) < rest_length) { c_vector<double, DIM> force = this->CalculateForceBetweenNodes(p_node_a->GetIndex(), p_node_b->GetIndex(), rCellPopulation); c_vector<double, DIM> negative_force = -1.0 * force; for (unsigned j=0; j<DIM; j++) { assert(!std::isnan(force[j])); } p_node_a->AddAppliedForceContribution(force); p_node_b->AddAppliedForceContribution(negative_force); } } /* --==-- 02: T Cell - Tumor Cell Springs segment --==-- */ /* Applies GeneralisedLinearSpringForce between Labelled T-Cells and Tumor Cells */ if ( (p_cell_a->HasCellProperty<CellLabel>()) && (p_cell_b->GetMutationState()->IsType<TumorCellMutationState>()) || (p_cell_b->HasCellProperty<CellLabel>()) && (p_cell_a->GetMutationState()->IsType<TumorCellMutationState>()) ) { // Calculate the force between nodes c_vector<double, DIM> force = this->CalculateForceBetweenNodes(p_node_a->GetIndex(), p_node_b->GetIndex(), rCellPopulation); c_vector<double, DIM> negative_force = -1.0 * force; for (unsigned j=0; j<DIM; j++) { assert(!std::isnan(force[j])); } // Add the force contribution to each node p_node_a->AddAppliedForceContribution(force); p_node_b->AddAppliedForceContribution(negative_force); } /* --==-- 03: Tumor Cell - Tumor Cell Springs segment --==-- */ /* Applies GeneralisedLinearSpringForce between Tumor Cells. Check both cells are Tumor Cells */ if ( (p_cell_a->GetMutationState()->IsType<TumorCellMutationState>()) && (p_cell_b->GetMutationState()->IsType<TumorCellMutationState>()) ) { // Calculate the force between nodes c_vector<double, DIM> force = this->CalculateForceBetweenNodes(p_node_a->GetIndex(), p_node_b->GetIndex(), rCellPopulation); c_vector<double, DIM> negative_force = -1.0 * force; for (unsigned j=0; j<DIM; j++) { assert(!std::isnan(force[j])); } // Add the force contribution to each node p_node_a->AddAppliedForceContribution(force); p_node_b->AddAppliedForceContribution(negative_force); } } }
c_vector<double,2> CryptProjectionForce::CalculateForceBetweenNodes(unsigned nodeAGlobalIndex, unsigned nodeBGlobalIndex, AbstractCellPopulation<2>& rCellPopulation) { MeshBasedCellPopulation<2>* p_static_cast_cell_population = static_cast<MeshBasedCellPopulation<2>*>(&rCellPopulation); // We should only ever calculate the force between two distinct nodes assert(nodeAGlobalIndex != nodeBGlobalIndex); // Get the node locations in 2D c_vector<double,2> node_a_location_2d = rCellPopulation.GetNode(nodeAGlobalIndex)->rGetLocation(); c_vector<double,2> node_b_location_2d = rCellPopulation.GetNode(nodeBGlobalIndex)->rGetLocation(); // "Get the unit vector parallel to the line joining the two nodes" [GeneralisedLinearSpringForce] // Create a unit vector in the direction of the 3D spring c_vector<double,3> unit_difference = mNode3dLocationMap[nodeBGlobalIndex] - mNode3dLocationMap[nodeAGlobalIndex]; // Calculate the distance between the two nodes double distance_between_nodes = norm_2(unit_difference); assert(distance_between_nodes > 0); assert(!std::isnan(distance_between_nodes)); unit_difference /= distance_between_nodes; // If mUseCutOffLength has been set, then there is zero force between // two nodes located a distance apart greater than mUseCutOffLength if (this->mUseCutOffLength) { if (distance_between_nodes >= mMechanicsCutOffLength) { // Return zero (2D projected) force return zero_vector<double>(2); } } // Calculate the rest length of the spring connecting the two nodes double rest_length = 1.0; CellPtr p_cell_A = rCellPopulation.GetCellUsingLocationIndex(nodeAGlobalIndex); CellPtr p_cell_B = rCellPopulation.GetCellUsingLocationIndex(nodeBGlobalIndex); double ageA = p_cell_A->GetAge(); double ageB = p_cell_B->GetAge(); assert(!std::isnan(ageA)); assert(!std::isnan(ageB)); /* * If the cells are both newly divided, then the rest length of the spring * connecting them grows linearly with time, until mMeinekeSpringGrowthDuration hour after division. */ if (ageA < mMeinekeSpringGrowthDuration && ageB < mMeinekeSpringGrowthDuration) { /* * The spring rest length increases from a predefined small parameter * to a normal rest length of 1.0, over a period of one hour. */ std::pair<CellPtr,CellPtr> cell_pair = p_static_cast_cell_population->CreateCellPair(p_cell_A, p_cell_B); if (p_static_cast_cell_population->IsMarkedSpring(cell_pair)) { double lambda = mMeinekeDivisionRestingSpringLength; rest_length = lambda + (1.0 - lambda) * ageA/mMeinekeSpringGrowthDuration; } if (ageA+SimulationTime::Instance()->GetTimeStep() >= mMeinekeSpringGrowthDuration) { // This spring is about to go out of scope p_static_cast_cell_population->UnmarkSpring(cell_pair); } } double a_rest_length = rest_length*0.5; double b_rest_length = a_rest_length; /* * If either of the cells has begun apoptosis, then the length of the spring * connecting them decreases linearly with time. */ if (p_cell_A->HasApoptosisBegun()) { double time_until_death_a = p_cell_A->GetTimeUntilDeath(); a_rest_length = a_rest_length * time_until_death_a / p_cell_A->GetApoptosisTime(); } if (p_cell_B->HasApoptosisBegun()) { double time_until_death_b = p_cell_B->GetTimeUntilDeath(); b_rest_length = b_rest_length * time_until_death_b / p_cell_B->GetApoptosisTime(); } rest_length = a_rest_length + b_rest_length; // Assert that the rest length does not exceed 1 assert(rest_length <= 1.0+1e-12); bool is_closer_than_rest_length = true; if (distance_between_nodes - rest_length > 0) { is_closer_than_rest_length = false; } /* * Although in this class the 'spring constant' is a constant parameter, in * subclasses it can depend on properties of each of the cells. */ double multiplication_factor = 1.0; multiplication_factor *= VariableSpringConstantMultiplicationFactor(nodeAGlobalIndex, nodeBGlobalIndex, rCellPopulation, is_closer_than_rest_length); // Calculate the 3D force between the two points c_vector<double,3> force_between_nodes = multiplication_factor * this->GetMeinekeSpringStiffness() * unit_difference * (distance_between_nodes - rest_length); // Calculate an outward normal unit vector to the tangent plane of the crypt surface at the 3D point corresponding to node B c_vector<double,3> outward_normal_unit_vector; double dfdr = CalculateCryptSurfaceDerivativeAtPoint(node_b_location_2d); double theta_B = atan2(node_b_location_2d[1], node_b_location_2d[0]); // use atan2 to determine the quadrant double normalization_factor = sqrt(1 + dfdr*dfdr); outward_normal_unit_vector[0] = dfdr*cos(theta_B)/normalization_factor; outward_normal_unit_vector[1] = dfdr*sin(theta_B)/normalization_factor; outward_normal_unit_vector[2] = -1.0/normalization_factor; // Calculate the projection of the force onto the plane z=0 c_vector<double,2> projected_force_between_nodes_2d; double force_dot_normal = inner_prod(force_between_nodes, outward_normal_unit_vector); for (unsigned i=0; i<2; i++) { projected_force_between_nodes_2d[i] = force_between_nodes[i] - force_dot_normal*outward_normal_unit_vector[i] + force_dot_normal*outward_normal_unit_vector[2]; } return projected_force_between_nodes_2d; }