3 #include <spdlog/fmt/bundled/core.h>
16 std::string
trim(
const std::string& str) {
18 auto start = str.find_first_not_of(
" \t\n\r\f\v");
19 auto end = str.find_last_not_of(
" \t\n\r\f\v");
21 if (start == std::string::npos) {
24 return str.substr(start, end - start + 1);
29 std::string sanitized =
trim(text);
32 if (sanitized.back() ==
'/') {
37 std::replace(sanitized.begin(), sanitized.end(),
' ',
'_');
40 std::transform(sanitized.begin(), sanitized.end(), sanitized.begin(), [](
unsigned char c) { return std::tolower(c); });
45 std::filesystem::path
convertToPath(
const std::filesystem::path& base_path,
const std::filesystem::path& path) {
46 auto is_relative_path = path.is_relative();
48 if (is_relative_path) {
49 return base_path / path;
56 std::string file_extension = path.extension().string();
57 if (file_extension !=
".chkpt") {
58 Logger::logger->error(
"Error: file extension '{}' is not supported. Only .chkpt files can be used as checkpoints.", file_extension);
63 auto [loaded_particles, iteration] = reader.
readFile(path);
64 particles.insert(particles.end(), loaded_particles.begin(), loaded_particles.end());
66 Logger::logger->info(
"Loaded {} particles from checkpoint file {}", loaded_particles.size(), path.string());
72 if (!std::filesystem::exists(base_path)) {
76 std::optional<std::filesystem::path> check_point_path = std::nullopt;
77 size_t best_iteration = 0;
78 auto directory_iterator = std::filesystem::directory_iterator(base_path);
80 std::set<std::filesystem::path> entries;
81 for (
auto& entry : directory_iterator) {
82 if (entry.path().extension() ==
".chkpt") {
83 entries.insert(entry.path());
87 for (
auto it = entries.rbegin(); it != entries.rend(); ++it) {
90 std::string filename = entry.filename().string();
91 std::string iteration_str =
92 filename.substr(filename.find_last_of(
"_") + 1, filename.find_last_of(
".") - filename.find_last_of(
"_") - 1);
94 size_t current_file_number = std::stoul(iteration_str);
96 if (current_file_number > best_iteration) {
102 "The input file for the checkpoint file {} has changed since the checkpoint file was created. Skipping.",
107 best_iteration = current_file_number;
108 check_point_path = entry;
110 Logger::logger->warn(
"Error: Could not read checkpoint file {}. Skipping.", entry.string());
115 return check_point_path;
118 auto loadConfig(
const SubSimulationType& sub_simulation,
const std::filesystem::path& curr_file_path,
119 const std::filesystem::path& base_path) {
121 auto other_file_name =
convertToPath(base_path, std::filesystem::path(std::string(sub_simulation.path())));
123 std::string file_extension = other_file_name.extension().string();
124 if (file_extension !=
".xml") {
125 Logger::logger->error(
"Error: file extension '{}' is not supported. Only .xml files can be used as sub simulations.",
130 auto config = configuration(other_file_name);
131 return std::make_pair(*config, other_file_name);
135 bool fresh,
bool allow_recursion,
136 std::filesystem::path output_base_path =
"",
int depth = 0) {
137 Logger::logger->info(
"Constructing configuration for file {} at depth {}", curr_file_path.string(), depth);
139 auto settings = config.settings();
140 auto particle_sources = config.particle_source();
144 std::vector<Particle> particles;
152 auto params =
SimulationParams{curr_file_path, settings.
delta_t(), settings.end_time(), container_type, interceptors,
153 std::get<0>(forces), std::get<1>(forces), std::get<2>(forces), fresh, output_base_path};
155 if (output_base_path.empty()) {
156 output_base_path = params.output_dir_path;
159 auto curr_folder = std::filesystem::path(curr_file_path).parent_path().string();
161 bool load_in_spawners =
true;
165 if (latest_checkpoint_path.has_value()) {
166 Logger::logger->warn(
"Found checkpoint file {}", latest_checkpoint_path.value().string());
168 Logger::logger->warn(
" [y] Continue from checkpoint [n] Start from scratch");
173 if (std::cin.fail() || std::cin.eof()) {
178 if (answer !=
'y' && answer !=
'n') {
189 params.start_iteration = end_iteration;
190 load_in_spawners =
false;
192 Logger::logger->warn(
"Continuing from checkpoint file {} with iteration {}", latest_checkpoint_path.value().string(),
193 params.start_iteration);
198 Logger::logger->warn(
"Error: No valid checkpoint file found in output directory {}", params.output_dir_path.string());
201 if (load_in_spawners) {
203 for (
auto cuboid_spawner : particle_sources.cuboid_spawner()) {
205 int num_spawned = spawner.spawnParticles(particles);
206 Logger::logger->info(
"Spawned {} particles from cuboid spawner", num_spawned);
209 for (
auto soft_body_cuboid_spawner : particle_sources.soft_body_cuboid_spawner()) {
211 if (std::holds_alternative<SimulationParams::LinkedCellsType>(container_type)) {
212 auto container = std::get<SimulationParams::LinkedCellsType>(container_type);
213 if (std::find(container.boundary_conditions.begin(), container.boundary_conditions.end(),
220 int num_spawned = spawner.spawnParticles(particles);
221 Logger::logger->info(
"Spawned {} particles from soft body cuboid spawner", num_spawned);
224 for (
auto sphere_spawner : particle_sources.sphere_spawner()) {
226 int num_spawned = spawner.spawnParticles(particles);
227 Logger::logger->info(
"Spawned {} particles from sphere spawner", num_spawned);
230 for (
auto single_particle_spawner : particle_sources.single_particle_spawner()) {
232 int num_spawned = spawner.spawnParticles(particles);
233 Logger::logger->info(
"Spawned {} particles from single particle spawner", num_spawned);
236 for (
auto check_point_loader : particle_sources.check_point_loader()) {
237 auto path =
convertToPath(curr_folder, std::filesystem::path(std::string(check_point_loader.path())));
241 for (
auto sub_simulation : particle_sources.sub_simulation()) {
242 if (!allow_recursion) {
243 Logger::logger->warn(
"Error: Recursion is disabled. Skipping sub simulation at depth {}", depth);
247 auto name = std::filesystem::path(std::string(sub_simulation.path())).stem().string();
249 Logger::logger->info(
"Found sub simulation {} at depth {}", name, depth);
251 std::filesystem::path new_output_base_path = output_base_path /
sanitizePath(name);
257 if (checkpoint_path.has_value()) {
259 "Using cached result for sub simulation {} at depth {}. To force a rerun, delete the checkpoint file at {}", name,
260 depth, checkpoint_path.value().string());
263 if (!checkpoint_path.has_value()) {
264 Logger::logger->info(
"Starting sub simulation {} at depth {}", name, depth);
267 auto [loaded_config, file_name] =
loadConfig(sub_simulation, curr_file_path, curr_folder);
270 auto [sub_particles, sub_config] =
271 prepareParticles(file_name, loaded_config, fresh, allow_recursion, new_output_base_path, depth + 1);
272 sub_config.output_dir_path = new_output_base_path;
275 Simulation simulation{sub_particles, sub_config};
277 sub_config.logSummary(depth);
284 checkpoint_path = file_output_handler.writeFile(result.total_iterations, result.resulting_particles);
286 Logger::logger->info(
"Wrote {} particles to checkpoint file in: {}", result.resulting_particles.size(),
287 (*checkpoint_path).string());
295 if (settings.log_level()) {
299 params.num_particles = particles.size();
301 return std::make_tuple(particles, std::move(params));
304 std::tuple<std::vector<Particle>, std::optional<SimulationParams>>
XMLFileReader::readFile(
const std::filesystem::path& filepath)
const {
306 auto config = configuration(filepath);
309 }
catch (
const xml_schema::exception& e) {
310 std::stringstream error_message;
311 error_message <<
"Error: could not parse file '" << filepath <<
"'.\n";
312 error_message << e << std::endl;
ThirdDimension
Enum class to define the dimension count of the simulation (2D or 3D). Affects primarily the dimensio...
std::filesystem::path convertToPath(const std::filesystem::path &base_path, const std::filesystem::path &path)
auto loadConfig(const SubSimulationType &sub_simulation, const std::filesystem::path &curr_file_path, const std::filesystem::path &base_path)
int loadCheckpointFile(std::vector< Particle > &particles, const std::filesystem::path &path)
std::string trim(const std::string &str)
std::tuple< std::vector< Particle >, SimulationParams > prepareParticles(std::filesystem::path curr_file_path, ConfigurationType &config, bool fresh, bool allow_recursion, std::filesystem::path output_base_path="", int depth=0)
std::filesystem::path sanitizePath(const std::string &text)
std::optional< std::filesystem::path > loadLatestValidCheckpointFromFolder(const std::filesystem::path &base_path)
Class to read particle and simulation data from a '.xml' file.
static bool detectSourceFileChanges(const std::string &filepath)
Checks if the given file contains a valid hash generated from the original input file.
std::tuple< std::vector< Particle >, int > readFile(const std::filesystem::path &filepath) const
Reads particle data from a '.xml' file and returns a vector of particles.
Wrapper class to abstract the writing of output files.
static void update_level(std::string &log_level)
Sets the log level of the logger.
static std::shared_ptr< spdlog::logger > logger
Publically accessible shared pointer to the logger.
void logSummary(int depth=0) const
Prints a summary of the simulation overview to the logger.
Contains all parameters needed to run a simulation.
double delta_t
Time step of a single simulation iteration.
Class to run a simulation.
SimulationOverview runSimulation()
Runs the simulation, using the parameters given at construction and returns a SimulationOverview obje...
std::tuple< std::vector< Particle >, std::optional< SimulationParams > > readFile(const std::filesystem::path &filepath) const override
Reads particle data from a '.xml' file and returns a vector of particles Other simulation parameters ...
static SoftBodyCuboidSpawner convertToSoftBodyCuboidSpawner(const SoftBodySpawnerType &soft_body_cuboid, ThirdDimension third_dimension)
Converts a soft body cuboid from the XSD format to the internal format.
static SphereSpawner convertToSphereSpawner(const SphereSpawnerType &sphere, ThirdDimension third_dimension)
Converts a sphere from the XSD format to the internal format.
static std::tuple< std::vector< std::shared_ptr< SimpleForceSource > >, std::vector< std::shared_ptr< PairwiseForceSource > >, std::vector< std::shared_ptr< TargettedForceSource > > > convertToForces(const ForcesType &forces, const std::variant< SimulationParams::DirectSumType, SimulationParams::LinkedCellsType > &container_data)
Converts a force type from the XSD format to the internal format.
static std::variant< SimulationParams::DirectSumType, SimulationParams::LinkedCellsType > convertToParticleContainer(const ParticleContainerType &container_type)
Converts a container type from the XSD format to the internal format.
static CuboidSpawner convertToSingleParticleSpawner(const SingleParticleSpawnerType &particle, ThirdDimension third_dimension)
Converts a particle from the XSD format to the internal format.
static std::vector< std::shared_ptr< SimulationInterceptor > > convertToSimulationInterceptors(const SimulationInterceptorsType &interceptors, ThirdDimension third_dimension, std::variant< SimulationParams::DirectSumType, SimulationParams::LinkedCellsType > container_type)
Converts the simulation interceptors from the XSD format to the internal format.
static CuboidSpawner convertToCuboidSpawner(const CuboidSpawnerType &cuboid, ThirdDimension third_dimension)
Converts a cuboid from the XSD format to the internal format.