From 2493dcf7f5af83d446ab2605d253951f3586b08b Mon Sep 17 00:00:00 2001 From: Katrin Koesler Date: Thu, 30 Jul 2026 14:29:52 -0400 Subject: [PATCH 1/7] Option to throw runtime error or print warning if maximum number of iterations is reached. This is specified in the input file with the parameter "max_iter_warning". If true, a warning is printed, if false (default) the error is thrown. --- src/algebra/Integrator.cpp | 9 +++++++-- src/algebra/Integrator.h | 3 ++- src/solve/SimulationParameters.cpp | 2 ++ src/solve/SimulationParameters.h | 3 +++ src/solve/Solver.cpp | 3 ++- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/algebra/Integrator.cpp b/src/algebra/Integrator.cpp index ab01c100d..f1a1f8312 100644 --- a/src/algebra/Integrator.cpp +++ b/src/algebra/Integrator.cpp @@ -4,7 +4,7 @@ #include "Integrator.h" Integrator::Integrator(Model* model, double time_step_size, double rho, - double atol, int max_iter) { + double atol, int max_iter, bool max_iter_warning) { this->model = model; alpha_m = 0.5 * (3.0 - rho) / (1.0 + rho); alpha_f = 1.0 / (1.0 + rho); @@ -19,6 +19,7 @@ Integrator::Integrator(Model* model, double time_step_size, double rho, this->time_step_size = time_step_size; this->atol = atol; this->max_iter = max_iter; + this->max_iter_warning = max_iter_warning; y_af = Eigen::Matrix(size); ydot_am = Eigen::Matrix(size); @@ -82,9 +83,13 @@ State Integrator::step(const State& old_state, double time) { // Abort if maximum number of non-linear iterations is reached else if (i == max_iter - 1) { - throw std::runtime_error( + if (max_iter_warning) { + std::cout << "Warning: Maximum number of non-linear iterations reached at time " << time << std::endl; + } else { + throw std::runtime_error( "Maximum number of non-linear iterations reached at time " + std::to_string(time)); + } } // Evaluate Jacobian diff --git a/src/algebra/Integrator.h b/src/algebra/Integrator.h index 0876ff763..ae65712fa 100644 --- a/src/algebra/Integrator.h +++ b/src/algebra/Integrator.h @@ -34,6 +34,7 @@ class Integrator { double y_coeff_jacobian{0.0}; double atol{0.0}; int max_iter{0}; + bool max_iter_warning{false}; int size{0}; int n_iter{0}; int n_nonlin_iter{0}; @@ -53,7 +54,7 @@ class Integrator { * @param max_iter Maximum number of non-linear iterations */ Integrator(Model* model, double time_step_size, double rho, double atol, - int max_iter); + int max_iter, bool max_iter_warning = false); /** * @brief Construct a new Integrator object diff --git a/src/solve/SimulationParameters.cpp b/src/solve/SimulationParameters.cpp index 036609994..c5cdc8a14 100644 --- a/src/solve/SimulationParameters.cpp +++ b/src/solve/SimulationParameters.cpp @@ -237,6 +237,8 @@ SimulationParameters load_simulation_params(const nlohmann::json& config) { } sim_params.sim_abs_tol = sim_config.value("absolute_tolerance", 1e-8); sim_params.sim_nliter = sim_config.value("maximum_nonlinear_iterations", 30); + sim_params.sim_max_iter_warning = + sim_config.value("max_iter_warning", false); sim_params.sim_steady_initial = sim_config.value("steady_initial", true); sim_params.sim_rho_infty = sim_config.value("rho_infty", 0.5); sim_params.output_variable_based = diff --git a/src/solve/SimulationParameters.h b/src/solve/SimulationParameters.h index b84b2845a..6999a9010 100644 --- a/src/solve/SimulationParameters.h +++ b/src/solve/SimulationParameters.h @@ -67,6 +67,9 @@ struct SimulationParameters { false}; ///< Running 0D simulation coupled with external solver double sim_external_step_size{0.0}; ///< Step size of external solver if ///< running coupled + bool sim_max_iter_warning{ + false}; ///< If true, warn instead of throwing when max nonlinear + ///< iterations is reached }; /// @brief Wrapper class for nlohmann:json with error checking diff --git a/src/solve/Solver.cpp b/src/solve/Solver.cpp index 7f9bf06a7..07317f55f 100644 --- a/src/solve/Solver.cpp +++ b/src/solve/Solver.cpp @@ -87,7 +87,8 @@ void Solver::setup_integrator() { DEBUG_MSG("Setup time integration"); integrator = Integrator(this->model.get(), simparams.sim_time_step_size, simparams.sim_rho_infty, simparams.sim_abs_tol, - simparams.sim_nliter); + simparams.sim_nliter, + simparams.sim_max_iter_warning); // Initialize loop states = std::vector(); From 021f80ba918c30f7e816a9751601c72260836bce Mon Sep 17 00:00:00 2001 From: Katrin Koesler Date: Thu, 30 Jul 2026 15:54:23 -0400 Subject: [PATCH 2/7] added max_iter warning to chamber_sphere test case --- tests/cases/chamber_sphere.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/cases/chamber_sphere.json b/tests/cases/chamber_sphere.json index 4936ccf09..e6b2988f6 100644 --- a/tests/cases/chamber_sphere.json +++ b/tests/cases/chamber_sphere.json @@ -26,7 +26,8 @@ "steady_initial": false, "output_variable_based": true, "absolute_tolerance": 1e-9, - "output_all_cycles": true + "output_all_cycles": true, + "max_iter_warning": true }, "vessels": [ { From abe789bbb55c13ac83ec9bdc5c54784db03f1027 Mon Sep 17 00:00:00 2001 From: Katrin Koesler Date: Thu, 30 Jul 2026 15:57:37 -0400 Subject: [PATCH 3/7] Added chamber sphere closed loop test case --- tests/cases/chamber_sphere_closed_loop.json | 162 ++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 tests/cases/chamber_sphere_closed_loop.json diff --git a/tests/cases/chamber_sphere_closed_loop.json b/tests/cases/chamber_sphere_closed_loop.json new file mode 100644 index 000000000..38ced86ec --- /dev/null +++ b/tests/cases/chamber_sphere_closed_loop.json @@ -0,0 +1,162 @@ +{ + "description": { + "description of test case": "Closed loop circulation with left ventricle modeled as sphere and lumped parameter model of vasculature inspired by Sharifi et al. 2024.", + "circuit": "LV -> AV -> aorta -> arteries -> arterioles -> capillaries -> venules -> veins -> RV -> MV -> LV" + }, + "simulation_parameters": { + "number_of_cardiac_cycles": 10, + "number_of_time_pts_per_cardiac_cycle": 5000, + "absolute_tolerance": 1e-8, + "output_variable_based": true, + "steady_initial": false, + "output_all_cycles": false, + "use_cycle_to_cycle_error": true + }, + "boundary_conditions": [], + "vessels": [ + { + "boundary_conditions": {}, + "vessel_id": 0, + "vessel_length": 1.0, + "vessel_name": "LV", + "zero_d_element_type": "ChamberSphere", + "zero_d_element_values": { + "rho": 1e3, + "thick0": 0.022317634723395277, + "radius0": 0.03457500522201688, + "sigma_max": 237593.9219611258, + "W1": 10e3, + "W2": 40, + "eta": 250.0, + "alpha_max": 19.0, + "alpha_min": -30.0, + "tsys": 0.17, + "tdias": 0.484, + "steepness": 0.005 + } + }, + { + "boundary_conditions": {}, + "vessel_name": "aorta", + "vessel_id": 1, + "vessel_length": 1.0, + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 2.666e6, + "C": 3.0e-9 + } + }, + { + "boundary_conditions": {}, + "vessel_id": 2, + "vessel_length": 1.0, + "vessel_name": "arteries", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 2.666e6, + "C": 6e-9 + } + }, + { + "boundary_conditions": {}, + "vessel_id": 3, + "vessel_length": 1.0, + "vessel_name": "arterioles", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 1.067e8, + "C": 7.5e-9 + } + }, + { + "boundary_conditions": {}, + "vessel_id": 4, + "vessel_length": 1.0, + "vessel_name": "capillaries", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 4.666e7, + "C": 7.5e-8 + } + }, + { + "boundary_conditions": {}, + "vessel_id": 5, + "vessel_length": 1.0, + "vessel_name": "venules", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 6.666e6, + "C": 2.25e-7 + } + }, + { + "boundary_conditions": {}, + "vessel_name": "veins", + "vessel_id": 6, + "vessel_length": 1.0, + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 6.666e6, + "C": 7.5e-7 + } + } + ], + "junctions": [ + { + "junction_name": "J_aorta_arteries", + "junction_type": "NORMAL_JUNCTION", + "inlet_blocks": ["aorta"], + "outlet_blocks": ["arteries"] + }, + { + "junction_name": "J_arteries_arterioles", + "junction_type": "NORMAL_JUNCTION", + "inlet_blocks": ["arteries"], + "outlet_blocks": ["arterioles"] + }, + { + "junction_name": "J_arterioles_capillaries", + "junction_type": "NORMAL_JUNCTION", + "inlet_blocks": ["arterioles"], + "outlet_blocks": ["capillaries"] + }, + { + "junction_name": "J_capillaries_venules", + "junction_type": "NORMAL_JUNCTION", + "inlet_blocks": ["capillaries"], + "outlet_blocks": ["venules"] + }, + { + "junction_name": "J_venules_veins", + "junction_type": "NORMAL_JUNCTION", + "inlet_blocks": ["venules"], + "outlet_blocks": ["veins"] + } + ], + + "valves": [ + { + "type": "ValveTanh", + "name": "MV", + "params": { + "Rmin": 0.0, + "Rmax": 1.0e10, + "Steepness": 0.1, + "upstream_block": "veins", + "downstream_block": "LV" + } + }, + { + "type": "ValveTanh", + "name": "AV", + "params": { + "Rmin": 0.0, + "Rmax": 1.0e11, + "Steepness": 1.0, + "upstream_block": "LV", + "downstream_block": "aorta" + } + } + ] +} From 473a311c4837cad6b5cf95d089c7af955c5d1592 Mon Sep 17 00:00:00 2001 From: Katrin Koesler Date: Thu, 30 Jul 2026 15:58:35 -0400 Subject: [PATCH 4/7] deleted closed loop case --- tests/cases/chamber_sphere_closed_loop.json | 162 -------------------- 1 file changed, 162 deletions(-) delete mode 100644 tests/cases/chamber_sphere_closed_loop.json diff --git a/tests/cases/chamber_sphere_closed_loop.json b/tests/cases/chamber_sphere_closed_loop.json deleted file mode 100644 index 38ced86ec..000000000 --- a/tests/cases/chamber_sphere_closed_loop.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "description": { - "description of test case": "Closed loop circulation with left ventricle modeled as sphere and lumped parameter model of vasculature inspired by Sharifi et al. 2024.", - "circuit": "LV -> AV -> aorta -> arteries -> arterioles -> capillaries -> venules -> veins -> RV -> MV -> LV" - }, - "simulation_parameters": { - "number_of_cardiac_cycles": 10, - "number_of_time_pts_per_cardiac_cycle": 5000, - "absolute_tolerance": 1e-8, - "output_variable_based": true, - "steady_initial": false, - "output_all_cycles": false, - "use_cycle_to_cycle_error": true - }, - "boundary_conditions": [], - "vessels": [ - { - "boundary_conditions": {}, - "vessel_id": 0, - "vessel_length": 1.0, - "vessel_name": "LV", - "zero_d_element_type": "ChamberSphere", - "zero_d_element_values": { - "rho": 1e3, - "thick0": 0.022317634723395277, - "radius0": 0.03457500522201688, - "sigma_max": 237593.9219611258, - "W1": 10e3, - "W2": 40, - "eta": 250.0, - "alpha_max": 19.0, - "alpha_min": -30.0, - "tsys": 0.17, - "tdias": 0.484, - "steepness": 0.005 - } - }, - { - "boundary_conditions": {}, - "vessel_name": "aorta", - "vessel_id": 1, - "vessel_length": 1.0, - "zero_d_element_type": "BloodVessel", - "zero_d_element_values": { - "R_poiseuille": 2.666e6, - "C": 3.0e-9 - } - }, - { - "boundary_conditions": {}, - "vessel_id": 2, - "vessel_length": 1.0, - "vessel_name": "arteries", - "zero_d_element_type": "BloodVessel", - "zero_d_element_values": { - "R_poiseuille": 2.666e6, - "C": 6e-9 - } - }, - { - "boundary_conditions": {}, - "vessel_id": 3, - "vessel_length": 1.0, - "vessel_name": "arterioles", - "zero_d_element_type": "BloodVessel", - "zero_d_element_values": { - "R_poiseuille": 1.067e8, - "C": 7.5e-9 - } - }, - { - "boundary_conditions": {}, - "vessel_id": 4, - "vessel_length": 1.0, - "vessel_name": "capillaries", - "zero_d_element_type": "BloodVessel", - "zero_d_element_values": { - "R_poiseuille": 4.666e7, - "C": 7.5e-8 - } - }, - { - "boundary_conditions": {}, - "vessel_id": 5, - "vessel_length": 1.0, - "vessel_name": "venules", - "zero_d_element_type": "BloodVessel", - "zero_d_element_values": { - "R_poiseuille": 6.666e6, - "C": 2.25e-7 - } - }, - { - "boundary_conditions": {}, - "vessel_name": "veins", - "vessel_id": 6, - "vessel_length": 1.0, - "zero_d_element_type": "BloodVessel", - "zero_d_element_values": { - "R_poiseuille": 6.666e6, - "C": 7.5e-7 - } - } - ], - "junctions": [ - { - "junction_name": "J_aorta_arteries", - "junction_type": "NORMAL_JUNCTION", - "inlet_blocks": ["aorta"], - "outlet_blocks": ["arteries"] - }, - { - "junction_name": "J_arteries_arterioles", - "junction_type": "NORMAL_JUNCTION", - "inlet_blocks": ["arteries"], - "outlet_blocks": ["arterioles"] - }, - { - "junction_name": "J_arterioles_capillaries", - "junction_type": "NORMAL_JUNCTION", - "inlet_blocks": ["arterioles"], - "outlet_blocks": ["capillaries"] - }, - { - "junction_name": "J_capillaries_venules", - "junction_type": "NORMAL_JUNCTION", - "inlet_blocks": ["capillaries"], - "outlet_blocks": ["venules"] - }, - { - "junction_name": "J_venules_veins", - "junction_type": "NORMAL_JUNCTION", - "inlet_blocks": ["venules"], - "outlet_blocks": ["veins"] - } - ], - - "valves": [ - { - "type": "ValveTanh", - "name": "MV", - "params": { - "Rmin": 0.0, - "Rmax": 1.0e10, - "Steepness": 0.1, - "upstream_block": "veins", - "downstream_block": "LV" - } - }, - { - "type": "ValveTanh", - "name": "AV", - "params": { - "Rmin": 0.0, - "Rmax": 1.0e11, - "Steepness": 1.0, - "upstream_block": "LV", - "downstream_block": "aorta" - } - } - ] -} From 7c4d0bdade33546fb2c474f4ced0af0c86372ea7 Mon Sep 17 00:00:00 2001 From: Katrin Koesler Date: Thu, 30 Jul 2026 16:23:43 -0400 Subject: [PATCH 5/7] documentation fix --- src/algebra/Integrator.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/algebra/Integrator.h b/src/algebra/Integrator.h index ae65712fa..eca90136b 100644 --- a/src/algebra/Integrator.h +++ b/src/algebra/Integrator.h @@ -52,6 +52,8 @@ class Integrator { * @param rho Spectral radius for generalized-alpha step * @param atol Absolut tolerance for non-linear iteration termination * @param max_iter Maximum number of non-linear iterations + * @param max_iter_warning If true, print a warning instead of throwing error + * when maximum iterations is reached */ Integrator(Model* model, double time_step_size, double rho, double atol, int max_iter, bool max_iter_warning = false); From 63e48d5811ea9d5fe13f5a98ccf7de4f82962cff Mon Sep 17 00:00:00 2001 From: Katrin Koesler Date: Mon, 3 Aug 2026 11:12:10 -0400 Subject: [PATCH 6/7] changed max_iter_warning to false in chamber_sphere test case --- tests/cases/chamber_sphere.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cases/chamber_sphere.json b/tests/cases/chamber_sphere.json index e6b2988f6..f2bb937a8 100644 --- a/tests/cases/chamber_sphere.json +++ b/tests/cases/chamber_sphere.json @@ -27,7 +27,7 @@ "output_variable_based": true, "absolute_tolerance": 1e-9, "output_all_cycles": true, - "max_iter_warning": true + "max_iter_warning": false }, "vessels": [ { From 56f3b1f2f4e4df0e794a7d267eabd4dcdf77c2fc Mon Sep 17 00:00:00 2001 From: Katrin Koesler Date: Mon, 3 Aug 2026 11:26:59 -0400 Subject: [PATCH 7/7] GUI test fixes --- .github/workflows/gui.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gui.yml b/.github/workflows/gui.yml index 9309c2eb2..8a7d79f3f 100644 --- a/.github/workflows/gui.yml +++ b/.github/workflows/gui.yml @@ -14,12 +14,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' - name: Install dependencies working-directory: tests/cypress @@ -43,7 +43,7 @@ jobs: FLASK_ENV: development - name: Run Cypress tests - uses: cypress-io/github-action@v5 + uses: cypress-io/github-action@v6 with: start: npm start working-directory: tests/cypress