Skip to content

Commit 0305596

Browse files
Spatially variable robin bug fixes (SimVascular#446)
1 parent 277dd06 commit 0305596

File tree

11 files changed

+125
-45
lines changed

11 files changed

+125
-45
lines changed

Code/Source/solver/BoundaryCondition.cpp

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,15 @@
4040

4141
#define n_debug_bc
4242

43-
BoundaryCondition::BoundaryCondition(const std::string& vtp_file_path, const std::vector<std::string>& array_names, const StringBoolMap& flags, const faceType& face)
43+
BoundaryCondition::BoundaryCondition(const std::string& vtp_file_path, const std::vector<std::string>& array_names, const StringBoolMap& flags, const faceType& face, SimulationLogger& logger)
4444
: face_(&face)
4545
, global_num_nodes_(face.nNo)
4646
, local_num_nodes_(0)
4747
, array_names_(array_names)
4848
, spatially_variable(true)
4949
, vtp_file_path_(vtp_file_path)
5050
, flags_(flags)
51+
, logger_(&logger)
5152
{
5253
try {
5354
global_data_ = read_data_from_vtp_file(vtp_file_path, array_names);
@@ -73,13 +74,14 @@ BoundaryCondition::BoundaryCondition(const std::string& vtp_file_path, const std
7374
}
7475
}
7576

76-
BoundaryCondition::BoundaryCondition(const StringDoubleMap& uniform_values, const StringBoolMap& flags, const faceType& face)
77+
BoundaryCondition::BoundaryCondition(const StringDoubleMap& uniform_values, const StringBoolMap& flags, const faceType& face, SimulationLogger& logger)
7778
: face_(&face)
7879
, global_num_nodes_(face.nNo)
7980
, local_num_nodes_(0)
8081
, spatially_variable(false)
8182
, vtp_file_path_("")
8283
, flags_(flags)
84+
, logger_(&logger)
8385
{
8486
try {
8587
// Store array names, validate and store values
@@ -107,6 +109,7 @@ BoundaryCondition::BoundaryCondition(const BoundaryCondition& other)
107109
, vtp_file_path_(other.vtp_file_path_)
108110
, flags_(other.flags_)
109111
, global_node_map_(other.global_node_map_)
112+
, logger_(other.logger_)
110113
{
111114
if (other.vtp_data_) {
112115
vtp_data_ = std::make_unique<VtkVtpData>(*other.vtp_data_);
@@ -126,6 +129,7 @@ void swap(BoundaryCondition& lhs, BoundaryCondition& rhs) noexcept {
126129
swap(lhs.flags_, rhs.flags_);
127130
swap(lhs.global_node_map_, rhs.global_node_map_);
128131
swap(lhs.vtp_data_, rhs.vtp_data_);
132+
swap(lhs.logger_, rhs.logger_);
129133
}
130134

131135
BoundaryCondition& BoundaryCondition::operator=(BoundaryCondition other) {
@@ -145,6 +149,7 @@ BoundaryCondition::BoundaryCondition(BoundaryCondition&& other) noexcept
145149
, flags_(std::move(other.flags_))
146150
, global_node_map_(std::move(other.global_node_map_))
147151
, vtp_data_(std::move(other.vtp_data_))
152+
, logger_(other.logger_)
148153
{
149154
other.face_ = nullptr;
150155
other.global_num_nodes_ = 0;
@@ -203,6 +208,11 @@ BoundaryCondition::StringArrayMap BoundaryCondition::read_data_from_vtp_file(con
203208
#endif
204209
}
205210

211+
logger_ -> log_message("[BoundaryCondition] Loaded from VTP file");
212+
logger_ -> log_message("\t File path:", vtp_file_path);
213+
logger_ -> log_message("\t Arrays:", array_names);
214+
logger_ -> log_message("\t Face:", face_->name);
215+
206216
return result;
207217
}
208218

@@ -375,11 +385,20 @@ void BoundaryCondition::distribute_spatially_variable(const ComMod& com_mod, con
375385
// Get VTP points for position matching
376386
Array<double> vtp_points = vtp_data_->get_points();
377387

378-
// Look up data for all nodes using point matching
388+
// Get mesh scale factor from the face's mesh
389+
double mesh_scale_factor = 1.0; // Default scale factor
390+
if (face_ != nullptr) {
391+
mesh_scale_factor = com_mod.msh[face_->iM].scF;
392+
#ifdef debug_distribute_spatially_variable
393+
dmsg << "Mesh scale factor: " << mesh_scale_factor << std::endl;
394+
#endif
395+
}
396+
397+
// Look up data for all nodes using point matching
379398
for (const auto& array_name : array_names_) {
380399
all_values[array_name].resize(total_num_nodes);
381400
for (int i = 0; i < total_num_nodes; i++) {
382-
int vtp_idx = find_vtp_point_index(all_positions(0,i), all_positions(1,i), all_positions(2,i), vtp_points);
401+
int vtp_idx = find_vtp_point_index(all_positions(0,i), all_positions(1,i), all_positions(2,i), vtp_points, mesh_scale_factor);
383402
all_values[array_name](i) = global_data_[array_name](vtp_idx, 0);
384403
}
385404
}
@@ -474,10 +493,13 @@ void BoundaryCondition::distribute_flags(const CmMod& cm_mod, const cmType& cm,
474493
}
475494

476495
int BoundaryCondition::find_vtp_point_index(double x, double y, double z,
477-
const Array<double>& vtp_points) const
496+
const Array<double>& vtp_points, double mesh_scale_factor) const
478497
{
479498
const int num_points = vtp_points.ncols();
480-
Vector<double> target_point{x, y, z};
499+
500+
// Scale down the target coordinates to match the unscaled VTP coordinates
501+
// The simulation coordinates are scaled by mesh_scale_factor, but VTP coordinates are not
502+
Vector<double> target_point{x / mesh_scale_factor, y / mesh_scale_factor, z / mesh_scale_factor};
481503

482504
// Simple linear search through all points in the VTP file
483505
for (int i = 0; i < num_points; i++) {
@@ -490,6 +512,7 @@ int BoundaryCondition::find_vtp_point_index(double x, double y, double z,
490512
#ifdef debug_bc_find_vtp_point_index
491513
DebugMsg dmsg(__func__, 0);
492514
dmsg << "Found VTP point index for node at position (" << x << ", " << y << ", " << z << ")" << std::endl;
515+
dmsg << "Scaled target position (" << target_point(0) << ", " << target_point(1) << ", " << target_point(2) << ")" << std::endl;
493516
dmsg << "VTP point index: " << i << std::endl;
494517
#endif
495518

Code/Source/solver/BoundaryCondition.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "CmMod.h"
3737
#include "VtkData.h"
3838
#include <string>
39+
#include "SimulationLogger.h"
3940
#include <memory>
4041
#include <map>
4142
#include <vector>
@@ -79,7 +80,7 @@ class BoundaryCondition {
7980
using StringDoubleMap = std::map<std::string, double>;
8081

8182
/// @brief Data members for BC
82-
const faceType* face_ = nullptr; ///< Face associated with the BC (can be null)
83+
const faceType* face_ = nullptr; ///< Face associated with the BC (not owned by BoundaryCondition)
8384
int global_num_nodes_ = 0; ///< Global number of nodes on the face
8485
int local_num_nodes_ = 0; ///< Local number of nodes on this processor
8586
std::vector<std::string> array_names_; ///< Names of arrays to read from VTP file
@@ -90,6 +91,7 @@ class BoundaryCondition {
9091
std::string vtp_file_path_; ///< Path to VTP file (empty if uniform)
9192
std::map<int, int> global_node_map_; ///< Maps global node IDs to local array indices
9293
std::unique_ptr<VtkVtpData> vtp_data_; ///< VTP data object
94+
const SimulationLogger* logger_ = nullptr; ///< Logger for warnings/info (not owned by BoundaryCondition)
9395

9496
public:
9597
/// @brief Tolerance for point matching in VTP files
@@ -102,13 +104,15 @@ class BoundaryCondition {
102104
/// @param vtp_file_path Path to VTP file containing arrays
103105
/// @param array_names Names of arrays to read from VTP file
104106
/// @param face Face associated with the BC
107+
/// @param logger Simulation logger used to write warnings
105108
/// @throws std::runtime_error if file cannot be read or arrays are missing
106-
BoundaryCondition(const std::string& vtp_file_path, const std::vector<std::string>& array_names, const StringBoolMap& flags, const faceType& face);
109+
BoundaryCondition(const std::string& vtp_file_path, const std::vector<std::string>& array_names, const StringBoolMap& flags, const faceType& face, SimulationLogger& logger);
107110

108111
/// @brief Constructor for uniform values
109112
/// @param uniform_values Map of array names to uniform values
110113
/// @param face Face associated with the BC
111-
BoundaryCondition(const StringDoubleMap& uniform_values, const StringBoolMap& flags, const faceType& face);
114+
/// @param logger Simulation logger used to write warnings
115+
BoundaryCondition(const StringDoubleMap& uniform_values, const StringBoolMap& flags, const faceType& face, SimulationLogger& logger);
112116

113117
/// @brief Copy constructor
114118
BoundaryCondition(const BoundaryCondition& other);
@@ -198,9 +202,10 @@ class BoundaryCondition {
198202
/// @param y Y coordinate
199203
/// @param z Z coordinate
200204
/// @param vtp_points VTP points array
205+
/// @param mesh_scale_factor Scale factor applied to mesh coordinates
201206
/// @return Index of the matching point in the VTP array
202207
/// @throws std::runtime_error if no matching point is found
203-
int find_vtp_point_index(double x, double y, double z, const Array<double>& vtp_points) const;
208+
int find_vtp_point_index(double x, double y, double z, const Array<double>& vtp_points, double mesh_scale_factor) const;
204209

205210
/// @brief Hook for derived classes to validate array values
206211
/// @param array_name Name of the array being validated

Code/Source/solver/Parameters.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,6 @@ BoundaryConditionParameters::BoundaryConditionParameters()
443443
set_parameter("Spatial_profile_file_path", "", !required, spatial_profile_file_path);
444444
set_parameter("Spatial_values_file_path", "", !required, spatial_values_file_path);
445445
set_parameter("Stiffness", 1.0, !required, stiffness);
446-
set_parameter("Robin_vtp_file_path", "", !required, robin_vtp_file_path);
447446
set_parameter("svZeroDSolver_block", "", !required, svzerod_solver_block);
448447

449448
set_parameter("Temporal_and_spatial_values_file_path", "", !required, temporal_and_spatial_values_file_path);

Code/Source/solver/Parameters.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,6 @@ class BoundaryConditionParameters : public ParameterLists
806806
Parameter<std::string> spatial_profile_file_path;
807807
Parameter<std::string> spatial_values_file_path;
808808
Parameter<double> stiffness;
809-
Parameter<std::string> robin_vtp_file_path;
810809
Parameter<std::string> svzerod_solver_block;
811810

812811
Parameter<std::string> temporal_and_spatial_values_file_path;

Code/Source/solver/RobinBoundaryCondition.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@
2929
*/
3030

3131
#include "RobinBoundaryCondition.h"
32+
#include <iostream>
3233

3334
#define n_debug_robin_bc
3435

36+
RobinBoundaryCondition::RobinBoundaryCondition(double uniform_stiffness, double uniform_damping, bool normal_only, const faceType& face, SimulationLogger& logger)
37+
: BoundaryCondition({{"Stiffness", uniform_stiffness}, {"Damping", uniform_damping}}, StringBoolMap{{"normal_direction_only", normal_only}}, face, logger)
38+
{
39+
// Warning if both stiffness and damping are zero
40+
if (uniform_stiffness == 0.0 && uniform_damping == 0.0) {
41+
logger_ -> log_message("WARNING [RobinBoundaryCondition] Both stiffness and damping values set to zero. "
42+
"This will result in effectively no boundary condition being applied.");
43+
}
44+
}
45+

Code/Source/solver/RobinBoundaryCondition.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,22 @@ class RobinBoundaryCondition : public BoundaryCondition {
7070
/// @param vtp_file_path Path to VTP file containing Stiffness and Damping point arrays
7171
/// @param normal_only Flag to apply only along normal direction
7272
/// @param face Face associated with the Robin BC
73+
/// @param logger Simulation logger used to write warnings
7374
/// @throws BoundaryConditionFileException if file cannot be read
7475
/// @throws BoundaryConditionVtpArrayException if arrays are missing
7576
/// @throws BoundaryConditionValidationException if values are invalid
76-
RobinBoundaryCondition(const std::string& vtp_file_path, bool normal_only, const faceType& face)
77-
: BoundaryCondition(vtp_file_path, std::vector<std::string>{"Stiffness", "Damping"}, StringBoolMap{{"normal_direction_only", normal_only}}, face) {}
77+
RobinBoundaryCondition(const std::string& vtp_file_path, bool normal_only, const faceType& face, SimulationLogger& logger)
78+
: BoundaryCondition(vtp_file_path, std::vector<std::string>{"Stiffness", "Damping"}, StringBoolMap{{"normal_direction_only", normal_only}}, face, logger) {}
7879

7980

8081
/// @brief Constructor for uniform values
8182
/// @param uniform_stiffness Uniform stiffness value for all nodes
8283
/// @param uniform_damping Uniform damping value for all nodes
8384
/// @param normal_only Flag to apply only along normal direction
8485
/// @param face Face associated with the Robin BC
86+
/// @param logger Simulation logger used to write warnings
8587
/// @throws BoundaryConditionValidationException if values are invalid
86-
RobinBoundaryCondition(double uniform_stiffness, double uniform_damping, bool normal_only, const faceType& face)
87-
: BoundaryCondition({{"Stiffness", uniform_stiffness}, {"Damping", uniform_damping}}, StringBoolMap{{"normal_direction_only", normal_only}}, face) {}
88+
RobinBoundaryCondition(double uniform_stiffness, double uniform_damping, bool normal_only, const faceType& face, SimulationLogger& logger);
8889

8990
/// @brief Apply only along normal direction (getter)
9091
/// @return true if BC should be applied only along normal direction

Code/Source/solver/SimulationLogger.h

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class SimulationLogger {
4747
this->initialize(file_name, cout_write);
4848
}
4949

50-
void initialize(const std::string& file_name, bool cout_write=false)
50+
void initialize(const std::string& file_name, bool cout_write=false) const
5151
{
5252
log_file_.open(file_name);
5353
if (log_file_.fail()) {
@@ -58,12 +58,14 @@ class SimulationLogger {
5858
file_name_ = file_name;
5959
}
6060

61+
bool is_initialized() const { return log_file_.is_open(); }
62+
6163
~SimulationLogger()
6264
{
6365
log_file_.close();
6466
}
6567

66-
template <class T> SimulationLogger& operator<< (const T& value)
68+
template <class T> const SimulationLogger& operator<< (const T& value) const
6769
{
6870
if (file_name_ == "") {
6971
return *this;
@@ -78,7 +80,28 @@ class SimulationLogger {
7880
return *this;
7981
}
8082

81-
SimulationLogger& operator<<(std::ostream&(*value)(std::ostream& o))
83+
// Special handling for vector<string>
84+
const SimulationLogger& operator<< (const std::vector<std::string>& values) const
85+
{
86+
if (file_name_ == "") {
87+
return *this;
88+
}
89+
90+
bool first = true;
91+
for (const auto& value : values) {
92+
if (!first) {
93+
log_file_ << ", ";
94+
if (cout_write_) std::cout << ", ";
95+
}
96+
log_file_ << value;
97+
if (cout_write_) std::cout << value;
98+
first = false;
99+
}
100+
101+
return *this;
102+
}
103+
104+
const SimulationLogger& operator<<(std::ostream&(*value)(std::ostream& o)) const
82105
{
83106
if (file_name_ == "") {
84107
return *this;
@@ -93,10 +116,27 @@ class SimulationLogger {
93116
return *this;
94117
};
95118

119+
/// @brief Log a message with automatic space separation
120+
/// @param args Arguments to log
121+
template<typename... Args>
122+
const SimulationLogger& log_message(const Args&... args) const {
123+
if (file_name_ == "") return *this;
124+
125+
bool first = true;
126+
((first ? (first = false, (*this << args)) : (*this << ' ' << args)), ...);
127+
*this << std::endl;
128+
return *this;
129+
}
130+
96131
private:
97-
bool cout_write_ = false;
98-
std::string file_name_;
99-
std::ofstream log_file_;
132+
// These members are marked mutable because they can be modified in const member functions.
133+
// While logging writes to a file (modifying these members), it doesn't change the logical
134+
// state of the logger - this is exactly what mutable is designed for: implementation
135+
// details that need to change even when the object's public state remains constant.
136+
mutable bool cout_write_ = false;
137+
mutable bool initialized_ = false;
138+
mutable std::string file_name_;
139+
mutable std::ofstream log_file_;
100140
};
101141

102142
#endif

Code/Source/solver/initialize.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -371,22 +371,6 @@ void initialize(Simulation* simulation, Vector<double>& timeP)
371371
dmsg.banner();
372372
#endif
373373

374-
// Setup logging to history file.
375-
if (!simulation->com_mod.cm.slv(simulation->cm_mod)) {
376-
std::string hist_file_name;
377-
378-
if (chnl_mod.appPath != "") {
379-
auto mkdir_arg = std::string("mkdir -p ") + chnl_mod.appPath;
380-
std::system(mkdir_arg.c_str());
381-
hist_file_name = chnl_mod.appPath + "/" + simulation->history_file_name;
382-
} else {
383-
hist_file_name = simulation->history_file_name;
384-
}
385-
386-
bool output_to_cout = true;
387-
simulation->logger.initialize(hist_file_name, output_to_cout);
388-
}
389-
390374
#ifdef debug_initialize
391375
dmsg << "Set time " << std::endl;
392376
dmsg << "com_mod.timeP[0]: " << com_mod.timeP[0] << std::endl;

Code/Source/solver/read_files.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -394,15 +394,17 @@ void read_bc(Simulation* simulation, EquationParameters* eq_params, eqType& lEq,
394394
if (utils::btest(lBc.bType, enum_int(BoundaryConditionType::bType_Robin))) {
395395

396396
// Read VTP file path for per-node stiffness and damping (optional)
397-
if (bc_params->robin_vtp_file_path.defined()) {
398-
lBc.robin_bc = RobinBoundaryCondition(bc_params->robin_vtp_file_path.value(),
397+
if (bc_params->spatial_values_file_path.defined()) {
398+
lBc.robin_bc = RobinBoundaryCondition(bc_params->spatial_values_file_path.value(),
399399
bc_params->apply_along_normal_direction.value(),
400-
com_mod.msh[lBc.iM].fa[lBc.iFa]);
400+
com_mod.msh[lBc.iM].fa[lBc.iFa],
401+
simulation->logger);
401402
} else {
402403
lBc.robin_bc = RobinBoundaryCondition(bc_params->stiffness.value(),
403404
bc_params->damping.value(),
404405
bc_params->apply_along_normal_direction.value(),
405-
com_mod.msh[lBc.iM].fa[lBc.iFa]);
406+
com_mod.msh[lBc.iM].fa[lBc.iFa],
407+
simulation->logger);
406408
}
407409
}
408410

@@ -1703,7 +1705,23 @@ void read_files(Simulation* simulation, const std::string& file_name)
17031705
com_mod.stFileFlag = gen_params.continue_previous_simulation.value();
17041706
}
17051707

1706-
// Set simulatioin and module member data from XML parameters.
1708+
// Setup logging to history file.
1709+
if (!simulation->com_mod.cm.slv(simulation->cm_mod)) {
1710+
std::string hist_file_name;
1711+
1712+
if (chnl_mod.appPath != "") {
1713+
auto mkdir_arg = std::string("mkdir -p ") + chnl_mod.appPath;
1714+
std::system(mkdir_arg.c_str());
1715+
hist_file_name = chnl_mod.appPath + "/" + simulation->history_file_name;
1716+
} else {
1717+
hist_file_name = simulation->history_file_name;
1718+
}
1719+
1720+
bool output_to_cout = true;
1721+
simulation->logger.initialize(hist_file_name, output_to_cout);
1722+
}
1723+
1724+
// Set simulation and module member data from XML parameters.
17071725
simulation->set_module_parameters();
17081726

17091727
// Read mesh and BCs data.

tests/cases/struct/spatially_variable_robin/solver.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132

133133
<Add_BC name="Y0" >
134134
<Type> Robin </Type>
135-
<Robin_vtp_file_path> Y0_spatially_varying_robin.vtp </Robin_vtp_file_path>
135+
<Spatial_values_file_path> Y0_spatially_varying_robin.vtp </Spatial_values_file_path>
136136
<Apply_along_normal_direction> false </Apply_along_normal_direction>
137137
</Add_BC>
138138

0 commit comments

Comments
 (0)