Skip to content

Commit 07b6852

Browse files
committed
metabolism-mediated damage
1 parent 7226c53 commit 07b6852

File tree

3 files changed

+37
-114
lines changed

3 files changed

+37
-114
lines changed

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -238,23 +238,27 @@ Each `substrate` includes the attribute `name="S"` to set the substrate.
238238
**See templates [here](#pd_templates).**
239239

240240
### PD models
241-
Two similar models are supplied for PD, both using the principle of Area Under the Curve or AUC.
242-
One uses the AUC of the internalized *concentration*, `AUC`, and the other uses the AUC of the internalized *amount*, `AUC_amount`.
241+
There are four available PD models: `AUC`, `AUC_amount`, `MMD`, and `MMD_amount`.
242+
The `AUC`-prefixed models use the area under the curve (AUC) of the internalized concentration to set the damage variable, `S_damage`.
243+
By contrast, the `MMD`-prefixed models are based on metabolism-mediated damage (MMD) and use the amount of internalized substrate metabolized to set the damage variable, also accumulated over time similar to AUC.
244+
The value of $\gamma$ below differentiates between these two classes.
245+
246+
The `_amount`-suffixed models use the internalized *amount* rather than the internalized *concentration* to update the damage variable.
243247
PhysiPKPD will turn on `track_internalized_substrates` for you if it detects a PD model.
244-
When using `AUC`, PhysiPKPD divides the internalized amount that PhysiCell tracks by the cell's current volume to determine the initial condition for $A$.
248+
When using `AUC` or `MMD`, PhysiPKPD divides the internalized amount that PhysiCell tracks by the cell's current volume to determine the initial condition for $A$.
249+
245250
Both update according to the following differential equation:
246251

247252
$$
248253
\begin{aligned}
249254
A' & = -mA \\
250-
D' & = A - r_1D - r_0
255+
D' & = \gamma A - r_1D - r_0
251256
\end{aligned}
252257
$$
253258

254-
where $A$ is the internalized quantity, amount or concentration, and $D$ is the accumulated damage or AUC[^auc_no_par].
259+
where $A$ is the internalized quantity, amount or concentration, and $D$ is the accumulated damage.
255260
The internalized quantity, $A$, is metabolized at a rate $m$, damage is repaired with linear rate $r_1$ and constant rate $r_0$.
256-
257-
[^auc_no_par]: Because $D$ is measuring the AUC, the coefficient for $A$ in $D'$ is fixed to $1$. That is, $D$ has units amount x min or concentration x min, depending on the model used.
261+
The parameter $\gamma$ is set either to `1.0` (for `AUC` and `AUC_amount`) or set to $m$ (for `MMD` and `MMD_amount`).
258262

259263
| Parameter | Description | If Missing |
260264
| :-- | :-- | :-- |
@@ -407,7 +411,7 @@ Place the following within the `cell_definition` element:
407411
<dt>0.1</dt>
408412
</substrate>
409413
<substrate name="PKPD_D2">
410-
<model>AUC</model>
414+
<model>MMD</model>
411415
<metabolism_rate>0.05</metabolism_rate>
412416
<constant_repair_rate>5e3</constant_repair_rate>
413417
<linear_repair_rate>2e-2</linear_repair_rate>

addons/PhysiPKPD/src/PhysiPKPD_PD.cpp

Lines changed: 21 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -10,68 +10,6 @@ static double tolerance = 0.01 * diffusion_dt; // using this in single_pd_model
1010
Pharmacodynamics_Model::Pharmacodynamics_Model()
1111
{ return; }
1212

13-
/*
14-
Pharmacodynamics_Model *create_pd_model(int substrate_index, std::string substrate_name, int cell_definition_index, std::string cell_definition_name)
15-
{
16-
Pharmacodynamics_Model *pNew = create_pd_model(substrate_index, cell_definition_index);
17-
pNew->substrate_name = substrate_name;
18-
pNew->cell_definition_name = cell_definition_name;
19-
20-
if (parameters.doubles.find_index(substrate_name + "_dt_" + cell_definition_name)==-1)
21-
{
22-
std::cout << "PhysiPKPD WARNING: No PD time step supplied for " << substrate_name << " effects on " << cell_definition_name << std::endl
23-
<< "\tWill use the mechanics_dt by default." << std::endl
24-
<< "\tSpecify one using " << substrate_name + "_dt_" + cell_definition_name << std::endl << std::endl;
25-
26-
pNew->dt = mechanics_dt;
27-
}
28-
else
29-
{
30-
pNew->dt = parameters.doubles(substrate_name + "_dt_" + cell_definition_name);
31-
}
32-
33-
setup_pd_advancer(pNew);
34-
pNew->previous_pd_time = PhysiCell_globals.current_time;
35-
pNew->next_pd_time = PhysiCell_globals.current_time;
36-
Cell_Definition* pCD = cell_definitions_by_index[cell_definition_index];
37-
if (pCD->custom_data.find_variable_index(substrate_name + "_damage")==-1) // make sure a damage variable for this cell was initialized for this substrate
38-
{
39-
for (int i = 0; i < cell_definitions_by_index.size(); i++)
40-
{
41-
if (cell_definitions_by_index[i]->custom_data.find_variable_index(substrate_name + "_damage")!=-1)
42-
{
43-
std::cout << "PhysiPKPD ERROR: No damage variable for " << substrate_name << " acting on " << cell_definition_name << " given." << std::endl
44-
<< "\tBut " << cell_definitions_by_index[i]->name << " does have this damage variable." << std::endl
45-
<< "\tI cannot guarantee that adding this variable will put it in the right index." << std:: endl
46-
<< "\tSet this with " << substrate_name + "_damage"
47-
<< "\tas a custom variable for " << cell_definition_name << std::endl
48-
<< std::endl;
49-
exit(-1);
50-
}
51-
}
52-
std::cout << "PhysiPKPD WARNING: No damage variable for " << substrate_name << " acting on " << cell_definition_name << " given." << std::endl
53-
<< "\tSet this with " << substrate_name + "_damage for all cell types affected by this substrate."
54-
<< "\tOtherwise, you risk changing variable indices and I can't guarantee that won't cause issues." << std::endl
55-
<< std::endl;
56-
pCD->custom_data.add_variable(substrate_name + "_damage", 0.0);
57-
58-
#pragma omp parallel for
59-
for (int i = 0; i < (*all_cells).size(); i++) // loop over all cells to see if they have a type that got moas added to their custom_data
60-
{
61-
if ((*all_cells)[i]->type==cell_definition_index) // check if this cell is of the current type
62-
{
63-
(*all_cells)[i]->custom_data.add_variable(substrate_name + "_damage", 0.0);
64-
}
65-
} // finish looping over each cell
66-
}
67-
68-
pNew->damage_index = cell_definitions_by_index[cell_definition_index]->custom_data.find_variable_index(substrate_name + "_damage");
69-
pNew->advance = &single_pd_model;
70-
71-
return pNew;
72-
}
73-
*/
74-
7513
Pharmacodynamics_Model *create_pd_model(int substrate_index, int cell_definition_index)
7614
{
7715
Pharmacodynamics_Model *pNew = create_pd_model();
@@ -226,7 +164,7 @@ void PD_model(double current_time)
226164
void setup_pd_advancer(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_node)
227165
{
228166
void (*setup_function)(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_node);
229-
std::vector<std::string> current_options = {"AUC","AUC_amount","SBML"};
167+
std::vector<std::string> current_options = {"AUC","AUC_amount","SBML","MMD","MMD_amount"};
230168
std::string method;
231169
if (!(substrate_node.child("model")))
232170
{
@@ -243,6 +181,8 @@ void setup_pd_advancer(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_nod
243181
fns.push_back(&setup_pd_model_auc);
244182
fns.push_back(&setup_pd_model_auc);
245183
fns.push_back(&setup_pd_model_sbml);
184+
fns.push_back(&setup_pd_model_auc);
185+
fns.push_back(&setup_pd_model_auc);
246186

247187
std::string model = substrate_node.child("model").text().as_string();
248188
bool model_found = false;
@@ -264,7 +204,7 @@ void setup_pd_advancer(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_nod
264204
}
265205
}
266206
setup_function(pPD, substrate_node);
267-
pPD->use_internalized_amount = (method=="AUC_amount");
207+
pPD->use_internalized_amount = (method=="AUC_amount") || (method=="MMD_amount");
268208
return;
269209
}
270210

@@ -300,6 +240,7 @@ void setup_pd_model_auc(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_no
300240
exit(-1);
301241
}
302242
double metabolism_rate = substrate_node.child("metabolism_rate").text().as_double();
243+
double substrate_to_damage_rate = parse_substrate_to_damage_rate(substrate_node);
303244
double linear_repair_rate = substrate_node.child("linear_repair_rate").text().as_double();
304245
double constant_repair_rate = substrate_node.child("constant_repair_rate").text().as_double();
305246

@@ -318,50 +259,27 @@ void setup_pd_model_auc(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_no
318259
}
319260
if (metabolism_rate != linear_repair_rate)
320261
{
321-
pPD->initial_substrate_coefficient = (pPD->metabolism_reduction_factor - pPD->initial_damage_coefficient) / (linear_repair_rate - metabolism_rate); // d_10
262+
pPD->initial_substrate_coefficient = substrate_to_damage_rate * (pPD->metabolism_reduction_factor - pPD->initial_damage_coefficient) / (linear_repair_rate - metabolism_rate); // d_10
322263
}
323264
else
324265
{
325-
pPD->initial_substrate_coefficient = pPD->initial_damage_coefficient * pPD->dt; // d_10
266+
pPD->initial_substrate_coefficient = substrate_to_damage_rate * pPD->initial_damage_coefficient * pPD->dt; // d_10
326267
}
327-
/* OLD CODE USED TO COMPUTE d_00, d_10, and d_01
328-
if (linear_repair_rate == 0)
329-
{
330-
// Damage (D) follows D' = A - constant_rate ==> D(dt) = d_00 + d_10 * A0 + 1 * D0; defining d_00 and d_01 here
331-
pPD->damage_constant = -constant_repair_rate * pPD->dt; // d_00
332-
if (metabolism_rate == 0)
333-
{
334-
pPD->initial_substrate_coefficient = pPD->dt; // d_10
335-
}
336-
else
337-
{
338-
pPD->initial_substrate_coefficient = (1 - pPD->metabolism_reduction_factor) / metabolism_rate; // d_10
339-
}
340-
pPD->initial_damage_coefficient = 1.0; // d_01
341-
}
342-
343-
else
344-
{
345-
// Damage (D) follows D' = A - linear_rate * D - constant_rate ==> D(dt) = d_00 + d_10 * A0 + d_01 * D0; defining d_00, d_10, and d_01 here
346-
pPD->initial_damage_coefficient = exp(-linear_repair_rate * pPD->dt); // d_01
347-
348-
pPD->damage_constant = constant_repair_rate;
349-
pPD->damage_constant /= linear_repair_rate;
350-
pPD->damage_constant *= pPD->initial_damage_coefficient - 1; // d_00
268+
}
269+
}
351270

352-
// if the metabolism and repair rates are equal, then the system has repeated eigenvalues and the analytic solution is qualitatively different; notice the division by the difference of these rates in the first case
353-
if (metabolism_rate != linear_repair_rate)
354-
{
355-
pPD->initial_substrate_coefficient = pPD->metabolism_reduction_factor;
356-
pPD->initial_substrate_coefficient -= pPD->initial_damage_coefficient; // d_10
357-
pPD->initial_substrate_coefficient /= linear_repair_rate - metabolism_rate; // this would be bad if these rates were equal!
358-
}
359-
else
360-
{
361-
pPD->initial_substrate_coefficient = pPD->initial_damage_coefficient * pPD->dt; // d_10
362-
}
363-
}
364-
*/
271+
double parse_substrate_to_damage_rate(const pugi::xml_node &substrate_node)
272+
{
273+
std::string model = substrate_node.child("model").text().as_string();
274+
if (model.find("AUC") == 0)
275+
{ return 1.0; }
276+
else if (model.find("MMD") == 0)
277+
{ return substrate_node.child("metabolism_rate").text().as_double(); }
278+
else
279+
{
280+
std::cout << "PhysiPKPD ERROR: The model " << model << " is not a valid option for the substrate_to_damage_rate." << std::endl
281+
<< "\tValid options include AUC, AUC_amount, MMD, and MMD_amount." << std::endl;
282+
exit(-1);
365283
}
366284
}
367285

addons/PhysiPKPD/src/PhysiPKPD_PD.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ using namespace PhysiCell;
1313

1414
class Pharmacodynamics_Model;
1515

16+
// possible future exercise: use this as abstract class and define inheritance from it for auc, sbml, etc.
1617
class Pharmacodynamics_Model
1718
{
1819
public:
@@ -26,7 +27,7 @@ class Pharmacodynamics_Model
2627
double dt = mechanics_dt; // mechanics_dt is the default time step for PD dynamics
2728
double previous_pd_time = 0.0;
2829
double next_pd_time = 0.0;
29-
30+
3031
double metabolism_reduction_factor;
3132
double damage_constant;
3233
double initial_substrate_coefficient;
@@ -51,8 +52,8 @@ void setup_pd_advancer(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_nod
5152
void setup_pd_model_auc(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_node);
5253
void setup_pd_model_sbml(Pharmacodynamics_Model *pPD, pugi::xml_node substrate_node);
5354
void single_pd_model(Pharmacodynamics_Model *pPD, double current_time);
54-
// void pd_phenotype_function( Cell* pC, Phenotype& p, double dt );
55-
// void pd_custom_function( Cell* pC, Phenotype& p, double dt );
55+
56+
double parse_substrate_to_damage_rate(const pugi::xml_node &substrate_node);
5657

5758
// Coloring and miscellaneous functions
5859
void intialize_damage_coloring(int nCD, std::vector<std::vector<int>> &default_colors, std::vector<std::vector<int>> &color_diffs_D1, std::vector<std::vector<int>> &color_diffs_D2, std::vector<std::vector<int>> &damage_inds, std::vector<std::vector<int>> &ec50_inds, std::vector<std::vector<int>> &hp_inds);

0 commit comments

Comments
 (0)