Skip to content

Commit

Permalink
Improve support for scan path update
Browse files Browse the repository at this point in the history
  • Loading branch information
Rombur committed Jun 17, 2024
1 parent 5ae485c commit 65540cc
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 45 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ The following options are available:
* method: name of the method to use for the time integration: forward\_euler,
rk\_third\_order, rk\_fourth\_order, backward\_euler, implicit\_midpoint,
crank\_nicolson, or sdirk2 (required)
* duration: duration of the simulation in seconds (required)
* scan\_path\_for\_duration: if the flag is true, the duration of the simulation is determined by the duration of the scan path. In this case the scan path file needs to contain SCAN\_PATH\_END to terminate the simulation. If the flag is false, the duration of the simulation is determined by the duration input (default value: false)
* duration: duration of the simulation in seconds (required if scan\_path\_for\_duration is false)
* time\_step: length of the time steps used for the simulation in seconds (required)
* for implicit method:
* max\_iteration: mamximum number of the iterations of the linear solver
Expand Down
148 changes: 107 additions & 41 deletions application/adamantine.hh
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,6 @@ run(MPI_Comm const &communicator, boost::property_tree::ptree const &database,
std::unique_ptr<adamantine::ThermalPhysicsInterface<dim, MemorySpaceType>>
thermal_physics;
std::vector<std::shared_ptr<adamantine::HeatSource<dim>>> heat_sources;
std::vector<std::pair<double, bool>> scan_path_end;
if (use_thermal_physics)
{
// PropertyTreeInput discretization.thermal.fe_degree
Expand All @@ -774,13 +773,6 @@ run(MPI_Comm const &communicator, boost::property_tree::ptree const &database,
fe_degree, quadrature_type, communicator, database, geometry,
material_properties);
heat_sources = thermal_physics->get_heat_sources();
// Store the current end time of each heat source and set a flag that the
// scan path has changed
for (auto const &source : heat_sources)
{
scan_path_end.emplace_back(
source->get_scan_path().get_segment_list().back().end_time, true);
}
post_processor_database.put("thermal_output", true);
}

Expand Down Expand Up @@ -955,8 +947,13 @@ run(MPI_Comm const &communicator, boost::property_tree::ptree const &database,
database.get_child("time_stepping");
// PropertyTreeInput time_stepping.time_step
double time_step = time_stepping_database.get<double>("time_step");
// PropertyTreeInput time_stepping.scan_path_for_duration
bool const scan_path_for_duration =
time_stepping_database.get("scan_path_for_duration", false);
// PropertyTreeInput time_stepping.duration
double const duration = time_stepping_database.get<double>("duration");
double const duration = scan_path_for_duration
? std::numeric_limits<double>::max()
: time_stepping_database.get<double>("duration");

// Extract the refinement database
boost::property_tree::ptree refinement_database =
Expand Down Expand Up @@ -1008,44 +1005,53 @@ run(MPI_Comm const &communicator, boost::property_tree::ptree const &database,
timers[adamantine::add_material_activate].start();
if (time > activation_time_end)
{
double const eps = time_step / 1e10;

// We may need to check if the scan path has been updated. We don't want
// to keep reading the file if it's not going to be updated. Once we
// reach the end of the scan path, we reread the scan path file. If it was
// not updated, we won't try to read it again.
bool scan_path_updated = false;
for (unsigned int s = 0; s < scan_path_end.size(); ++s)
// If we use scan_path_for_duration, we may need to read the scan path
// file once again.
if (scan_path_for_duration)
{
if ((time > scan_path_end[s].first - eps) && (scan_path_end[s].second))
// Check if we have reached the end of current scan path.
bool need_updated_scan_path = false;
for (auto &source : heat_sources)
{
scan_path_updated = true;
break;
if (time > source->get_scan_path().get_segment_list().back().end_time)
{
need_updated_scan_path = true;
break;
}
}
}
if (scan_path_updated)
{
for (unsigned int s = 0; s < scan_path_end.size(); ++s)

if (need_updated_scan_path)
{
// Read the scan path file
heat_sources[s]->get_scan_path().read_file();

// Update scan_path_end
double new_end_time = heat_sources[s]
->get_scan_path()
.get_segment_list()
.back()
.end_time;
scan_path_end[s].second = scan_path_end[s].first != new_end_time;
scan_path_end[s].first = new_end_time;
}
// Check if we have reached the end of the file. If not, read the
// updated scan path file
bool scan_path_end = true;
for (auto &source : heat_sources)
{
if (!source->get_scan_path().is_finished())
{
scan_path_end = false;
// This functions waits for the scan path file to be updated
// before reading the file.
source->get_scan_path().read_file();
}
}

std::tie(material_deposition_boxes, deposition_times, deposition_cos,
deposition_sin) =
adamantine::create_material_deposition_boxes<dim>(geometry_database,
heat_sources);
// If we have reached the end of scan path file for all the heat
// sources, we just exit.
if (scan_path_end)
{
break;
}

std::tie(material_deposition_boxes, deposition_times, deposition_cos,
deposition_sin) =
adamantine::create_material_deposition_boxes<dim>(
geometry_database, heat_sources);
}
}

double const eps = time_step / 1e10;

auto activation_start =
std::lower_bound(deposition_times.begin(), deposition_times.end(),
time - eps) -
Expand Down Expand Up @@ -1707,8 +1713,13 @@ run_ensemble(MPI_Comm const &global_communicator,
refinement_database.get("time_steps_between_refinement", 10);
// PropertyTreeInput time_stepping.time_step
double time_step = time_stepping_database.get<double>("time_step");
// PropertyTreeInput time_stepping.scan_path_for_duration
bool const scan_path_for_duration =
time_stepping_database.get("scan_path_for_duration", false);
// PropertyTreeInput time_stepping.duration
double const duration = time_stepping_database.get<double>("duration");
double const duration = scan_path_for_duration
? std::numeric_limits<double>::max()
: time_stepping_database.get<double>("duration");
// PropertyTreeInput post_processor.time_steps_between_output
unsigned int const time_steps_output =
post_processor_database.get("time_steps_between_output", 1);
Expand Down Expand Up @@ -1785,6 +1796,61 @@ run_ensemble(MPI_Comm const &global_communicator,
timers[adamantine::add_material_activate].start();
if (time > activation_time_end)
{
// If we use scan_path_for_duration, we may need to read the scan path
// file once again.
if (scan_path_for_duration)
{
// Check if we have reached the end of current scan path.
bool need_updated_scan_path = false;
{
for (auto &source : heat_sources_ensemble[0])
{
if (time >
source->get_scan_path().get_segment_list().back().end_time)
{
need_updated_scan_path = true;
break;
}
}
}

if (need_updated_scan_path)
{
// Check if we have reached the end of the file. If not, read the
// updated scan path file. We assume that the scan paths are identical
// for all the heat sources and thus, we can use
// heat_sources_ensemble[0] to get the material deposition boxes and
// times. We still need every ensemble member to read the scan path in
// order to compute the correct heat sources.
bool scan_path_end = true;
for (unsigned int member = 0; member < local_ensemble_size; ++member)
{
for (auto &source : heat_sources_ensemble[member])
{
if (!source->get_scan_path().is_finished())
{
scan_path_end = false;
// This functions waits for the scan path file to be updated
// before reading the file.
source->get_scan_path().read_file();
}
}
}

// If we have reached the end of scan path file for all the heat
// sources, we just exit.
if (scan_path_end)
{
break;
}

std::tie(material_deposition_boxes, deposition_times, deposition_cos,
deposition_sin) =
adamantine::create_material_deposition_boxes<dim>(
geometry_database, heat_sources_ensemble[0]);
}
}

double const eps = time_step / 1e12;
auto activation_start =
std::lower_bound(deposition_times.begin(), deposition_times.end(),
Expand Down
19 changes: 19 additions & 0 deletions source/ScanPath.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ ScanPath::ScanPath(std::string scan_path_file, std::string file_format)

void ScanPath::read_file()
{
wait_for_file_to_update(_scan_path_file, "Waiting for " + _scan_path_file,
_last_write_time);

if (_file_format == "segment")
{
load_segment_scan_path();
Expand Down Expand Up @@ -56,6 +59,13 @@ void ScanPath::load_segment_scan_path()
// segments to read, whichever comes first
while ((data_index < n_segments) && (getline(file, line)))
{
// If we reach the end of the scan path, we stop reading the file.
if (line.find("SCAN_PATH_END") != std::string::npos)
{
_scan_path_end = true;
break;
}

std::vector<std::string> split_line;
boost::split(split_line, line, boost::is_any_of(" "),
boost::token_compress_on);
Expand Down Expand Up @@ -126,6 +136,13 @@ void ScanPath::load_event_series_scan_path()
double last_power = 0.0;
while (getline(file, line))
{
// If we reach the end of the scan path, we stop reading the file.
if (line.find("SCAN_PATH_END") != std::string::npos)
{
_scan_path_end = true;
break;
}

// For an event series the first segment is a ScanPathSegment point, then
// the rest are ScanPathSegment lines
ScanPathSegment segment;
Expand Down Expand Up @@ -220,4 +237,6 @@ std::vector<ScanPathSegment> ScanPath::get_segment_list() const
return _segment_list;
}

bool ScanPath::is_finished() const { return _scan_path_end; }

} // namespace adamantine
14 changes: 14 additions & 0 deletions source/ScanPath.hh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <deal.II/base/function.h>
#include <deal.II/base/point.h>

#include <filesystem>
#include <limits>
#include <string>
#include <vector>
Expand Down Expand Up @@ -93,6 +94,11 @@ public:
*/
void read_file();

/**
* Return true if we reach the end of the scan path.
*/
bool is_finished() const;

private:
/**
* Method to load a "segment" scan path file
Expand All @@ -111,6 +117,10 @@ private:
dealii::Point<3> &segment_start_point,
double &segment_start_time) const;

/**
* Flag is true if we have reached the end of _scan_path_file.
*/
bool _scan_path_end = false;
/**
* File name of the scan path
*/
Expand All @@ -119,6 +129,10 @@ private:
* Format of the scan path file, either segment of event_series.
*/
std::string _file_format;
/**
* Time the last time _scan_path_file was updated.
*/
std::filesystem::file_time_type _last_write_time;
/**
* The list of information about each segment in the scan path.
*/
Expand Down
32 changes: 31 additions & 1 deletion source/utils.hh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (c) 2016 - 2023, the adamantine authors.
/* Copyright (c) 2016 - 2024, the adamantine authors.
*
* This file is subject to the Modified BSD License and may not be distributed
* without copyright and license information. Please refer to the file LICENSE
Expand Down Expand Up @@ -38,6 +38,36 @@ inline void wait_for_file(std::string const &filename,
}
}

/**
* Wait for the file to be updated.
*/
inline void
wait_for_file_to_update(std::string const &filename, std::string const &message,
std::filesystem::file_time_type &last_write_time)
{
unsigned int counter = 1;
// We check when the file was last written to know if he file was updated.
// When the file is being overwritten, the last_write_time() function throws
// an error. Since it's an "expected" behavior, we just catch the error keep
// working.
try
{
while (std::filesystem::last_write_time(filename) == last_write_time)
{
// Spin loop waiting for the file to be updated (message printed if
// counter overflows)
if (counter == 0)
std::cout << message << std::endl;
++counter;
}
}
catch (std::filesystem::filesystem_error)
{
// No-op
}
last_write_time = std::filesystem::last_write_time(filename);
}

#define ASSERT(condition, message) assert((condition) && (message))

inline void ASSERT_THROW(bool cond, std::string const &message)
Expand Down
7 changes: 5 additions & 2 deletions source/validate_input_database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,11 @@ void validate_input_database(boost::property_tree::ptree &database)
"'rk_third_order', 'rk_fourth_order', 'backward_euler', "
"'implicit_midpoint', 'crank_nicolson', and 'sdirk2'.");

ASSERT_THROW(database.get<double>("time_stepping.duration") >= 0.0,
"Error: Time stepping duration must be non-negative.");
if (database.get("time.scan_path_for_duration", false))
{
ASSERT_THROW(database.get<double>("time_stepping.duration") >= 0.0,
"Error: Time stepping duration must be non-negative.");
}

ASSERT_THROW(database.get<double>("time_stepping.time_step") >= 0.0,
"Error: Time step must be non-negative.");
Expand Down

0 comments on commit 65540cc

Please sign in to comment.