diff --git a/modules/PhysiCell_MultiCellDS.cpp b/modules/PhysiCell_MultiCellDS.cpp
index 1558b8cad..40aac05e9 100644
--- a/modules/PhysiCell_MultiCellDS.cpp
+++ b/modules/PhysiCell_MultiCellDS.cpp
@@ -1354,34 +1354,7 @@ int resume_from_MultiCellDS(std::string folder_path, std::string xml_filename, b
return -1;
}
- // Let's confirm the last cell param, before the custom data vars, is what we expect.
- // If/when this labels list is modified, this code will need to be updated.
-
- std::string label_path = "//cellular_information//cell_populations//custom//simplified_data//labels//label[@index=99]";
- std::cout << "\nreading " << label_path << std::endl;
- // xpath_node = doc.select_node(label_path); // not allowed
-
- //
- //
- // xpath_node = doc.select_node("//cellular_information//cell_populations//custom//simplified_data//labels//label[@index=99]");
- // node = xpath_node.node();
- // if (node)
- // {
- // std::cout << "\n Success!\n" << std::endl;
- // }
- // else
- // {
- // std::cout << "\n --- Error reading node\n" << std::endl;
- // return -1;
- // }
- // std::string last_var = xml_get_my_string_value(node);
- // std::cout << "last_var= " << last_var << std::endl;
- // if (last_var != "damage_repair_rate")
- // {
- // std::cout << "\n Error: last var should be 'damage_repair_rate'\n" << std::endl;
- // return -1;
- // }
-
+ // read/skip over all labels until the last one, damage_repair_rate, so we can parse custom data vars
xpath_node = doc.select_node("//cellular_information//cell_populations//custom//simplified_data//labels//label[@index=0]");
node = xpath_node.node();
if (!node)
@@ -2173,7 +2146,10 @@ int recreate_sim_state(std::string filename, Microenvironment& M,
// if it exists, overwrite
if( idx_var > -1 )
{
- pCell->custom_data.variables[idx_var].value = pair.second;
+ // pCell->custom_data.variables[idx_var].value = pair.second;
+ pCell->custom_data.variables[idx_var].value = dTemp;
+ if (debug_print)
+ { std::cout << " ---- pCell->custom_data[" << pair.first << "] = " <custom_data[pair.first] << std::endl; }
}
else
{
diff --git a/unit_tests/resume_sim/README.md b/unit_tests/resume_sim/README.md
new file mode 100644
index 000000000..300596a11
--- /dev/null
+++ b/unit_tests/resume_sim/README.md
@@ -0,0 +1,33 @@
+## Resume a simulation from a previous checkpoint
+
+* provide a MultiCellDS file (output000*N.xml) as a starting point to continue a simulation
+* does not handle intracellular model state
+* does not currently recover "output*_graph.txt" data; rather relies on the next mechanics step to recreate the information
+* if a model relies on global data generated in custom C++ code that is not captured in the MultiCellDS data, it cannot be recovered
+* an agent's `is_movable` flag is not currently saved in the MultiCellDS (.mat), so it will not be recovered
+
+Normal simulation:
+
+```
+project config/cycle_phase_3cells_custom_vecs.xml # writes output to /output
+```
+
+
+Resume a simulation:
+
+```
+Usage options:
+- no args: will try to use ./config/PhysiCell_settings.xml
+- 1 arg: will try to use it as the config file
+- 5 args to resume a simulation:
+ <.xml config file>
+
+ e.g. project config/PhysiCell_settings.xml output output00000042.xml 43 43
+
+```
+
+```
+project config/cycle_phase_3cells_custom_vecs.xml output output00000048.xml 49 49
+```
+
+
diff --git a/unit_tests/resume_sim/main.cpp b/unit_tests/resume_sim/main.cpp
new file mode 100644
index 000000000..7ca344767
--- /dev/null
+++ b/unit_tests/resume_sim/main.cpp
@@ -0,0 +1,314 @@
+/*
+###############################################################################
+# If you use PhysiCell in your project, please cite PhysiCell and the version #
+# number, such as below: #
+# #
+# We implemented and solved the model using PhysiCell (Version x.y.z) [1]. #
+# #
+# [1] A Ghaffarizadeh, R Heiland, SH Friedman, SM Mumenthaler, and P Macklin, #
+# PhysiCell: an Open Source Physics-Based Cell Simulator for Multicellu- #
+# lar Systems, PLoS Comput. Biol. 14(2): e1005991, 2018 #
+# DOI: 10.1371/journal.pcbi.1005991 #
+# #
+# See VERSION.txt or call get_PhysiCell_version() to get the current version #
+# x.y.z. Call display_citations() to get detailed information on all cite-#
+# able software used in your PhysiCell application. #
+# #
+# Because PhysiCell extensively uses BioFVM, we suggest you also cite BioFVM #
+# as below: #
+# #
+# We implemented and solved the model using PhysiCell (Version x.y.z) [1], #
+# with BioFVM [2] to solve the transport equations. #
+# #
+# [1] A Ghaffarizadeh, R Heiland, SH Friedman, SM Mumenthaler, and P Macklin, #
+# PhysiCell: an Open Source Physics-Based Cell Simulator for Multicellu- #
+# lar Systems, PLoS Comput. Biol. 14(2): e1005991, 2018 #
+# DOI: 10.1371/journal.pcbi.1005991 #
+# #
+# [2] A Ghaffarizadeh, SH Friedman, and P Macklin, BioFVM: an efficient para- #
+# llelized diffusive transport solver for 3-D biological simulations, #
+# Bioinformatics 32(8): 1256-8, 2016. DOI: 10.1093/bioinformatics/btv730 #
+# #
+###############################################################################
+# #
+# BSD 3-Clause License (see https://opensource.org/licenses/BSD-3-Clause) #
+# #
+# Copyright (c) 2015-2023, Paul Macklin and the PhysiCell Project #
+# All rights reserved. #
+# #
+# Redistribution and use in source and binary forms, with or without #
+# modification, are permitted provided that the following conditions are met: #
+# #
+# 1. Redistributions of source code must retain the above copyright notice, #
+# this list of conditions and the following disclaimer. #
+# #
+# 2. Redistributions in binary form must reproduce the above copyright #
+# notice, this list of conditions and the following disclaimer in the #
+# documentation and/or other materials provided with the distribution. #
+# #
+# 3. Neither the name of the copyright holder nor the names of its #
+# contributors may be used to endorse or promote products derived from this #
+# software without specific prior written permission. #
+# #
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" #
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE #
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE #
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE #
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR #
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF #
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS #
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN #
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) #
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE #
+# POSSIBILITY OF SUCH DAMAGE. #
+# #
+###############################################################################
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "./core/PhysiCell.h"
+#include "./modules/PhysiCell_standard_modules.h"
+
+// put custom code modules here!
+
+#include "./custom_modules/custom.h"
+
+using namespace BioFVM;
+using namespace PhysiCell;
+
+//-----------------------------------------
+int main( int argc, char* argv[] )
+{
+ // load and parse settings file(s)
+
+ bool XML_status = false;
+ char copy_command [1024];
+ bool resume_from_checkpoint = false;
+ std::string resume_from_dir;
+ std::string resume_from_file;
+ int next_full_index, next_svg_index;
+
+ std::cout << "argc = " << argc << ", argv[0]=" << argv[0] << std::endl;
+ if( argc == 6 )
+ {
+ resume_from_checkpoint = true;
+
+ XML_status = load_PhysiCell_config_file( argv[1] );
+ if( !XML_status )
+ { exit(-1); }
+
+ sprintf( copy_command , "cp %s %s" , argv[1] , PhysiCell_settings.folder.c_str() );
+ // copy config file to output directry
+ system( copy_command );
+
+ resume_from_dir = argv[2];
+ resume_from_file = argv[3];
+
+ next_full_index = std::stoi(argv[4]);
+ next_svg_index = std::stoi(argv[5]);
+ }
+ else if( argc == 2 )
+ {
+ XML_status = load_PhysiCell_config_file( argv[1] );
+ sprintf( copy_command , "cp %s %s" , argv[1] , PhysiCell_settings.folder.c_str() );
+ }
+ else if ( argc < 2 )
+ {
+ XML_status = load_PhysiCell_config_file( "./config/PhysiCell_settings.xml" );
+ sprintf( copy_command , "cp ./config/PhysiCell_settings.xml %s" , PhysiCell_settings.folder.c_str() );
+ }
+ else
+ {
+ std::string err_msg = "\nIncorrect arguments. Usage options: \n"
+ "- no args: will try to use ./config/PhysiCell_settings.xml\n"
+ "- 1 arg: will try to use it as the config file\n"
+ "- 5 args to resume a simulation:\n"
+ " <.xml config file> \n";
+
+ std::cout << err_msg << std::endl;
+ std::cout << " e.g. config/PhysiCell_settings.xml output output00000042.xml 43 43\n" << std::endl;
+ exit(-1);
+ }
+
+ if( !XML_status )
+ { exit(-1); }
+
+ // OpenMP setup
+ omp_set_num_threads(PhysiCell_settings.omp_num_threads);
+
+ // time setup
+ std::string time_units = "min";
+
+ // Microenvironment setup
+ setup_microenvironment(); // modify this in the custom code
+
+ // PhysiCell setup
+ // set mechanics voxel size, and match the data structure to BioFVM
+ double mechanics_voxel_size = 30;
+ Cell_Container* cell_container = create_cell_container_for_microenvironment( microenvironment, mechanics_voxel_size );
+
+ // Users typically start modifying here. START USERMODS
+
+ create_cell_types();
+
+
+ //---------
+ if ( !resume_from_checkpoint )
+ {
+ setup_tissue();
+ }
+ else
+ {
+ // read in and resume from a checkpoint file (replaces setup_tissue)
+
+ // This sets PhysiCell_globals.current_time to be that of resumed checkpoint
+ // resume_from_MultiCellDS_xml("output_test_resume_phases_custom_vec", "output00000072.xml");
+
+ // int resume_from_MultiCellDS(std::string folder_path, std::string xml_filename, bool create_cells = true, bool debug_print = false);
+ int retval = resume_from_MultiCellDS(resume_from_dir, resume_from_file);
+ // int retval = resume_from_MultiCellDS(resume_from_dir, resume_from_file, true, true);
+
+ // Another option: don't recreate state, but print out state of cells
+ // int retval = resume_from_MultiCellDS(resume_from_dir, resume_from_file, false, true);
+ if (retval < 0)
+ {
+ std::cout << "Error in resume_from_MultiCellDS, exiting" << std::endl;
+ exit(-1);
+ }
+
+ // Need to adjust these values
+ PhysiCell_globals.next_full_save_time = PhysiCell_globals.current_time + PhysiCell_settings.full_save_interval;
+ PhysiCell_globals.next_SVG_save_time = PhysiCell_globals.current_time + PhysiCell_settings.SVG_save_interval;
+
+ PhysiCell_globals.full_output_index = next_full_index;
+ PhysiCell_globals.SVG_output_index = next_svg_index;
+ }
+
+ // Users typically stop modifying here. END USERMODS
+
+ // set MultiCellDS save options
+
+ set_save_biofvm_mesh_as_matlab( true );
+ set_save_biofvm_data_as_matlab( true );
+ set_save_biofvm_cell_data( true );
+ set_save_biofvm_cell_data_as_custom_matlab( true );
+
+ // save a simulation snapshot
+
+ char filename[1024];
+ sprintf( filename , "%s/initial" , PhysiCell_settings.folder.c_str() );
+ save_PhysiCell_to_MultiCellDS_v2( filename , microenvironment , PhysiCell_globals.current_time );
+
+ // save a quick SVG cross section through z = 0, after setting its
+ // length bar to 200 microns
+
+ PhysiCell_SVG_options.length_bar = 200;
+
+ // for simplicity, set a pathology coloring function
+
+ std::vector (*cell_coloring_function)(Cell*) = my_coloring_function;
+ std::string (*substrate_coloring_function)(double, double, double) = paint_by_density_percentage;
+
+ sprintf( filename , "%s/initial.svg" , PhysiCell_settings.folder.c_str() );
+ SVG_plot( filename , microenvironment, 0.0 , PhysiCell_globals.current_time, cell_coloring_function );
+
+ sprintf( filename , "%s/legend.svg" , PhysiCell_settings.folder.c_str() );
+ create_plot_legend( filename , cell_coloring_function );
+
+ display_citations();
+
+ // set the performance timers
+
+ BioFVM::RUNTIME_TIC();
+ BioFVM::TIC();
+
+ std::ofstream report_file;
+ if( PhysiCell_settings.enable_legacy_saves == true )
+ {
+ sprintf( filename , "%s/simulation_report.txt" , PhysiCell_settings.folder.c_str() );
+
+ report_file.open(filename); // create the data log file
+ report_file<<"simulated time\tnum cells\tnum division\tnum death\twall time"< PhysiCell_globals.next_full_save_time - 0.5 * diffusion_dt )
+ {
+ display_simulation_status( std::cout );
+ if( PhysiCell_settings.enable_legacy_saves == true )
+ {
+ log_output( PhysiCell_globals.current_time , PhysiCell_globals.full_output_index, microenvironment, report_file);
+ }
+
+ if( PhysiCell_settings.enable_full_saves == true )
+ {
+ sprintf( filename , "%s/output%08u" , PhysiCell_settings.folder.c_str(), PhysiCell_globals.full_output_index );
+
+ save_PhysiCell_to_MultiCellDS_v2( filename , microenvironment , PhysiCell_globals.current_time );
+ }
+
+ PhysiCell_globals.full_output_index++;
+ PhysiCell_globals.next_full_save_time += PhysiCell_settings.full_save_interval;
+ }
+
+ // save SVG plot if it's time
+ if( PhysiCell_globals.current_time > PhysiCell_globals.next_SVG_save_time - 0.5 * diffusion_dt )
+ {
+ if( PhysiCell_settings.enable_SVG_saves == true )
+ {
+ sprintf( filename , "%s/snapshot%08u.svg" , PhysiCell_settings.folder.c_str() , PhysiCell_globals.SVG_output_index );
+ SVG_plot( filename , microenvironment, 0.0 , PhysiCell_globals.current_time, cell_coloring_function );
+
+ PhysiCell_globals.SVG_output_index++;
+ PhysiCell_globals.next_SVG_save_time += PhysiCell_settings.SVG_save_interval;
+ }
+ }
+
+ // update the microenvironment
+ microenvironment.simulate_diffusion_decay( diffusion_dt );
+
+ // run PhysiCell
+ ((Cell_Container *)microenvironment.agent_container)->update_all_cells( PhysiCell_globals.current_time );
+
+ /*
+ Custom add-ons could potentially go here.
+ */
+
+ PhysiCell_globals.current_time += diffusion_dt;
+ }
+
+ if( PhysiCell_settings.enable_legacy_saves == true )
+ {
+ log_output(PhysiCell_globals.current_time, PhysiCell_globals.full_output_index, microenvironment, report_file);
+ report_file.close();
+ }
+ }
+ catch( const std::exception& e )
+ { // reference to the base of a polymorphic object
+ std::cout << e.what(); // information from length_error printed
+ }
+
+ // save a final simulation snapshot
+
+ sprintf( filename , "%s/final" , PhysiCell_settings.folder.c_str() );
+ save_PhysiCell_to_MultiCellDS_v2( filename , microenvironment , PhysiCell_globals.current_time );
+
+ sprintf( filename , "%s/final.svg" , PhysiCell_settings.folder.c_str() );
+ SVG_plot( filename , microenvironment, 0.0 , PhysiCell_globals.current_time, cell_coloring_function );
+
+ std::cout << std::endl << "Total simulation runtime: " << std::endl;
+ BioFVM::display_stopwatch_value( std::cout , BioFVM::runtime_stopwatch_value() );
+
+ return 0;
+}