From d91d6f9ecd9f75912609da05c36c11f1a7aa00b3 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:47:15 +0200 Subject: [PATCH 1/5] fix: complete partial temperature ramps --- src/setup/thermostatSetup.cpp | 3 ++- tests/src/setup/testThermostatSetup.cpp | 27 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/setup/thermostatSetup.cpp b/src/setup/thermostatSetup.cpp index c9f4277c7..04585e341 100644 --- a/src/setup/thermostatSetup.cpp +++ b/src/setup/thermostatSetup.cpp @@ -251,7 +251,8 @@ void ThermostatSetup::setupTemperatureRamp() const auto targetTemp = ThermostatSettings::getTargetTemperature(); const auto tempDelta = targetTemp - startTemp; - const auto tempIncrease = tempDelta / double(steps) * frequency; + const auto updates = (steps + frequency - 1) / frequency; + const auto tempIncrease = tempDelta / double(updates); _engine.getThermostat().setTemperatureIncrease(tempIncrease); _engine.getThermostat().setTemperatureRampingFrequency(frequency); diff --git a/tests/src/setup/testThermostatSetup.cpp b/tests/src/setup/testThermostatSetup.cpp index 16e377f0b..413990b73 100644 --- a/tests/src/setup/testThermostatSetup.cpp +++ b/tests/src/setup/testThermostatSetup.cpp @@ -99,7 +99,34 @@ TEST_F(TestSetup, setupThermostat_temp_ramping) thermostatSetup.getEngine().getThermostat().getRampingFrequency(), 2 ); +} + +TEST_F(TestSetup, temperatureRampReachesEndWithPartialFinalInterval) +{ + ThermostatSetup thermostatSetup(*_mdEngine); + + settings::TimingsSettings::setNumberOfSteps(10); + settings::ThermostatSettings::setThermostatType("berendsen"); + settings::ThermostatSettings::setTargetTemperature(300); + settings::ThermostatSettings::setStartTemperature(200); + settings::ThermostatSettings::setTemperatureRampSteps(10); + settings::ThermostatSettings::setTemperatureRampFrequency(3); + settings::ThermostatSettings::setEndTemperatureSet(false); + + thermostatSetup.setup(); + + EXPECT_DOUBLE_EQ( + thermostatSetup.getEngine().getThermostat().getTemperatureIncrease(), + 25.0 + ); + + for (size_t step = 0; step < 10; ++step) + thermostatSetup.getEngine().getThermostat().applyTemperatureRamping(); + EXPECT_DOUBLE_EQ( + thermostatSetup.getEngine().getThermostat().getTargetTemperature(), + 300.0 + ); } TEST_F(TestSetup, setupThermostat_only_end_temp_defined) From 9555dcdcdb14a372668dcc6e1d1ab6f05d6b68e9 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:48:27 +0200 Subject: [PATCH 2/5] fix: validate temperature ramp schedules --- docs/sphinx/src/userGuide/inputFile.rst | 2 + src/setup/thermostatSetup.cpp | 40 +++++++++++-------- tests/src/setup/testThermostatSetup.cpp | 51 +++++++++++++++++++++---- 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/docs/sphinx/src/userGuide/inputFile.rst b/docs/sphinx/src/userGuide/inputFile.rst index 30e04b017..4a960b795 100644 --- a/docs/sphinx/src/userGuide/inputFile.rst +++ b/docs/sphinx/src/userGuide/inputFile.rst @@ -815,6 +815,8 @@ Temperature Ramp Frequency With the ``temp_ramp_frequency`` keyword the user can specify the frequency of the temperature ramping from the ``start_temp`` to the ``temp`` value. If no starting temperature is given the keyword will be ignored. If a starting temperature is given and this keyword is omitted the temperature ramping will be performed, so that each step the temperature is increased by the same value. +If the ramp length is not divisible by this frequency, the temperature increments are scaled by the number of scheduled updates so that the final update reaches the requested target temperature exactly. + .. centered:: *default value* = 1 step .. _thermostatKey: diff --git a/src/setup/thermostatSetup.cpp b/src/setup/thermostatSetup.cpp index 04585e341..605eeee94 100644 --- a/src/setup/thermostatSetup.cpp +++ b/src/setup/thermostatSetup.cpp @@ -225,37 +225,47 @@ void ThermostatSetup::setupTemperatureRamp() return; /************************************************************* - * resetting the target temperature to the start temperature * + * If steps is 0, set the steps to the total number of steps * *************************************************************/ - const auto startTemp = ThermostatSettings::getStartTemperature(); + auto steps = ThermostatSettings::getTemperatureRampSteps(); + const auto useFullSimulation = steps == 0; - _engine.getThermostat().setTargetTemperature(startTemp); - ThermostatSettings::setActualTargetTemperature(startTemp); + if (useFullSimulation) + steps = TimingsSettings::getNumberOfSteps(); + + if (steps == 0) + throw InputFileException( + "Temperature ramp requires at least one simulation step" + ); + + const auto frequency = ThermostatSettings::getTemperatureRampFrequency(); - auto steps = ThermostatSettings::getTemperatureRampSteps(); + if (frequency == 0) + throw InputFileException( + "Temperature ramp frequency must be greater than zero" + ); + + if (useFullSimulation) + ThermostatSettings::setTemperatureRampSteps(steps); /************************************************************* - * If steps is 0, set the steps to the total number of steps * + * resetting the target temperature to the start temperature * *************************************************************/ - if (steps == 0) - { - steps = TimingsSettings::getNumberOfSteps(); - ThermostatSettings::setTemperatureRampSteps(steps); - } + const auto startTemp = ThermostatSettings::getStartTemperature(); + _engine.getThermostat().setTargetTemperature(startTemp); + ThermostatSettings::setActualTargetTemperature(startTemp); _engine.getThermostat().setTemperatureRampingSteps(steps); - - const auto frequency = ThermostatSettings::getTemperatureRampFrequency(); + _engine.getThermostat().setTemperatureRampingFrequency(frequency); const auto targetTemp = ThermostatSettings::getTargetTemperature(); const auto tempDelta = targetTemp - startTemp; - const auto updates = (steps + frequency - 1) / frequency; + const auto updates = steps / frequency + (steps % frequency != 0); const auto tempIncrease = tempDelta / double(updates); _engine.getThermostat().setTemperatureIncrease(tempIncrease); - _engine.getThermostat().setTemperatureRampingFrequency(frequency); } void ThermostatSetup::writeSetupInfo() const diff --git a/tests/src/setup/testThermostatSetup.cpp b/tests/src/setup/testThermostatSetup.cpp index 413990b73..d92fca40d 100644 --- a/tests/src/setup/testThermostatSetup.cpp +++ b/tests/src/setup/testThermostatSetup.cpp @@ -28,7 +28,9 @@ #include "berendsenThermostat.hpp" // for BerendsenThermostat #include "constants/conversionFactors.hpp" // for _FS_TO_S_, _KG_TO_GRAM_ #include "constants/natureConstants.hpp" // for _UNIVERSAL_GAS_CONSTANT_ +#include "exceptions.hpp" // for InputFileException #include "gtest/gtest.h" // for Message, TestPartResult +#include "inputFileReader.hpp" // for InputFileReader #include "langevinThermostat.hpp" // for LangevinThermostat #include "noseHooverThermostat.hpp" // for NoseHooverThermostat #include "testSetup.hpp" // for TestSetup @@ -103,15 +105,15 @@ TEST_F(TestSetup, setupThermostat_temp_ramping) TEST_F(TestSetup, temperatureRampReachesEndWithPartialFinalInterval) { - ThermostatSetup thermostatSetup(*_mdEngine); + ThermostatSetup thermostatSetup(*_mdEngine); + input::InputFileReader reader("input.in", *_mdEngine); - settings::TimingsSettings::setNumberOfSteps(10); - settings::ThermostatSettings::setThermostatType("berendsen"); - settings::ThermostatSettings::setTargetTemperature(300); - settings::ThermostatSettings::setStartTemperature(200); - settings::ThermostatSettings::setTemperatureRampSteps(10); - settings::ThermostatSettings::setTemperatureRampFrequency(3); - settings::ThermostatSettings::setEndTemperatureSet(false); + reader.process({"nstep", "=", "10"}); + reader.process({"thermostat", "=", "berendsen"}); + reader.process({"temp", "=", "300"}); + reader.process({"start_temp", "=", "200"}); + reader.process({"temp_ramp_steps", "=", "10"}); + reader.process({"temp_ramp_frequency", "=", "3"}); thermostatSetup.setup(); @@ -127,6 +129,39 @@ TEST_F(TestSetup, temperatureRampReachesEndWithPartialFinalInterval) thermostatSetup.getEngine().getThermostat().getTargetTemperature(), 300.0 ); + + settings::ThermostatSettings::setTemperatureRampSteps(0); + settings::ThermostatSettings::setTemperatureRampFrequency(1); +} + +TEST_F(TestSetup, rejectsEmptyTemperatureRamp) +{ + ThermostatSetup thermostatSetup(*_mdEngine); + + settings::TimingsSettings::setNumberOfSteps(0); + settings::ThermostatSettings::setThermostatType("berendsen"); + settings::ThermostatSettings::setTargetTemperature(300); + settings::ThermostatSettings::setStartTemperature(200); + settings::ThermostatSettings::setTemperatureRampSteps(0); + + EXPECT_THROW(thermostatSetup.setup(), customException::InputFileException); +} + +TEST_F(TestSetup, rejectsZeroTemperatureRampFrequency) +{ + ThermostatSetup thermostatSetup(*_mdEngine); + + settings::TimingsSettings::setNumberOfSteps(10); + settings::ThermostatSettings::setThermostatType("berendsen"); + settings::ThermostatSettings::setTargetTemperature(300); + settings::ThermostatSettings::setStartTemperature(200); + settings::ThermostatSettings::setTemperatureRampSteps(10); + settings::ThermostatSettings::setTemperatureRampFrequency(0); + + EXPECT_THROW(thermostatSetup.setup(), customException::InputFileException); + + settings::ThermostatSettings::setTemperatureRampSteps(0); + settings::ThermostatSettings::setTemperatureRampFrequency(1); } TEST_F(TestSetup, setupThermostat_only_end_temp_defined) From 533e4549749dfb7d3dca4ece063ba3902bb4e6d4 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Thu, 30 Jul 2026 12:59:47 +0200 Subject: [PATCH 3/5] chore: add required changelog fragment --- changes/temperature-ramp-updates.user.bugfix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/temperature-ramp-updates.user.bugfix.md diff --git a/changes/temperature-ramp-updates.user.bugfix.md b/changes/temperature-ramp-updates.user.bugfix.md new file mode 100644 index 000000000..0a57032a8 --- /dev/null +++ b/changes/temperature-ramp-updates.user.bugfix.md @@ -0,0 +1 @@ +- Temperature ramps validate their schedule and reach the requested target across partial final intervals. From d7fdfc0a07f96181799f82f69bee10453b93dd91 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Sat, 1 Aug 2026 23:23:21 +0200 Subject: [PATCH 4/5] style: format thermostat setup test --- tests/src/setup/testThermostatSetup.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/src/setup/testThermostatSetup.cpp b/tests/src/setup/testThermostatSetup.cpp index 1b4868d1c..4d10cfa32 100644 --- a/tests/src/setup/testThermostatSetup.cpp +++ b/tests/src/setup/testThermostatSetup.cpp @@ -28,13 +28,13 @@ #include "constants/conversionFactors.hpp" // for _FS_TO_S_, _KG_TO_GRAM_ #include "constants/natureConstants.hpp" // for _UNIVERSAL_GAS_CONSTANT_ #include "exceptions.hpp" // for InputFileException -#include "inputFileReader.hpp" // for InputFileReader -#include "langevinThermostat.hpp" // for LangevinThermostat -#include "noseHooverThermostat.hpp" // for NoseHooverThermostat -#include "testSetup.hpp" // for TestSetup -#include "thermostatSettings.hpp" // for ThermostatSettings -#include "thermostatSetup.hpp" // for ThermostatSetup, setupThermostat -#include "timingsSettings.hpp" // for TimingsSettings +#include "inputFileReader.hpp" // for InputFileReader +#include "langevinThermostat.hpp" // for LangevinThermostat +#include "noseHooverThermostat.hpp" // for NoseHooverThermostat +#include "testSetup.hpp" // for TestSetup +#include "thermostatSettings.hpp" // for ThermostatSettings +#include "thermostatSetup.hpp" // for ThermostatSetup, setupThermostat +#include "timingsSettings.hpp" // for TimingsSettings #include "velocityRescalingThermostat.hpp" // for VelocityRescalingThermostat using namespace setup; From 2092bc3a857efcbf7e046a759ce89070cfdedef4 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Thu, 13 Aug 2026 11:11:47 +0200 Subject: [PATCH 5/5] style: clean temperature ramp comment --- src/setup/thermostatSetup.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/setup/thermostatSetup.cpp b/src/setup/thermostatSetup.cpp index a4a19c2c7..b3584bb54 100644 --- a/src/setup/thermostatSetup.cpp +++ b/src/setup/thermostatSetup.cpp @@ -219,8 +219,7 @@ void ThermostatSetup::setupNoseHooverThermostat() void ThermostatSetup::setupTemperatureRamp() { /************************************************************************* - * If the start temperature is defined, the temperature ramp is enabled. - ** + * If the start temperature is defined, the temperature ramp is enabled. * *************************************************************************/ if (!ThermostatSettings::isStartTemperatureSet())