From 9a7d0066aea3add141ce0065c2464d8e9b2dbecd Mon Sep 17 00:00:00 2001 From: Paul Macklin Date: Tue, 16 Jul 2024 01:47:56 -0400 Subject: [PATCH] updated XML input and output. way better. --- config/PhysiCell_settings.xml | 54 + core/PhysiCell_cell.cpp | 40 +- core/PhysiCell_cell.h | 2 +- core/PhysiCell_phenotype.cpp | 6 +- core/PhysiCell_phenotype.h | 6 +- core/PhysiCell_standard_models.cpp | 71 +- modules/PhysiCell_MultiCellDS.cpp | 2001 ++++++++++++++++++++++++++-- 7 files changed, 2040 insertions(+), 140 deletions(-) diff --git a/config/PhysiCell_settings.xml b/config/PhysiCell_settings.xml index df383f175..895a1ae27 100644 --- a/config/PhysiCell_settings.xml +++ b/config/PhysiCell_settings.xml @@ -214,6 +214,7 @@ 0.01 0.0 0.0 + 6 0.1 @@ -274,6 +275,9 @@ 0 + 0 + 0 + 0 0.0 @@ -291,6 +295,8 @@ 0.0 1 + 0.1 + 0 0.0 @@ -310,6 +316,10 @@ 0.0 + + 0.0 + 0.0 + 1.0 @@ -385,6 +395,7 @@ 0.01 0.0 0.0 + 6 1.0 @@ -445,6 +456,8 @@ 0.01 + 0 + 0 0.0 0.0 @@ -462,6 +475,7 @@ 0.0 1.0 + 0.1 0.0 0.0 @@ -481,6 +495,10 @@ 0.0 + + 0.0 + 0.0 + 0.0 @@ -556,6 +574,7 @@ 0.01 0.0 0.0 + 6 1.0 @@ -616,6 +635,8 @@ 0.01 + 0 + 0 0.0 0.0 @@ -633,6 +654,8 @@ 0.0 1.0 + 0.1 + 0.0 0.0 @@ -652,6 +675,10 @@ 0.0 + + 0.0 + 0.0 + 0.0 @@ -727,6 +754,7 @@ 0.01 0.0 0.0 + 6 1.0 @@ -787,6 +815,8 @@ 0.01 + 0 + 0 0.0 0.0 @@ -804,6 +834,8 @@ 0.0 1.0 + 0.1 + 0.0 0.0 @@ -823,6 +855,10 @@ 0.0 + + 0.0 + 0.0 + 0.0 @@ -898,6 +934,7 @@ 0.01 0.0 0.0 + 6 0.1 @@ -958,6 +995,8 @@ 0 + 0 + 0 0.0 0.0 @@ -975,6 +1014,8 @@ 0.0 1.0 + 0.1 + 0.0 0.0 @@ -994,6 +1035,10 @@ 0.0 + + 0.0 + 0.0 + 0.0 @@ -1069,6 +1114,7 @@ 0.01 0.0 0.0 + 6 0.01 @@ -1129,6 +1175,8 @@ 0 + 0 + 0 0.0 0.0 @@ -1146,6 +1194,8 @@ 0.0 1.0 + 0.1 + 0.0 0.0 @@ -1165,6 +1215,10 @@ 0.0 + + 0.0 + 0.0 + 0.0 diff --git a/core/PhysiCell_cell.cpp b/core/PhysiCell_cell.cpp index 436f79292..a34ad4b68 100644 --- a/core/PhysiCell_cell.cpp +++ b/core/PhysiCell_cell.cpp @@ -247,7 +247,7 @@ Cell_State::Cell_State() number_of_nuclei = 1; // damage = 0.0; - total_attack_time = 0.0; + // total_attack_time = 0.0; contact_with_basement_membrane = false; @@ -357,7 +357,7 @@ void Cell::advance_bundled_phenotype_functions( double dt_ ) phenotype.geometry.update( this, phenotype, dt_ ); // update integrity - phenotype.integrity.advance_damage( dt_ ); + phenotype.cell_integrity.advance_damage( dt_ ); // check for new death events if( phenotype.death.check_for_death( dt_ ) == true ) @@ -648,8 +648,10 @@ Cell* Cell::divide( ) // changes for new phenotyp March 2022 // state.damage = 0.0; + // phenotype.integrity.damage = 0.0; // leave alone - damage is heritable state.total_attack_time = 0; // child->state.damage = 0.0; + // child->phenotype.integrity.damage = 0.0; // leave alone - damage is heritable child->state.total_attack_time = 0.0; return child; @@ -1408,7 +1410,7 @@ void Cell::attack_cell( Cell* pCell_to_attack , double dt ) { // std::cout << this->type_name << " attacks " << pCell_to_attack->type_name << std::endl; // - pCell_to_attack->phenotype.integrity.damage += phenotype.cell_interactions.attack_damage_rate * dt; + pCell_to_attack->phenotype.cell_integrity.damage += phenotype.cell_interactions.attack_damage_rate * dt; pCell_to_attack->state.total_attack_time += dt; } return; @@ -2589,7 +2591,6 @@ Cell_Definition* initialize_cell_definition_from_pugixml( pugi::xml_node cd_node node_mech = node.child( "maximum_number_of_attachments" ); if ( node_mech ) { pM->maximum_number_of_attachments = xml_get_my_int_value( node_mech ); } - } // motility @@ -2836,6 +2837,19 @@ Cell_Definition* initialize_cell_definition_from_pugixml( pugi::xml_node cd_node pugi::xml_node node_dpr = node.child("dead_phagocytosis_rate"); pCI->dead_phagocytosis_rate = xml_get_my_double_value(node_dpr); + pCI->apoptotic_phagocytosis_rate = pCI->dead_phagocytosis_rate; + pCI->necrotic_phagocytosis_rate = pCI->dead_phagocytosis_rate; + + // if specific apoptotic rate is specified, overwrite + pugi::xml_node node_apr = node.child("apoptotic_phagocytosis_rate"); + if( node_apr ) + { pCI->apoptotic_phagocytosis_rate = xml_get_my_double_value(node_apr); } + + // if specific necrotic rate is specified, overwrite + pugi::xml_node node_npr = node.child("necrotic_phagocytosis_rate"); + if( node_npr ) + { pCI->necrotic_phagocytosis_rate = xml_get_my_double_value(node_npr); } + // live phagocytosis rates pugi::xml_node node_lpcr = node.child( "live_phagocytosis_rates"); if( node_lpcr ) @@ -2967,7 +2981,23 @@ Cell_Definition* initialize_cell_definition_from_pugixml( pugi::xml_node cd_node node_tr = node_tr.next_sibling( "transformation_rate" ); } - } + } + + // cell integrity + node = cd_node.child( "phenotype" ); + node = node.child( "cell_integrity" ); + if( node ) + { + Cell_Integrity *pCI = &(pCD->phenotype.cell_integrity); + + pugi::xml_node node_dr = node.child("damage_rate"); + if( node_dr ) + { pCI->damage_rate = xml_get_my_double_value( node_dr ); } + + pugi::xml_node node_drr = node.child("damage_repair_rate"); + if( node_drr ) + { pCI->damage_repair_rate = xml_get_my_double_value( node_drr ); } + } // intracellular node = cd_node.child( "phenotype" ); diff --git a/core/PhysiCell_cell.h b/core/PhysiCell_cell.h index 7dc390de7..6807390e2 100644 --- a/core/PhysiCell_cell.h +++ b/core/PhysiCell_cell.h @@ -155,7 +155,7 @@ class Cell_State int number_of_nuclei; // double damage; - double total_attack_time; + double total_attack_time; // now in interactions bool contact_with_basement_membrane; // not implemented yet Cell_State(); diff --git a/core/PhysiCell_phenotype.cpp b/core/PhysiCell_phenotype.cpp index d75917e7e..b2eb1c74a 100644 --- a/core/PhysiCell_phenotype.cpp +++ b/core/PhysiCell_phenotype.cpp @@ -1211,7 +1211,7 @@ Phenotype& Phenotype::operator=(const Phenotype &p ) { molecular = p.molecular; - integrity = p.integrity; + cell_integrity = p.cell_integrity; delete intracellular; @@ -1357,7 +1357,7 @@ double& Cell_Transformations::transformation_rate( std::string type_name ) } // beta functionality in 1.10.3 -Integrity::Integrity() +Cell_Integrity::Cell_Integrity() { damage = 0; damage_rate = 0.0; @@ -1377,7 +1377,7 @@ Integrity::Integrity() return; } -void Integrity::advance_damage( double dt ) +void Cell_Integrity::advance_damage( double dt ) { double temp1; double temp2; diff --git a/core/PhysiCell_phenotype.h b/core/PhysiCell_phenotype.h index ca4435beb..5d3617fa3 100644 --- a/core/PhysiCell_phenotype.h +++ b/core/PhysiCell_phenotype.h @@ -713,7 +713,7 @@ class Cell_Transformations }; // pre-beta functionality in 1.10.3 -class Integrity +class Cell_Integrity { private: public: @@ -738,7 +738,7 @@ class Integrity // mitochondrial? spindle? other? */ - Integrity(); + Cell_Integrity(); void advance_damage( double dt ); }; @@ -760,7 +760,7 @@ class Phenotype Molecular molecular; - Integrity integrity; + Cell_Integrity cell_integrity; // We need it to be a pointer to allow polymorphism // then this object could be a MaBoSSIntracellular, or a RoadRunnerIntracellular diff --git a/core/PhysiCell_standard_models.cpp b/core/PhysiCell_standard_models.cpp index e7f910e07..84431e3fd 100644 --- a/core/PhysiCell_standard_models.cpp +++ b/core/PhysiCell_standard_models.cpp @@ -1213,10 +1213,29 @@ void standard_cell_cell_interactions( Cell* pCell, Phenotype& phenotype, double bool apoptotic = (bool) get_single_signal( pTarget , "apoptotic" ); bool necrotic = (bool) get_single_signal( pTarget , "necrotic" ); - // dead phagocytosis + // apoptotic phagocytosis + probability = phenotype.cell_interactions.apoptotic_phagocytosis_rate * dt; + if( UniformRandom() < probability && phagocytosed == false && apoptotic == true ) // add the prior phago check in July 2024 + { + pCell->ingest_cell(pTarget); + phagocytosed = true; // was missing : bugfix + } + + // necrotic phagocytosis + probability = phenotype.cell_interactions.necrotic_phagocytosis_rate * dt; + if( UniformRandom() < probability && phagocytosed == false && necrotic == true ) // add the prior phago check in July 2024 + { + pCell->ingest_cell(pTarget); + phagocytosed = true; // was missing : bugfix + } + + // general dead phagocytosis probability = phenotype.cell_interactions.dead_phagocytosis_rate * dt; - if( UniformRandom() < probability ) - { pCell->ingest_cell(pTarget); } + if( UniformRandom() < probability && phagocytosed == false ) + { + pCell->ingest_cell(pTarget); + phagocytosed = true; // was missing : bugfix + } } else { @@ -1245,11 +1264,19 @@ void standard_cell_cell_interactions( Cell* pCell, Phenotype& phenotype, double pCell->phenotype.cell_interactions.pAttackTarget = pTarget; std::cout << "********* ********* ******** start atack **** " << probability << std::endl; attacked = true; + + // spring-link these cells + attach_cells_as_spring(pCell,pTarget); } } - // perform attack - if( pCell->phenotype.cell_interactions.pAttackTarget != NULL ) +/* + // perform attack. be careful to not overcount it! + // make sure that (1) I have a non-null pAttackTarget, and that (2) we are talking about pTarget + // easiest way to do this is to check if the pAttackTarget = pTarget. IF pAttackTarget = NULL, it + // won't be true. If pAttackTarget is non-null, but we're looking ata different cell, also not true. + // if pAttackTarget = pTarget, then we're "looking" at the right cell, so do the attack. + if( pCell->phenotype.cell_interactions.pAttackTarget == pTarget ) { pCell->attack_cell(pCell->phenotype.cell_interactions.pAttackTarget,dt); attacked = true; // attacked at least one cell in this time step @@ -1266,7 +1293,7 @@ void standard_cell_cell_interactions( Cell* pCell, Phenotype& phenotype, double pCell->phenotype.cell_interactions.pAttackTarget = NULL; } } - +*/ // fusion // assume you can only fuse once cell at a time @@ -1277,7 +1304,37 @@ void standard_cell_cell_interactions( Cell* pCell, Phenotype& phenotype, double fused = true; } } - } + } + + // move effector attack here. + + if( pCell->phenotype.cell_interactions.pAttackTarget != NULL ) + { + pCell->attack_cell(pCell->phenotype.cell_interactions.pAttackTarget,dt); + attacked = true; // attacked at least one cell in this time step + + // attack_cell + + // probability of ending attack + // end attack if target is dead + probability = dt / (1e-15 + pCell->phenotype.cell_interactions.attack_duration); + + if( UniformRandom() < probability || pCell->phenotype.cell_interactions.pAttackTarget->phenotype.death.dead ) + { + std::cout << "********* ********* ******** attack done **** " << probability << " " << + pCell->phenotype.cell_interactions.pAttackTarget->state.total_attack_time << " " + << (int) pCell->phenotype.cell_interactions.pAttackTarget->phenotype.death.dead << std::endl; + + detach_cells_as_spring(pCell,pCell->phenotype.cell_interactions.pAttackTarget); + + pCell->phenotype.cell_interactions.pAttackTarget = NULL; + } + } + + + // move decision if to end attack here. + + } void standard_cell_transformations( Cell* pCell, Phenotype& phenotype, double dt ) diff --git a/modules/PhysiCell_MultiCellDS.cpp b/modules/PhysiCell_MultiCellDS.cpp index 52632317b..0d6b5eb61 100644 --- a/modules/PhysiCell_MultiCellDS.cpp +++ b/modules/PhysiCell_MultiCellDS.cpp @@ -73,8 +73,10 @@ namespace PhysiCell{ void add_PhysiCell_cell_to_open_xml_pugi( pugi::xml_document& xml_dom, Cell& C ); // not implemented -- future edition -void add_PhysiCell_cells_to_open_xml_pugi( pugi::xml_document& xml_dom, std::string filename_base, Microenvironment& M ) +void add_PhysiCell_cells_to_open_xml_pugi_old( pugi::xml_document& xml_dom, std::string filename_base, Microenvironment& M ) { + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + static double temp_zero = 0.0; if( BioFVM::save_cell_data == false ) @@ -405,7 +407,28 @@ void add_PhysiCell_cells_to_open_xml_pugi( pugi::xml_document& xml_dom, std::str attrib.set_value( size ); node_temp1 = node_temp1.parent(); index += size; +/* + // apoptotic and necrotic phagocytosis rates + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "apoptotic_phagocytosis_rate" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "necrotic_phagocytosis_rate" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; +*/ // live_phagocytosis_rates size = number_of_cell_defs; @@ -553,7 +576,7 @@ void add_PhysiCell_cells_to_open_xml_pugi( pugi::xml_document& xml_dom, std::str // cell interactions: size_of_each_datum += - 1+number_of_cell_defs+number_of_cell_defs+1+number_of_cell_defs; + 1+number_of_cell_defs+number_of_cell_defs+1+number_of_cell_defs; // cell transformations; size_of_each_datum += number_of_cell_defs; @@ -582,7 +605,7 @@ void add_PhysiCell_cells_to_open_xml_pugi( pugi::xml_document& xml_dom, std::str } Cell* pCell; - // storing data as cols (each column is a cell) + // storing data as cols (each row is a cell) for( int i=0; i < number_of_data_entries ; i++ ) { // ID, x,y,z, total_volume @@ -646,12 +669,21 @@ void add_PhysiCell_cells_to_open_xml_pugi( pugi::xml_document& xml_dom, std::str fwrite( (char*) &( pCell->phenotype.motility.chemotactic_sensitivities ) , sizeof(double) , number_of_substrates , fp ); // chemotactic_sensitivities fwrite( (char*) &( pCell->phenotype.mechanics.cell_adhesion_affinities ) , sizeof(double) , number_of_cell_defs , fp ); // cell_adhesion_affinities fwrite( (char*) &( pCell->phenotype.cell_interactions.dead_phagocytosis_rate ) , sizeof(double) , 1 , fp ); // dead_phagocytosis_rate + + /* new July 2024 */ + // fwrite( (char*) &( pCell->phenotype.cell_interactions.apoptotic_phagocytosis_rate ) , sizeof(double) , 1 , fp ); // apoptotic_phagocytosis_rate + // fwrite( (char*) &( pCell->phenotype.cell_interactions.necrotic_phagocytosis_rate ) , sizeof(double) , 1 , fp ); // necrotic_phagocytosis_rate + + fwrite( (char*) &( pCell->phenotype.cell_interactions.live_phagocytosis_rates ) , sizeof(double) , number_of_cell_defs , fp ); // live_phagocytosis_rates fwrite( (char*) &( pCell->phenotype.cell_interactions.attack_rates ) , sizeof(double) , number_of_cell_defs , fp ); // attack_rates fwrite( (char*) &( pCell->phenotype.cell_interactions.attack_damage_rate ) , sizeof(double) , 1 , fp ); // attack_damage_rate fwrite( (char*) &( pCell->phenotype.cell_interactions.fusion_rates ) , sizeof(double) , number_of_cell_defs , fp ); // fusion_rates fwrite( (char*) &( pCell->phenotype.cell_transformations.transformation_rates ) , sizeof(double) , number_of_cell_defs , fp ); // transformation_rates + /* write teh cell integrity stuff */ + + // custom variables for( int j=0 ; j < pCell->custom_data.variables.size(); j++ ) { @@ -771,141 +803,814 @@ void add_PhysiCell_cells_to_open_xml_pugi( pugi::xml_document& xml_dom, std::str return; } -void add_PhysiCell_to_open_xml_pugi( pugi::xml_document& xml_dom , std::string filename_base, double current_simulation_time , Microenvironment& M ); - -void save_PhysiCell_to_MultiCellDS_xml_pugi( std::string filename_base , Microenvironment& M , double current_simulation_time) +// returns: +int add_label( pugi::xml_node& start_node, int start_index, std::string varname , int size ) { - // start with a standard BioFVM save - - add_BioFVM_to_open_xml_pugi( BioFVM::biofvm_doc , filename_base , current_simulation_time , M ); - - // now, add the PhysiCell data + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; - add_PhysiCell_cells_to_open_xml_pugi( BioFVM::biofvm_doc , filename_base , M ); - // add_PhysiCell_cells_to_open_xml_pugi_v2( BioFVM::biofvm_doc , filename_base , M ); - - // Lastly, save to the indicated filename - - char filename[1024]; - sprintf( filename , "%s.xml" , filename_base.c_str() ); - BioFVM::biofvm_doc.save_file( filename ); + start_node = start_node.append_child( "label" ); + start_node.append_child( pugi::node_pcdata ).set_value( varname.c_str() ); + pugi::xml_attribute attrib = start_node.append_attribute( "index" ); + attrib.set_value( index ); + attrib = start_node.append_attribute( "size" ); + attrib.set_value( size ); + start_node = start_node.parent(); - return; + return start_index + size; } -void save_PhysiCell_to_MultiCellDS_v2( std::string filename_base , Microenvironment& M , double current_simulation_time) +void add_PhysiCell_cells_to_open_xml_pugi( pugi::xml_document& xml_dom, std::string filename_base, Microenvironment& M ) { - // set some metadata + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; - BioFVM::MultiCellDS_version_string = "2"; - BioFVM::BioFVM_metadata.program.program_name = "PhysiCell"; - BioFVM::BioFVM_metadata.program.program_version = PhysiCell_Version; - BioFVM::BioFVM_metadata.program.program_URL = "http://physicell.org"; + static double temp_zero = 0.0; + + if( BioFVM::save_cell_data == false ) + { return; } + + pugi::xml_node root = xml_dom.child("MultiCellDS") ; + pugi::xml_node node = root.child( "cellular_information" ); + root = node; + + // Let's reduce memory allocations and sprintf calls. + // This reduces execution time by around 30%. (e.g., write time for 1,000,000 cells decreases from + // 45 to 30 seconds on an older machine. + static char* temp; + static bool initialized = false; + + static char rate_chars [1024]; + static char volume_chars [1024]; + static char diffusion_chars [1024]; + if( !initialized ) + { + temp = new char [1024]; + initialized = true; + + sprintf( rate_chars, "1/%s" , M.time_units.c_str() ); + sprintf( volume_chars, "%s^3" , M.spatial_units.c_str() ); + sprintf( diffusion_chars , "%s^2/%s", M.spatial_units.c_str() , M.time_units.c_str() ); + } + + node = node.child( "cell_populations" ); + if( !node ) + { + node = root.append_child( "cell_populations" ); + } + root = node; // root = cell_populations - BioFVM::BioFVM_metadata.program.creator.type = "creator"; - BioFVM::BioFVM_metadata.program.creator.surname = "Macklin"; - BioFVM::BioFVM_metadata.program.creator.given_names = "Paul"; - BioFVM::BioFVM_metadata.program.creator.email = "macklinp@iu.edu"; - BioFVM::BioFVM_metadata.program.creator.URL = "http://MathCancer.org"; - BioFVM::BioFVM_metadata.program.creator.organization = "Indiana University & PhysiCell Project"; - BioFVM::BioFVM_metadata.program.creator.department = "Intelligent Systems Engineering"; - BioFVM::BioFVM_metadata.program.creator.ORCID = "0000-0002-9925-0151"; + if( BioFVM::save_cells_as_custom_matlab == false ) + { + std::cout << "\n\n\t*** Warning: We haven't used the old XML-based cell agent output since forever.\n"; + std::cout << "\t We are using compact label/binary Matlab-based cell agent output because we are sane. ***\n\n"; + } - BioFVM::BioFVM_metadata.program.citation.DOI = "10.1371/journal.pcbi.1005991"; - BioFVM::BioFVM_metadata.program.citation.PMID = "29474446"; - BioFVM::BioFVM_metadata.program.citation.PMCID = "PMC5841829"; - BioFVM::BioFVM_metadata.program.citation.text = "A Ghaffarizadeh, R Heiland, SH Friedman, SM Mumenthaler, and P Macklin. PhysiCell: an Open Source Physics-Based Cell Simulator for Multicellular Systems, PLoS Comput. Biol. 14(2): e1005991, 2018. DOI: 10.1371/journal.pcbi.1005991"; - BioFVM::BioFVM_metadata.program.citation.notes = ""; - BioFVM::BioFVM_metadata.program.citation.URL = "https://dx.doi.org/PMC5841829"; + // We _are_ using the customized matlab data, do it here. - // start with a standard BioFVM save - // overall XML structure - add_MultiCellDS_main_structure_to_open_xml_pugi( BioFVM::biofvm_doc ); - // save metadata - BioFVM_metadata.add_to_open_xml_pugi( current_simulation_time , BioFVM::biofvm_doc ); - // save diffusing substrates - add_BioFVM_substrates_to_open_xml_pugi( BioFVM::biofvm_doc , filename_base, M ); + node = node.child( "cell_population" ); + if( !node ) + { + node = root.append_child( "cell_population" ); + pugi::xml_attribute attrib = node.append_attribute( "type" ); + attrib.set_value( "individual" ); + } + + if( !node.child( "custom" ) ) + { + node.append_child( "custom" ); + } + node = node.child( "custom" ); + + // look for a node called simplified_data, with source = PhysiCell + + pugi::xml_node node_temp = node.child( "simplified_data" ); + bool temp_search_done = false; + while( !temp_search_done && node_temp ) + { + if( node_temp ) + { + pugi::xml_attribute attribute_temp = node_temp.attribute( "source" ); + if( attribute_temp ) + { + if( strcmp( attribute_temp.value() , "PhysiCell" ) == 0 ) + { + temp_search_done = true; + } + else + { + node_temp = node_temp.next_sibling(); + } + } + } + else + { + node_temp = (pugi::xml_node) NULL; + } + } - // add_BioFVM_agents_to_open_xml_pugi( xml_dom , filename_base, M); + static int single_cell_datum_size = 0; - // now, add the PhysiCell data + if( !node_temp ) + { + node_temp = node.append_child( "simplified_data" ); + pugi::xml_attribute attrib = node_temp.append_attribute( "type" ); + attrib.set_value( "matlab" ) ; + + attrib = node_temp.append_attribute( "source" ); + attrib.set_value("PhysiCell"); + + int index = 0; + int size = 1; + + pugi::xml_node node_temp1 = node_temp.append_child( "labels" ); + + // ID,x,y,z,total volume + /* + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "ID" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + */ - // add_PhysiCell_cells_to_open_xml_pugi( BioFVM::biofvm_doc , filename_base , M ); - add_PhysiCell_cells_to_open_xml_pugi_v2( BioFVM::biofvm_doc , filename_base , M ); + index = add_label( node_temp1, index, "ID", 1 ); + single_cell_datum_size = index; - // Lastly, save to the indicated filename + size = 3; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "position" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; - char filename[1024]; - sprintf( filename , "%s.xml" , filename_base.c_str() ); - BioFVM::biofvm_doc.save_file( filename ); + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "total_volume" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // type, cycle model, current phase, elapsed time in phase, + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "cell_type" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "cycle_model" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "current_phase" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "elapsed_time_in_phase" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // nuclear volume, cytoplasmic volume, fluid fraction, calcified fraction, + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "nuclear_volume" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "cytoplasmic_volume" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "fluid_fraction" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; - return; -} + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "calcified_fraction" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // orientation, polarity -void add_PhysiCell_cells_to_open_xml_pugi_v2( pugi::xml_document& xml_dom, std::string filename_base, Microenvironment& M ) -{ - // get number of substrates - static int m = microenvironment.number_of_densities(); // number_of_substrates - // get number of cell types - static int n = cell_definition_indices_by_name.size(); // number_of_cell_types - // get number of death models - static int nd = (*all_cells)[0]->phenotype.death.rates.size(); // - // get number of custom data - static int nc = 0; // - static int nc_scalar = 0; - static int nc_vector = 0; + size = 3; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "orientation" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "polarity" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // motility + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "migration_speed" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; - static int cell_data_size = 0; + size = 3; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "motility_vector" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "migration_bias" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 3; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "motility_bias_direction" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "persistence_time" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "motility_reserved" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; - static bool legends_done= false; - static std::vector data_names; - static std::vector data_units; - static std::vector data_start_indices; - static std::vector data_sizes; + extern std::unordered_map cell_definition_indices_by_name; + int number_of_cell_defs = cell_definition_indices_by_name.size(); + int number_of_substrates = microenvironment.number_of_densities(); - // set up the labels - if( legends_done == false ) - { - data_names.clear(); - data_sizes.clear(); - data_start_indices.clear(); - data_units.clear(); + // new in 2022: chemotactic sensitivies + size = number_of_substrates; // number_of_cell_defs; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "chemotactic_sensitivities" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // new in 2022: adhesive affinities + size = number_of_cell_defs; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "adhesive_affinities" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; - std::string name; - std::string units; - int size; - int index = 0; + // save elastic adhsion stuff -// compatibilty : first 17 entries - // ID - name = "ID"; + + + // new in 2022: interactions : + // // phagocytosis parameters (e.g., macrophages) + + // dead_phagocytosis_rate size = 1; - units="none"; - data_names.push_back( name ); - data_units.push_back(units); - data_sizes.push_back( size ); - data_start_indices.push_back( index ); - cell_data_size += size; - index += size; - // - name = "position"; - size = 3; - units="microns"; - data_names.push_back( name ); - data_units.push_back(units); - data_sizes.push_back( size ); - data_start_indices.push_back( index ); - cell_data_size += size; - index += size; - // - name = "total_volume"; - units = "cubic microns"; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "dead_phagocytosis_rate" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // apoptotic and necrotic phagocytosis rates size = 1; - data_names.push_back( name ); - data_units.push_back(units); + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "apoptotic_phagocytosis_rate" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "necrotic_phagocytosis_rate" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // live_phagocytosis_rates + size = number_of_cell_defs; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "live_phagocytosis_rates" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // attack_rates + size = number_of_cell_defs; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "attack_rates" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // attack_damage_rate + size = 1; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "attack_damage_rate" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // attack duration + + // fusion_rates + size = number_of_cell_defs; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "fusion_rates" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // new in 2022: transformations : + size = number_of_cell_defs; + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( "transformation_rates" ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + + // cell integrity + + // custom variables + for( int i=0; i < (*all_cells)[0]->custom_data.variables.size(); i++ ) + { + size = 1; + char szTemp [1024]; + strcpy( szTemp, (*all_cells)[0]->custom_data.variables[i].name.c_str() ); + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( szTemp ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + } + // custom vector variables + for( int i=0; i < (*all_cells)[0]->custom_data.vector_variables.size(); i++ ) + { + size = (*all_cells)[0]->custom_data.vector_variables[i].value.size(); + char szTemp [1024]; + strcpy( szTemp, (*all_cells)[0]->custom_data.vector_variables[i].name.c_str() ); + node_temp1 = node_temp1.append_child( "label" ); + node_temp1.append_child( pugi::node_pcdata ).set_value( szTemp ); + attrib = node_temp1.append_attribute( "index" ); + attrib.set_value( index ); + attrib = node_temp1.append_attribute( "size" ); + attrib.set_value( size ); + node_temp1 = node_temp1.parent(); + index += size; + single_cell_datum_size += size; + } + + } + node = node_temp; + + if( !node.child( "filename" ) ) + { + node.append_child( "filename" ); + } + node = node.child( "filename" ); + + // next, filename + char filename [1024]; + sprintf( filename , "%s_cells_physicell.mat" , filename_base.c_str() ); + + /* store filename without the relative pathing (if any) */ + char filename_without_pathing [1024]; + char* filename_start = strrchr( filename , '/' ); + if( filename_start == NULL ) + { filename_start = filename; } + else + { filename_start++; } + strcpy( filename_without_pathing , filename_start ); + + if( !node.first_child() ) + { + node.append_child( pugi::node_pcdata ).set_value( filename_without_pathing ); // filename ); + } + else + { + node.first_child().set_value( filename_without_pathing ); // filename ); + } + + // next, create a matlab structure and save it! + + // order: ID,x,y,z,total volume, (same as BioFVM custom data, but instead of secretions ...) + // type, cycle model, current phase, elapsed time in phase, + // nuclear volume, cytoplasmic volume, fluid fraction, calcified fraction, + // orientation, polarity + + int number_of_data_entries = (*all_cells).size(); + int size_of_each_datum = single_cell_datum_size; + + extern std::unordered_map cell_definition_indices_by_name; + int number_of_substrates = microenvironment.number_of_densities(); + int number_of_cell_defs = cell_definition_indices_by_name.size(); + + FILE* fp = write_matlab_header( size_of_each_datum, number_of_data_entries, filename, "cells" ); + if( fp == NULL ) + { + std::cout << std::endl << "Error: Failed to open " << filename << " for MAT writing." << std::endl << std::endl; + + std::cout << std::endl << "Error: We're not writing data like we expect. " << std::endl + << "Check to make sure your save directory exists. " << std::endl << std::endl + << "I'm going to exit with a crash code of -1 now until " << std::endl + << "you fix your directory. Sorry!" << std::endl << std::endl; + exit(-1); + } + Cell* pCell; + + // storing data as cols (each row is a cell) + for( int i=0; i < number_of_data_entries ; i++ ) + { + // ID, x,y,z, total_volume + double ID_temp = (double) (*all_cells)[i]->ID; + fwrite( (char*) &( ID_temp ) , sizeof(double) , 1 , fp ); + + pCell = (*all_cells)[i]; + + fwrite( (char*) &( pCell->position[0] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->position[1] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->position[2] ) , sizeof(double) , 1 , fp ); + double dTemp = pCell->phenotype.volume.total; // get_total_volume(); + fwrite( (char*) &( dTemp ) , sizeof(double) , 1 , fp ); + + // type, cycle model, current phase, elapsed time in phase, + dTemp = (double) pCell->type; + fwrite( (char*) &( dTemp ) , sizeof(double) , 1 , fp ); // cell type + + dTemp = (double) pCell->phenotype.cycle.model().code; + fwrite( (char*) &( dTemp ) , sizeof(double) , 1 , fp ); // cycle model + + dTemp = (double) pCell->phenotype.cycle.current_phase().code; + fwrite( (char*) &( dTemp ) , sizeof(double) , 1 , fp ); // current phase + + // dTemp = pCell->phenotype.cycle.phases[pCell->phenotype.current_phase_index].elapsed_time; + fwrite( (char*) &( pCell->phenotype.cycle.data.elapsed_time_in_phase ) , sizeof(double) , 1 , fp ); // elapsed time in phase + + // volume information + // nuclear volume, cytoplasmic volume, fluid fraction, calcified fraction, + fwrite( (char*) &( pCell->phenotype.volume.nuclear ) , sizeof(double) , 1 , fp ); // nuclear volume + fwrite( (char*) &( pCell->phenotype.volume.cytoplasmic ) , sizeof(double) , 1 , fp ); // cytoplasmic volume + fwrite( (char*) &( pCell->phenotype.volume.fluid_fraction ) , sizeof(double) , 1 , fp ); // fluid fraction + fwrite( (char*) &( pCell->phenotype.volume.calcified_fraction ) , sizeof(double) , 1 , fp ); // calcified fraction + + // orientation, polarity; + fwrite( (char*) &( pCell->state.orientation[0] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->state.orientation[1] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->state.orientation[2] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->phenotype.geometry.polarity ) , sizeof(double) , 1 , fp ); + + // motility information + fwrite( (char*) &( pCell->phenotype.motility.migration_speed ) , sizeof(double) , 1 , fp ); // speed + fwrite( (char*) &( pCell->phenotype.motility.motility_vector[0] ) , sizeof(double) , 1 , fp ); // velocity + fwrite( (char*) &( pCell->phenotype.motility.motility_vector[1] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->phenotype.motility.motility_vector[2] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->phenotype.motility.migration_bias ) , sizeof(double) , 1 , fp ); // bias (0 to 1) + fwrite( (char*) &( pCell->phenotype.motility.migration_bias_direction[0] ) , sizeof(double) , 1 , fp ); // bias direction + fwrite( (char*) &( pCell->phenotype.motility.migration_bias_direction[1] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->phenotype.motility.migration_bias_direction[2] ) , sizeof(double) , 1 , fp ); + fwrite( (char*) &( pCell->phenotype.motility.persistence_time ) , sizeof(double) , 1 , fp ); // persistence + fwrite( (char*) &( temp_zero ) , sizeof(double) , 1 , fp ); // reserved for "time in this direction" + + // new in 2022: interactions : + // fwrite( (char*) &( pCell->phenotype.motility.chemotactic_sensitivities ) , sizeof(double) , number_of_cell_defs , fp ); // chemotactic_sensitivities + fwrite( (char*) &( pCell->phenotype.motility.chemotactic_sensitivities ) , sizeof(double) , number_of_substrates , fp ); // chemotactic_sensitivities + fwrite( (char*) &( pCell->phenotype.mechanics.cell_adhesion_affinities ) , sizeof(double) , number_of_cell_defs , fp ); // cell_adhesion_affinities + fwrite( (char*) &( pCell->phenotype.cell_interactions.dead_phagocytosis_rate ) , sizeof(double) , 1 , fp ); // dead_phagocytosis_rate + + /* new July 2024 */ + fwrite( (char*) &( pCell->phenotype.cell_interactions.apoptotic_phagocytosis_rate ) , sizeof(double) , 1 , fp ); // apoptotic_phagocytosis_rate + fwrite( (char*) &( pCell->phenotype.cell_interactions.necrotic_phagocytosis_rate ) , sizeof(double) , 1 , fp ); // necrotic_phagocytosis_rate + + + fwrite( (char*) &( pCell->phenotype.cell_interactions.live_phagocytosis_rates ) , sizeof(double) , number_of_cell_defs , fp ); // live_phagocytosis_rates + fwrite( (char*) &( pCell->phenotype.cell_interactions.attack_rates ) , sizeof(double) , number_of_cell_defs , fp ); // attack_rates + fwrite( (char*) &( pCell->phenotype.cell_interactions.attack_damage_rate ) , sizeof(double) , 1 , fp ); // attack_damage_rate + fwrite( (char*) &( pCell->phenotype.cell_interactions.fusion_rates ) , sizeof(double) , number_of_cell_defs , fp ); // fusion_rates + fwrite( (char*) &( pCell->phenotype.cell_transformations.transformation_rates ) , sizeof(double) , number_of_cell_defs , fp ); // transformation_rates + + /* write teh cell integrity stuff */ + + + // custom variables + for( int j=0 ; j < pCell->custom_data.variables.size(); j++ ) + { + fwrite( (char*) &( pCell->custom_data.variables[j].value ) , sizeof(double) , 1 , fp ); + } + + // custom vector variables + for( int j=0 ; j < pCell->custom_data.vector_variables.size(); j++ ) + { + for( int k=0; k < pCell->custom_data.vector_variables[j].value.size(); k++ ) + { + fwrite( (char*) &( pCell->custom_data.vector_variables[j].value[k] ) , sizeof(double) , 1 , fp ); + } + } + + } + + fclose( fp ); + + + return; + +} + + + +void add_PhysiCell_to_open_xml_pugi( pugi::xml_document& xml_dom , std::string filename_base, double current_simulation_time , Microenvironment& M ); + +void save_PhysiCell_to_MultiCellDS_xml_pugi( std::string filename_base , Microenvironment& M , double current_simulation_time) +{ + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + + // start with a standard BioFVM save + + add_BioFVM_to_open_xml_pugi( BioFVM::biofvm_doc , filename_base , current_simulation_time , M ); + + // now, add the PhysiCell data + + add_PhysiCell_cells_to_open_xml_pugi( BioFVM::biofvm_doc , filename_base , M ); + // add_PhysiCell_cells_to_open_xml_pugi_v2( BioFVM::biofvm_doc , filename_base , M ); + + // Lastly, save to the indicated filename + + char filename[1024]; + sprintf( filename , "%s.xml" , filename_base.c_str() ); + BioFVM::biofvm_doc.save_file( filename ); + + return; +} + + +void save_PhysiCell_to_MultiCellDS_v2( std::string filename_base , Microenvironment& M , double current_simulation_time) +{ + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + + // set some metadata + + BioFVM::MultiCellDS_version_string = "2"; + BioFVM::BioFVM_metadata.program.program_name = "PhysiCell"; + BioFVM::BioFVM_metadata.program.program_version = PhysiCell_Version; + BioFVM::BioFVM_metadata.program.program_URL = "http://physicell.org"; + + BioFVM::BioFVM_metadata.program.creator.type = "creator"; + BioFVM::BioFVM_metadata.program.creator.surname = "Macklin"; + BioFVM::BioFVM_metadata.program.creator.given_names = "Paul"; + BioFVM::BioFVM_metadata.program.creator.email = "macklinp@iu.edu"; + BioFVM::BioFVM_metadata.program.creator.URL = "http://MathCancer.org"; + BioFVM::BioFVM_metadata.program.creator.organization = "Indiana University & PhysiCell Project"; + BioFVM::BioFVM_metadata.program.creator.department = "Intelligent Systems Engineering"; + BioFVM::BioFVM_metadata.program.creator.ORCID = "0000-0002-9925-0151"; + + BioFVM::BioFVM_metadata.program.citation.DOI = "10.1371/journal.pcbi.1005991"; + BioFVM::BioFVM_metadata.program.citation.PMID = "29474446"; + BioFVM::BioFVM_metadata.program.citation.PMCID = "PMC5841829"; + BioFVM::BioFVM_metadata.program.citation.text = "A Ghaffarizadeh, R Heiland, SH Friedman, SM Mumenthaler, and P Macklin. PhysiCell: an Open Source Physics-Based Cell Simulator for Multicellular Systems, PLoS Comput. Biol. 14(2): e1005991, 2018. DOI: 10.1371/journal.pcbi.1005991"; + BioFVM::BioFVM_metadata.program.citation.notes = ""; + BioFVM::BioFVM_metadata.program.citation.URL = "https://dx.doi.org/PMC5841829"; + + // start with a standard BioFVM save + // overall XML structure + add_MultiCellDS_main_structure_to_open_xml_pugi( BioFVM::biofvm_doc ); + // save metadata + BioFVM_metadata.add_to_open_xml_pugi( current_simulation_time , BioFVM::biofvm_doc ); + // save diffusing substrates + add_BioFVM_substrates_to_open_xml_pugi( BioFVM::biofvm_doc , filename_base, M ); + + // add_BioFVM_agents_to_open_xml_pugi( xml_dom , filename_base, M); + + // now, add the PhysiCell data + + // add_PhysiCell_cells_to_open_xml_pugi( BioFVM::biofvm_doc , filename_base , M ); + add_PhysiCell_cells_to_open_xml_pugi_v2( BioFVM::biofvm_doc , filename_base , M ); + + // Lastly, save to the indicated filename + + char filename[1024]; + sprintf( filename , "%s.xml" , filename_base.c_str() ); + BioFVM::biofvm_doc.save_file( filename ); + + return; +} + +void add_PhysiCell_cells_to_open_xml_pugi_v2_old( pugi::xml_document& xml_dom, std::string filename_base, Microenvironment& M ) +{ + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + + // get number of substrates + static int m = microenvironment.number_of_densities(); // number_of_substrates + // get number of cell types + static int n = cell_definition_indices_by_name.size(); // number_of_cell_types + // get number of death models + static int nd = (*all_cells)[0]->phenotype.death.rates.size(); // + // get number of custom data + static int nc = 0; // + static int nc_scalar = 0; + static int nc_vector = 0; + + static int cell_data_size = 0; + + + static bool legends_done= false; + static std::vector data_names; + static std::vector data_units; + static std::vector data_start_indices; + static std::vector data_sizes; + + // set up the labels + if( legends_done == false ) + { + data_names.clear(); + data_sizes.clear(); + data_start_indices.clear(); + data_units.clear(); + + std::string name; + std::string units; + int size; + int index = 0; + + +// compatibilty : first 17 entries + // ID + name = "ID"; + size = 1; + units="none"; + data_names.push_back( name ); + data_units.push_back(units); + data_sizes.push_back( size ); + data_start_indices.push_back( index ); + cell_data_size += size; + index += size; + // + name = "position"; + size = 3; + units="microns"; + data_names.push_back( name ); + data_units.push_back(units); + data_sizes.push_back( size ); + data_start_indices.push_back( index ); + cell_data_size += size; + index += size; + // + name = "total_volume"; + units = "cubic microns"; + size = 1; + data_names.push_back( name ); + data_units.push_back(units); data_sizes.push_back( size ); data_start_indices.push_back( index ); cell_data_size += size; @@ -1792,7 +2497,7 @@ void add_PhysiCell_cells_to_open_xml_pugi_v2( pugi::xml_document& xml_dom, std:: dTemp = (double) pCell->state.number_of_nuclei; std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); // name = "damage"; - std::fwrite( &( pCell->phenotype.integrity.damage ) , sizeof(double) , 1 , fp ); + std::fwrite( &( pCell->phenotype.cell_integrity.damage ) , sizeof(double) , 1 , fp ); // name = "total_attack_time"; std::fwrite( &( pCell->state.total_attack_time ) , sizeof(double) , 1 , fp ); // name = "contact_with_basement_membrane"; @@ -2144,16 +2849,1066 @@ void add_PhysiCell_cells_to_open_xml_pugi_v2( pugi::xml_document& xml_dom, std:: return; } -void write_neighbor_graph( std::string filename ) +/* look here */ + +int total_data_size( std::vector& data_sizes ) { - /* - char filename [1024]; - sprintf( filename , "%s_cell_neighbor_graph.txt" , filename_base.c_str() ); - - // store filename without the relative pathing (if any) - char filename_without_pathing [1024]; - char* filename_start = strrchr( filename , '/' ); - if( filename_start == NULL ) + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + + // current index: + int total = 0; + for( int i = 0 ; i < data_sizes.size(); i++ ) + { total += data_sizes[i]; } + return total; +} + +void add_variable_to_labels( std::vector& data_names , + std::vector& data_units, + std::vector& data_start_indices, + std::vector& data_sizes, + std::string var_name, std::string var_units, int var_size ) +{ + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + + // current index: + int index = 0; + for( int i = 0 ; i < data_sizes.size(); i++ ) + { index += data_sizes[i]; } + + data_names.push_back( var_name ); + data_units.push_back( var_units ); + data_sizes.push_back( var_size ); + data_start_indices.push_back( index ); + + return; +} + +void add_PhysiCell_cells_to_open_xml_pugi_v2( pugi::xml_document& xml_dom, std::string filename_base, Microenvironment& M ) +{ + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + + // get number of substrates + static int m = microenvironment.number_of_densities(); // number_of_substrates + // get number of cell types + static int n = cell_definition_indices_by_name.size(); // number_of_cell_types + // get number of death models + static int nd = (*all_cells)[0]->phenotype.death.rates.size(); // + // get number of custom data + static int nc = 0; // + static int nc_scalar = 0; + static int nc_vector = 0; + + static int cell_data_size = 0; + + static bool legend_done = false; + static std::vector data_names; + static std::vector data_units; + static std::vector data_start_indices; + static std::vector data_sizes; + + // set up the cell types labels + + static bool cell_types_legend_done = false; + static std::vector cell_type_names; + static std::vector cell_type_indices; + static std::vector cell_type_IDs; + + + if( cell_types_legend_done == false ) + { + cell_type_names.clear(); + cell_type_IDs.clear(); + cell_type_indices.clear(); + + for( int j=0; j < cell_definitions_by_index.size() ; j++ ) + { + Cell_Definition* pCD = cell_definitions_by_index[j]; + int index = j; + int type = pCD->type; + std::string name = pCD->name; + cell_type_names.push_back( name ); + cell_type_IDs.push_back( type ); + cell_type_indices.push_back( index); + } + + for( int j=0; j < n ; j++ ) + { + std::cout << cell_type_indices[j] << " : " << cell_type_IDs[j] << " " << cell_type_names[j] << std::endl; + } + + cell_types_legend_done = true; + } + + + // set up the labels + if( legend_done == false ) + { + data_names.clear(); + data_sizes.clear(); + data_start_indices.clear(); + data_units.clear(); + + std::string name; + std::string units; + int size; + int index = 0; + +// compatibilty : first 17 entries + // ID + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "ID" , "none" , 1 ) ; + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "position" , "microns" , 3 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "total_volume" , "cubic microns" , 1 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cell_type" , "none" , 1 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cycle_model" , "none" , 1 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "current_phase" , "none" , 1 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "elapsed_time_in_phase" , "min" , 1 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "nuclear_volume" , "cubic microns" , 1 ); + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cytoplasmic_volume" , "cubic microns" , 1 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "fluid_fraction" , "none" , 1 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "calcified_fraction" , "none" , 1 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "orientation" , "none" , 3 ); + + // + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "polarity" , "none" , 1 ); + + /* state variables to save */ + // state + // velocity // 3 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "velocity" , "micron/min" , 3 ); + + // pressure // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "pressure" , "none" , 1 ); + + // number of nuclei // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "number_of_nuclei" , "none" , 1 ); + + // damage // 1 // this is in cell_integrity now + // add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + // "damage" , "none" , 1 ); + + + // total attack time // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "total_attack_time" , "min" , 1 ); + + // contact_with_basement_membrane // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "contact_with_basement_membrane" , "none" , 1 ); + + /* now go through phenotype and state */ + // cycle + // cycle model // already above + // current phase // already above + // current exit rate // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "current_cycle_phase_exit_rate" , "1/min" , 1 ); + + // elapsed time in phase // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "elapsed_time_in_phase" , "min" , 1 ); + + // death + // live or dead state // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "dead" , "none" , 1 ); + + // current death model // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "current_death_model" , "none" , 1 ); + + // death rates // nd + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "death_rates" , "1/min" , nd ); + // + + // volume () + // cytoplasmic_biomass_change_rate // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cytoplasmic_biomass_change_rate" , "1/min" , 1 ); + + // nuclear_biomass_change_rate; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "nuclear_biomass_change_rate" , "1/min" , 1 ); + + // fluid_change_rate; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "fluid_change_rate" , "1/min" , 1 ); + + // calcification_rate; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "calcification_rate" , "1/min" , 1 ); + + // target_solid_cytoplasmic; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "target_solid_cytoplasmic" , "cubic microns" , 1 ); + + // target_solid_nuclear; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "target_solid_nuclear" , "cubic microns" , 1 ); + + // target_fluid_fraction; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "target_fluid_fraction" , "none" , 1 ); + + // geometry + // radius //1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "radius" , "microns" , 1 ); + + // nuclear_radius // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "nuclear_radius" , "microns" , 1 ); + + // surface_area //1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "surface_area" , "square microns" , 1 ); + + // polarity // arleady done + + // mechanics + // cell_cell_adhesion_strength; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cell_cell_adhesion_strength" , "micron/min" , 1 ); + + // cell_BM_adhesion_strength; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cell_BM_adhesion_strength" , "micron/min" , 1 ); + + // cell_cell_repulsion_strength; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cell_cell_repulsion_strength" , "micron/min" , 1 ); + + // cell_BM_repulsion_strength; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cell_BM_repulsion_strength" , "micron/min" , 1 ); + + // std::vector cell_adhesion_affinities; // n + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "cell_adhesion_affinities" , "none" , n ); + + // relative_maximum_adhesion_distance; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "relative_maximum_adhesion_distance" , "none" , 1 ); + + // maximum_number_of_attachments; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "maximum_number_of_attachments" , "none" , 1 ); + + // attachment_elastic_constant; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "attachment_elastic_constant" , "1/min" , 1 ); + + // attachment_rate; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "attachment_rate" , "1/min" , 1 ); + + // detachment_rate; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "detachment_rate" , "1/min" , 1 ); + + // Motility + // is_motile // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "is_motile" , "none" , 1 ); + + // persistence_time; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "persistence_time" , "min" , 1 ); + + // migration_speed; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "migration_speed" , "micron/min" , 1 ); + + // std::vector migration_bias_direction; // 3 // motility_bias_direction originally + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "migration_bias_direction" , "none" , 3 ); + + // migration_bias; //1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "migration_bias" , "none" , 1 ); + + // std::vector motility_vector; // 3 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "motility_vector" , "micron/min" , 3 ); + + // chemotaxis_index; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "chemotaxis_index" , "none" , 1 ); + + // chemotaxis_direction; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "chemotaxis_direction" , "none" , 1 ); + + // advanced chemotaxis + // std::vector chemotactic_sensitivities; // m + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "chemotactic_sensitivities" , "none" , m ); + + // secretion + // std::vector secretion_rates; // m + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "secretion_rates" , "1/min" , m ); + + // std::vector uptake_rates; // m + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "uptake_rates" , "1/min" , m ); + + // std::vector saturation_densities; // m + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "ssaturation_densities" , "stuff/cubic micron" , m ); + + // std::vector net_export_rates; // m + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "snet_export_rates" , "stuff/min" , m ); + + // molecular + // internalized_total_substrates // m + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "internalized_total_substrates" , "stuff" , m ); + + // fraction_released_at_death // m + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "sfraction_released_at_death" , "none" , m ); + + // fraction_transferred_when_ingested //m + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "fraction_transferred_when_ingested" , "none" , m ); + + // interactions + // dead_phagocytosis_rate; // 1 + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "dead_phagocytosis_rate" , "1/min" , 1 ); + + // apoptotic phagocytosis_rate // new + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "apoptotic_phagocytosis_rate" , "1/min" , 1 ); + + // necrotic phagocytosis rate // new + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "necrotic_phagocytosis_rate" , "1/min" , 1 ); + + // std::vector live_phagocytosis_rates; // n + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "live_phagocytosis_rates" , "1/min" , n ); + + // std::vector attack_rates; // n + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "attack_rates" , "1/min" , n ); + + // std::vector immunogenicities; n // was missing + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "immunogenicities" , "none" , n ); + + // pAttackTarget; 1 // new -- use the cell ID + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "attack_target" , "none" , 1 ); + + // double (attack_)damage_rate; 1 // changed from damage_rate + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "attack_damage_rate" , "1/min" , 1 ); + + // double attack_duration; 1 // new + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "attack_duration" , "min" , 1 ); + + // double total_damage_delivered; 1 // new + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "attack_total_damage_delivered" , "none" , 1 ); + + // std::vector fusion_rates; // n + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "fusion_rates" , "1/min" , n ); + + // transformations + // std::vector transformation_rates; // n + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "transformation_rates" , "1/min" , n ); + + // cell integrity + + // double damage; + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "damage" , "none" , 1 ); + + // double damage_rate; // new use of old name! now the rate of undergoing damage (not by attack) + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "damage_rate" , "1/min" , 1 ); + + // double damage_repair_rate; + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + "damage_repair_rate" , "1/min" , 1 ); + +// custom + for( int j=0 ; j < (*all_cells)[0]->custom_data.variables.size(); j++ ) + { + name = (*all_cells)[0]->custom_data.variables[j].name; + units = (*all_cells)[0]->custom_data.variables[j].units; + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + name,units,1 ); + } + + // custom vector variables + for( int j=0 ; j < (*all_cells)[0]->custom_data.vector_variables.size(); j++ ) + { + name = (*all_cells)[0]->custom_data.vector_variables[j].name; + units = (*all_cells)[0]->custom_data.vector_variables[j].units; + size = (*all_cells)[0]->custom_data.vector_variables[j].value.size(); + add_variable_to_labels( data_names,data_units,data_start_indices,data_sizes, + name,units,size ); + } + + cell_data_size = total_data_size( data_sizes ); + legend_done = true; + } + + // get ready for XML navigation + // pugi::xml_document& xml_dom = BioFVM::biofvm_doc; + + pugi::xml_node root = xml_dom.child("MultiCellDS") ; + pugi::xml_node node = root.child( "cellular_information" ); // root = cellular_information + root = node; + + // Let's reduce memory allocations and sprintf calls. + // This reduces execution time. + static char* temp; + static bool initialized = false; + + static char rate_chars [1024]; + static char volume_chars [1024]; + static char diffusion_chars [1024]; + if( !initialized ) + { + temp = new char [1024]; + initialized = true; + + sprintf( rate_chars, "1/%s" , M.time_units.c_str() ); + sprintf( volume_chars, "%s^3" , M.spatial_units.c_str() ); + sprintf( diffusion_chars , "%s^2/%s", M.spatial_units.c_str() , M.time_units.c_str() ); + } + + node = node.child( "cell_populations" ); + if( !node ) + { + node = root.append_child( "cell_populations" ); + } + root = node; // root = cellular_information.cell_populations + + node = root.child( "cell_population" ); + if( !node ) + { + node = root.append_child( "cell_population" ); + pugi::xml_attribute attrib = node.append_attribute( "type" ); + attrib.set_value( "individual" ); + } + root = node; // root = cellular_information.cell_populations.cell_population + + node = root.child( "custom" ); + if( !node ) + { + node = root.append_child( "custom" ); + } + root = node; // root = cellular_information.cell_populations.cell_population.custom + + node = root.child( "simplified_data" ); + if( !node ) + { + node = root.append_child( "simplified_data" ); + pugi::xml_attribute attrib = node.append_attribute( "type" ); + attrib.set_value( "matlab" ); + + attrib = node.append_attribute( "source" ); + attrib.set_value( "PhysiCell" ); + + attrib = node.append_attribute( "data_version" ); + attrib.set_value( "2" ); + } + root = node; // root = cellular_information.cell_populations.cell_population.custom.simplified_data + + // write cell definiton labels // new in July 2024 + node = root.child( "cell_types" ); + if( !node ) + { + node = root.append_child( "cell_types" ); + root = node; // root = cellular_information.cell_populations.cell_population.custom.simplified_data.cell_types + + for( int i=0; i < cell_type_names.size(); i++ ) + { + node = root.append_child( "type" ); + + pugi::xml_attribute attrib = node.append_attribute( "ID" ); + attrib.set_value( cell_type_indices[i] ); + + attrib = node.append_attribute( "type" ); + attrib.set_value( cell_type_IDs[i] ); + + node.append_child( pugi::node_pcdata ).set_value( cell_type_names[i].c_str() ); + } + root = root.parent(); // root = cellular_information.cell_populations.cell_population.custom.simplified_data + + // name + } + + // write legend + + node = root.child( "labels" ); + if( !node ) + { + node = root.append_child( "labels" ); + root = node; // root = cellular_information.cell_populations.cell_population.custom.simplified_data.labels + + for( int i=0; i < data_names.size(); i++ ) + { + node = root.append_child( "label" ); + pugi::xml_attribute attrib = node.append_attribute( "index" ); + attrib.set_value( data_start_indices[i] ); + + attrib = node.append_attribute( "size" ); + attrib.set_value( data_sizes[i] ); + + attrib = node.append_attribute( "units" ); + attrib.set_value( data_units[i].c_str() ); + + node.append_child( pugi::node_pcdata ).set_value( data_names[i].c_str() ); + } + root = root.parent(); // root = cellular_information.cell_populations.cell_population.custom.simplified_data + } + + // write data + node = root.child( "filename" ); + if( !node ) + { + node = root.append_child( "filename" ); + } + + // write the filename + + // next, filename + char filename [1024]; + sprintf( filename , "%s_cells.mat" , filename_base.c_str() ); + + /* store filename without the relative pathing (if any) */ + char filename_without_pathing [1024]; + char* filename_start = strrchr( filename , '/' ); + if( filename_start == NULL ) + { filename_start = filename; } + else + { filename_start++; } + strcpy( filename_without_pathing , filename_start ); + + if( !node.first_child() ) + { + node.append_child( pugi::node_pcdata ).set_value( filename_without_pathing ); // filename ); + } + else + { + node.first_child().set_value( filename_without_pathing ); // filename ); + } + + // now write the actual data + + int size_of_each_datum = cell_data_size; + int number_of_data_entries = (*all_cells).size(); + + FILE* fp = write_matlab_header( size_of_each_datum, number_of_data_entries, filename, "cells" ); + if( fp == NULL ) + { + std::cout << std::endl << "Error: Failed to open " << filename << " for MAT writing." << std::endl << std::endl; + + std::cout << std::endl << "Error: We're not writing data like we expect. " << std::endl + << "Check to make sure your save directory exists. " << std::endl << std::endl + << "I'm going to exit with a crash code of -1 now until " << std::endl + << "you fix your directory. Sorry!" << std::endl << std::endl; + exit(-1); + } + + Cell* pCell; + + double dTemp; + // storing data as cols (each column is a cell) + for( int i=0; i < number_of_data_entries ; i++ ) + { + pCell = (*all_cells)[i]; + + int writes = 0; + + // compatibilty : first 17 entries + // ID + // double ID_temp = (double) (*all_cells)[i]->ID; + // fwrite( (char*) &( ID_temp ) , sizeof(double) , 1 , fp ); + + // name = "ID"; + dTemp = (double) pCell->ID; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // name = "position"; NOTE very different syntax for writing vectors! + std::fwrite( pCell->position.data() , sizeof(double) , 3 , fp ); + // name = "total_volume"; + std::fwrite( &( pCell->phenotype.volume.total ) , sizeof(double) , 1 , fp ); + // name = "cell_type"; + dTemp = (double) pCell->type; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // name = "cycle_model"; + dTemp = (double) pCell->phenotype.cycle.model().code; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); // cycle model + // name = "current_phase"; + dTemp = (double) pCell->phenotype.cycle.current_phase().code; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); // cycle model + // name = "elapsed_time_in_phase"; + std::fwrite( &( pCell->phenotype.cycle.data.elapsed_time_in_phase ) , sizeof(double) , 1 , fp ); + // name = "nuclear_volume"; + std::fwrite( &( pCell->phenotype.volume.nuclear ) , sizeof(double) , 1 , fp ); + // name = "cytoplasmic_volume"; + std::fwrite( &( pCell->phenotype.volume.cytoplasmic ) , sizeof(double) , 1 , fp ); + // name = "fluid_fraction"; + std::fwrite( &( pCell->phenotype.volume.fluid_fraction ) , sizeof(double) , 1 , fp ); + // name = "calcified_fraction"; + std::fwrite( &( pCell->phenotype.volume.calcified_fraction ) , sizeof(double) , 1 , fp ); + // name = "orientation"; + std::fwrite( pCell->state.orientation.data() , sizeof(double) , 3 , fp ); + // name = "polarity"; + std::fwrite( &( pCell->phenotype.geometry.polarity ) , sizeof(double) , 1 , fp ); + + /* state variables to save */ +// state + // name = "velocity"; + std::fwrite( pCell->velocity.data() , sizeof(double) , 3 , fp ); + // name = "pressure"; + std::fwrite( &( pCell->state.simple_pressure ) , sizeof(double) , 1 , fp ); + // name = "number_of_nuclei"; + dTemp = (double) pCell->state.number_of_nuclei; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // // name = "damage"; + // std::fwrite( &( pCell->phenotype.integrity.damage ) , sizeof(double) , 1 , fp ); + // name = "total_attack_time"; + std::fwrite( &( pCell->state.total_attack_time ) , sizeof(double) , 1 , fp ); + // name = "contact_with_basement_membrane"; + dTemp = (double) pCell->state.contact_with_basement_membrane; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + +/* now go through phenotype and state */ +// cycle + // current exit rate // 1 + // name = "current_cycle_phase_exit_rate"; + int phase_index = pCell->phenotype.cycle.data.current_phase_index; + std::fwrite( &( pCell->phenotype.cycle.data.exit_rate(phase_index) ) , sizeof(double) , 1 , fp ); + // name = "elapsed_time_in_phase"; + std::fwrite( &( pCell->phenotype.cycle.data.elapsed_time_in_phase ) , sizeof(double) , 1 , fp ); + +// death + // live or dead state // 1 + // name = "dead"; + dTemp = (double) pCell->phenotype.death.dead; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // name = "current_death_model"; // + dTemp = (double) pCell->phenotype.death.current_death_model_index; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // name = "death_rates"; + std::fwrite( pCell->phenotype.death.rates.data() , sizeof(double) , nd , fp ); + + // volume () + // name = "cytoplasmic_biomass_change_rate"; + std::fwrite( &( pCell->phenotype.volume.cytoplasmic_biomass_change_rate ) , sizeof(double) , 1 , fp ); + // name = "nuclear_biomass_change_rate"; + std::fwrite( &( pCell->phenotype.volume.nuclear_biomass_change_rate ) , sizeof(double) , 1 , fp ); + // name = "fluid_change_rate"; + std::fwrite( &( pCell->phenotype.volume.fluid_change_rate ) , sizeof(double) , 1 , fp ); + // name = "calcification_rate"; + std::fwrite( &( pCell->phenotype.volume.calcification_rate ) , sizeof(double) , 1 , fp ); + // name = "target_solid_cytoplasmic"; + std::fwrite( &( pCell->phenotype.volume.target_solid_cytoplasmic ) , sizeof(double) , 1 , fp ); + // name = "target_solid_nuclear"; + std::fwrite( &( pCell->phenotype.volume.target_solid_nuclear ) , sizeof(double) , 1 , fp ); + // name = "target_fluid_fraction"; + std::fwrite( &( pCell->phenotype.volume.target_fluid_fraction ) , sizeof(double) , 1 , fp ); + + // geometry + // radius //1 + // name = "radius"; + std::fwrite( &( pCell->phenotype.geometry.radius ) , sizeof(double) , 1 , fp ); + // name = "nuclear_radius"; + std::fwrite( &( pCell->phenotype.geometry.nuclear_radius ) , sizeof(double) , 1 , fp ); + // name = "surface_area"; + std::fwrite( &( pCell->phenotype.geometry.surface_area ) , sizeof(double) , 1 , fp ); + + // mechanics + // cell_cell_adhesion_strength; // 1 + // name = "cell_cell_adhesion_strength"; + std::fwrite( &( pCell->phenotype.mechanics.cell_cell_adhesion_strength ) , sizeof(double) , 1 , fp ); + // name = "cell_BM_adhesion_strength"; + std::fwrite( &( pCell->phenotype.mechanics.cell_BM_adhesion_strength ) , sizeof(double) , 1 , fp ); + // name = "cell_cell_repulsion_strength"; + std::fwrite( &( pCell->phenotype.mechanics.cell_cell_repulsion_strength ) , sizeof(double) , 1 , fp ); + // name = "cell_BM_repulsion_strength"; + std::fwrite( &( pCell->phenotype.mechanics.cell_BM_repulsion_strength ) , sizeof(double) , 1 , fp ); + // name = "cell_adhesion_affinities"; + std::fwrite( pCell->phenotype.mechanics.cell_adhesion_affinities.data() , sizeof(double) , n , fp ); + // name = "relative_maximum_adhesion_distance"; + std::fwrite( &( pCell->phenotype.mechanics.relative_maximum_adhesion_distance ) , sizeof(double) , 1 , fp ); + // name = "maximum_number_of_attachments"; + dTemp = (double) pCell->phenotype.mechanics.maximum_number_of_attachments; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // name = "attachment_elastic_constant"; + std::fwrite( &( pCell->phenotype.mechanics.attachment_elastic_constant ) , sizeof(double) , 1 , fp ); + // name = "attachment_rate"; + std::fwrite( &( pCell->phenotype.mechanics.attachment_rate ) , sizeof(double) , 1 , fp ); + // name = "detachment_rate"; + std::fwrite( &( pCell->phenotype.mechanics.detachment_rate ) , sizeof(double) , 1 , fp ); + + // Motility + // name = "is_motile"; + dTemp = (double) pCell->phenotype.motility.is_motile; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // name = "persistence_time"; + std::fwrite( &( pCell->phenotype.motility.persistence_time ) , sizeof(double) , 1 , fp ); + // name = "migration_speed"; + std::fwrite( &( pCell->phenotype.motility.migration_speed ) , sizeof(double) , 1 , fp ); + // name = "migration_bias_direction"; + std::fwrite( pCell->phenotype.motility.migration_bias_direction.data() , sizeof(double) , 3 , fp ); + // name = "migration_bias"; + std::fwrite( &( pCell->phenotype.motility.migration_bias ) , sizeof(double) , 1 , fp ); + // name = "motility_vector"; + std::fwrite( pCell->phenotype.motility.motility_vector.data() , sizeof(double) , 3 , fp ); + // name = "chemotaxis_index"; + dTemp = (double) pCell->phenotype.motility.chemotaxis_index; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // name = "chemotaxis_direction"; + dTemp = (double) pCell->phenotype.motility.chemotaxis_direction; + std::fwrite( &( dTemp ) , sizeof(double) , 1 , fp ); + // name = "chemotactic_sensitivities"; + std::fwrite( pCell->phenotype.motility.chemotactic_sensitivities.data() , sizeof(double) , m , fp ); + +// secretion + // name = "secretion_rates"; + std::fwrite( pCell->phenotype.secretion.secretion_rates.data() , sizeof(double) , m , fp ); + // name = "uptake_rates"; + std::fwrite( pCell->phenotype.secretion.uptake_rates.data() , sizeof(double) , m , fp ); + // name = "saturation_densities"; + std::fwrite( pCell->phenotype.secretion.saturation_densities.data() , sizeof(double) , m , fp ); + // name = "net_export_rates"; + std::fwrite( pCell->phenotype.secretion.net_export_rates.data() , sizeof(double) , m , fp ); + +// molecular + // name = "internalized_total_substrates"; + std::fwrite( pCell->phenotype.molecular.internalized_total_substrates.data() , sizeof(double) , m , fp ); + // name = "fraction_released_at_death"; + std::fwrite( pCell->phenotype.molecular.fraction_released_at_death.data() , sizeof(double) , m , fp ); + // name = "fraction_transferred_when_ingested"; + std::fwrite( pCell->phenotype.molecular.fraction_transferred_when_ingested.data() , sizeof(double) , m , fp ); + +// interactions + // name = "dead_phagocytosis_rate"; + std::fwrite( &( pCell->phenotype.cell_interactions.dead_phagocytosis_rate ) , sizeof(double) , 1 , fp ); + // name = "apoptotic_phagocytosis_rate"; + std::fwrite( &( pCell->phenotype.cell_interactions.apoptotic_phagocytosis_rate ) , sizeof(double) , 1 , fp ); + // name = "necrotic_phagocytosis_rate"; + std::fwrite( &( pCell->phenotype.cell_interactions.necrotic_phagocytosis_rate ) , sizeof(double) , 1 , fp ); + // name = "live_phagocytosis_rates"; + std::fwrite( pCell->phenotype.cell_interactions.live_phagocytosis_rates.data() , sizeof(double) , n , fp ); + + // name = "attack_rates"; + std::fwrite( pCell->phenotype.cell_interactions.attack_rates.data() , sizeof(double) , n , fp ); + // name = "immunogenicities"; + std::fwrite( pCell->phenotype.cell_interactions.immunogenicities.data() , sizeof(double) , n , fp ); + // name = "attack_target"; + Cell* pTarget = pCell->phenotype.cell_interactions.pAttackTarget; + int AttackID = -1; + if( pTarget ) + { AttackID = pTarget->ID; } + dTemp = (double) AttackID; + std::fwrite( &(dTemp) , sizeof(double) , 1 , fp ); + // name = "attack_damage_rate"; + std::fwrite( &( pCell->phenotype.cell_interactions.attack_damage_rate ) , sizeof(double) , 1 , fp ); + // name = "attack_duration"; + std::fwrite( &( pCell->phenotype.cell_interactions.attack_duration ) , sizeof(double) , 1 , fp ); + // name = "total_damage_delivered"; + std::fwrite( &( pCell->phenotype.cell_interactions.total_damage_delivered ) , sizeof(double) , 1 , fp ); + + // name = "fusion_rates"; + std::fwrite( pCell->phenotype.cell_interactions.fusion_rates.data() , sizeof(double) , n , fp ); + +// transformations + // name = "transformation_rates"; + std::fwrite( pCell->phenotype.cell_transformations.transformation_rates.data() , sizeof(double) , n , fp ); + + // cell integrity + // name = "damage"; + std::fwrite( &( pCell->phenotype.cell_integrity.damage ) , sizeof(double) , 1 , fp ); + // name = "damage_rate"; + std::fwrite( &( pCell->phenotype.cell_integrity.damage_rate ) , sizeof(double) , 1 , fp ); + // name = "damage_repair_rate"; + std::fwrite( &( pCell->phenotype.cell_integrity.damage_repair_rate ) , sizeof(double) , 1 , fp ); + +// custom + // custom scalar variables + for( int j=0 ; j < (*all_cells)[0]->custom_data.variables.size(); j++ ) + { std::fwrite( &( pCell->custom_data.variables[j].value ) , sizeof(double) , 1 , fp ); } + + // custom vector variables + for( int j=0 ; j < (*all_cells)[0]->custom_data.vector_variables.size(); j++ ) + { + int size_temp = pCell->custom_data.vector_variables[j].value.size(); + std::fwrite( pCell->custom_data.vector_variables[j].value.data() , sizeof(double) , size_temp , fp ); + } + } + + fclose( fp ); + +#ifdef ADDON_PHYSIBOSS + + // PhysiBoSS Intracellular Data + node = node.parent().parent(); // custom + + root = node; + node = node.child( "boolean_intracellular_data" ); + if( !node ) + { + node = root.append_child( "boolean_intracellular_data" ); + + pugi::xml_attribute attrib = node.append_attribute( "type" ); + attrib.set_value( "text" ); + + attrib = node.append_attribute( "source" ); + attrib.set_value( "PhysiBoSS" ); + + attrib = node.append_attribute( "data_version" ); + attrib.set_value( "2" ); + } + root = node; // root = cellular_information.cell_populations.cell_population.custom.intracellular_data + node = root.child( "filename"); + if( !node ) + { + node = root.append_child( "filename" ); + + } + root = node; // root = cellular_information.cell_populations.cell_population.custom.intracellular_data.filename + + + // next, filename + sprintf( filename , "%s_boolean_intracellular.csv" , filename_base.c_str() ); + + /* store filename without the relative pathing (if any) */ + filename_start = strrchr( filename , '/' ); + if( filename_start == NULL ) + { filename_start = filename; } + else + { filename_start++; } + strcpy( filename_without_pathing , filename_start ); + + if( !node.first_child() ) + { + node.append_child( pugi::node_pcdata ).set_value( filename_without_pathing ); // filename ); + } + else + { + node.first_child().set_value( filename_without_pathing ); // filename ); + } + + MaBoSSIntracellular::save( filename ); + +#endif + + // neighbor graph + node = node.parent().parent(); // custom + + root = node; + node = node.child( "neighbor_graph" ); + if( !node ) + { + node = root.append_child( "neighbor_graph" ); + + pugi::xml_attribute attrib = node.append_attribute( "type" ); + attrib.set_value( "text" ); + + attrib = node.append_attribute( "source" ); + attrib.set_value( "PhysiCell" ); + + attrib = node.append_attribute( "data_version" ); + attrib.set_value( "2" ); + } + root = node; // root = cellular_information.cell_populations.cell_population.custom.neighbor_graph + node = root.child( "filename"); + if( !node ) + { + node = root.append_child( "filename" ); + + } + root = node; // root = cellular_information.cell_populations.cell_population.custom.neighbor_graph.filename + + + // next, filename + sprintf( filename , "%s_cell_neighbor_graph.txt" , filename_base.c_str() ); + + /* store filename without the relative pathing (if any) */ + filename_start = strrchr( filename , '/' ); + if( filename_start == NULL ) + { filename_start = filename; } + else + { filename_start++; } + strcpy( filename_without_pathing , filename_start ); + + if( !node.first_child() ) + { + node.append_child( pugi::node_pcdata ).set_value( filename_without_pathing ); // filename ); + } + else + { + node.first_child().set_value( filename_without_pathing ); // filename ); + } + + write_neighbor_graph( filename ); + + + // attached cell graph + node = root; + node = node.parent().parent(); // root = cellular_information.cell_populations.cell_population.custom + + root = node; + node = node.child( "attached_cells_graph" ); + if( !node ) + { + node = root.append_child( "attached_cells_graph" ); + + pugi::xml_attribute attrib = node.append_attribute( "type" ); + attrib.set_value( "text" ); + + attrib = node.append_attribute( "source" ); + attrib.set_value( "PhysiCell" ); + + attrib = node.append_attribute( "data_version" ); + attrib.set_value( "2" ); + } + root = node; // root = cellular_information.cell_populations.cell_population.custom.attached_cells_graph + node = root.child( "filename"); + if( !node ) + { node = root.append_child( "filename" ); } + root = node; // root = cellular_information.cell_populations.cell_population.custom.attached_cells_graph.filename + + + // next, filename + sprintf( filename , "%s_attached_cells_graph.txt" , filename_base.c_str() ); + + /* store filename without the relative pathing (if any) */ + filename_start = strrchr( filename , '/' ); + if( filename_start == NULL ) + { filename_start = filename; } + else + { filename_start++; } + strcpy( filename_without_pathing , filename_start ); + + if( !node.first_child() ) + { + node.append_child( pugi::node_pcdata ).set_value( filename_without_pathing ); // filename ); + } + else + { + node.first_child().set_value( filename_without_pathing ); // filename ); + } + + write_attached_cells_graph( filename ); + + // spring attached cell graph + node = root; + node = node.parent().parent(); // root = cellular_information.cell_populations.cell_population.custom + + root = node; + node = node.child( "spring_attached_cells_graph" ); + if( !node ) + { + node = root.append_child( "spring_attached_cells_graph" ); + + pugi::xml_attribute attrib = node.append_attribute( "type" ); + attrib.set_value( "text" ); + + attrib = node.append_attribute( "source" ); + attrib.set_value( "PhysiCell" ); + + attrib = node.append_attribute( "data_version" ); + attrib.set_value( "2" ); + } + root = node; // root = cellular_information.cell_populations.cell_population.custom.spring_attached_cells_graph + node = root.child( "filename"); + if( !node ) + { node = root.append_child( "filename" ); } + root = node; // root = cellular_information.cell_populations.cell_population.custom.spring_attached_cells_graph.filename + + + // next, filename + sprintf( filename , "%s_spring_attached_cells_graph.txt" , filename_base.c_str() ); + + /* store filename without the relative pathing (if any) */ + filename_start = strrchr( filename , '/' ); + if( filename_start == NULL ) + { filename_start = filename; } + else + { filename_start++; } + strcpy( filename_without_pathing , filename_start ); + + if( !node.first_child() ) + { + node.append_child( pugi::node_pcdata ).set_value( filename_without_pathing ); // filename ); + } + else + { + node.first_child().set_value( filename_without_pathing ); // filename ); + } + + write_spring_attached_cells_graph( filename ); + + return; +} + + +/* end of new stuff July 2024*/ + + +void write_neighbor_graph( std::string filename ) +{ + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + + /* + char filename [1024]; + sprintf( filename , "%s_cell_neighbor_graph.txt" , filename_base.c_str() ); + + // store filename without the relative pathing (if any) + char filename_without_pathing [1024]; + char* filename_start = strrchr( filename , '/' ); + if( filename_start == NULL ) { filename_start = filename; } else { filename_start++; } @@ -2185,6 +3940,8 @@ void write_neighbor_graph( std::string filename ) void write_attached_cells_graph( std::string filename ) { + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + /* char filename [1024]; sprintf( filename , "%s_cell_attached_graph.txt" , filename_base.c_str() ); @@ -2223,6 +3980,8 @@ void write_attached_cells_graph( std::string filename ) void write_spring_attached_cells_graph( std::string filename ) { + std::cout << __LINE__ << " " << __FUNCTION__ << std::endl; + /* char filename [1024]; sprintf( filename , "%s_cell_attached_graph.txt" , filename_base.c_str() );