Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 69 additions & 6 deletions core/PhysiCell_cell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1151,19 +1151,80 @@ Cell* create_cell( Cell_Definition& cd )
return pNew;
}

// A transformation hands any in-progress attack to the new cell definition, which may be
// one that would never have started it: attack_rates default to zero, so a type the user
// never wrote attack parameters for inherits the attack anyway, and it is then the
// attack_damage_rate (1.0) and attack_duration (30 min) defaults that decide how much
// damage gets done. Whether such an attack should continue is a modelling question we
// cannot answer for the user, so nothing is changed here -- but this is easy to reach by
// accident, so say it out loud.
static void warn_if_attack_inherited_by_nonattacking_type( Cell_Definition& cd,
const std::string& old_type_name , Cell* pTarget )
{
if( pTarget == NULL )
{ return; }

// go through the target's type (an int) rather than its type_name: a target
// transforming on another thread in this same step would be rewriting that string
// underneath us. find_cell_definition_index only reads its map, so it is safe here.
int nTarget = find_cell_definition_index( pTarget->type );
if( nTarget < 0 || nTarget >= (int) cd.phenotype.cell_interactions.attack_rates.size() )
{ return; }

// the new type does attack this type, so inheriting the attack is self-consistent
if( cd.phenotype.cell_interactions.attack_rates[nTarget] > 0.0 )
{ return; }

std::string target_type_name = cell_definitions_by_index[nTarget]->name;

// the same transformation recurs throughout a run, so report each combination of
// (old type, new type, target type) once instead of once per cell per time step.
static std::vector<std::string> already_reported;

// a named critical: detach_cells_as_spring() and the attacked_by helpers take the
// unnamed one, and OpenMP criticals are not reentrant.
#pragma omp critical( attack_transformation_warning )
{
std::string key = old_type_name + ">" + cd.name + ">" + target_type_name;
if( std::find( already_reported.begin() , already_reported.end() , key )
== already_reported.end() )
{
already_reported.push_back( key );
std::cout << "Warning: a '" << old_type_name << "' transformed into a '" << cd.name
<< "' while attacking a '" << target_type_name << "'," << std::endl
<< "\tbut '" << cd.name << "' has an attack rate of 0 against '"
<< target_type_name << "', i.e. it would never start this attack itself."
<< std::endl
<< "\tThe attack continues regardless, for up to attack_duration = "
<< cd.phenotype.cell_interactions.attack_duration
<< " min at attack_damage_rate = "
<< cd.phenotype.cell_interactions.attack_damage_rate << "." << std::endl
<< "\tIf that is not what you intend, set the attack parameters for '" << cd.name
<< "' explicitly, or end the attack in a custom function." << std::endl
<< "\t(reported once per combination of transformation and target type)"
<< std::endl;
}
}
return;
}

void Cell::convert_to_cell_definition( Cell_Definition& cd )
{
Volume cell_volume = phenotype.volume;
Geometry cell_geometry = phenotype.geometry;
Molecular cell_molecular = phenotype.molecular;
Custom_Cell_Data cell_custom_data = custom_data;

// pAttackTarget is phenotype state, and the assignment below replaces it, so
// end this cell's own attack. attacked_by and the spring are in state, which
// transformation preserves, so attacks against this cell continue.
remove_self_from_attacked();
// should we also remove all attackers? That would be a change in behavior, so for now we don't.
// remove_all_attackers();
// pAttackTarget is phenotype state, and the assignment below replaces it, so hold
// on to it and put it back afterwards. attacked_by and the spring are in state,
// which transformation preserves, so restoring pAttackTarget keeps the whole
// attack link intact -- matching attacks against this cell, which already persist.
Cell* cell_attack_target = phenotype.cell_interactions.pAttackTarget;
// a lifetime tally of this cell's own doing, so carry it across the transformation
// rather than restarting it partway through an attack that is still running
double cell_total_damage_delivered = phenotype.cell_interactions.total_damage_delivered;
// type_name is still this cell's old one until it is overwritten below
warn_if_attack_inherited_by_nonattacking_type( cd , type_name , cell_attack_target );

// use the cell defaults;
type = cd.type;
Expand All @@ -1184,6 +1245,8 @@ void Cell::convert_to_cell_definition( Cell_Definition& cd )

phenotype.geometry = cell_geometry; // leave the geometry alone
phenotype.molecular.internalized_total_substrates = cell_molecular.internalized_total_substrates;
phenotype.cell_interactions.pAttackTarget = cell_attack_target; // leave any ongoing attack alone
phenotype.cell_interactions.total_damage_delivered = cell_total_damage_delivered;

for( int nn = 0 ; nn < custom_data.variables.size() ; nn++ )
{
Expand Down
Loading