Skip to content

Commit 777f50c

Browse files
feat(md): add CSVR thermostat for NVT molecular dynamics (#7461)
* feat(md): add CSVR thermostat for NVT molecular dynamics Implement the Canonical Sampling through Velocity Rescaling (CSVR) thermostat as described in: G. Bussi, D. Donadio, M. Parrinello, J. Chem. Phys. 126, 014101 (2007) Features: - New thermostat option: md_thermostat = csvr - New parameter: md_csvr_tau (characteristic time scale) - Properly samples the canonical (NVT) ensemble - Simple implementation with only one parameter Implements #6941 * docs(md): add CSVR thermostat documentation - Add csvr option to md_thermostat parameter description - Add md_csvr_tau parameter documentation Implements #6941 * test(md): add unit test for CSVR thermostat Add CSVR thermostat test case to verlet_test.cpp: - Test position update correctness - Verify temperature is in reasonable range Implements #6941 * fix: address review comments for CSVR thermostat - Fix degrees of freedom: use 3N - frozen_freedom instead of frozen_freedom - Use MD_func::gaussrand() instead of std::random for consistency - Simplify CSVR core formula with factor2 variable - Remove unused #include <random> Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com> Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com> --------- Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
1 parent 59ebed5 commit 777f50c

6 files changed

Lines changed: 113 additions & 0 deletions

File tree

docs/advanced/input_files/input-main.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3194,6 +3194,7 @@
31943194
- berendsen: Berendsen thermostat, see md_nraise in detail.
31953195
- rescaling: velocity Rescaling method 1, see md_tolerance in detail.
31963196
- rescale_v: velocity Rescaling method 2, see md_nraise in detail.
3197+
- csvr: Canonical Sampling through Velocity Rescaling, see md_csvr_tau in detail.
31973198
- **Default**: nhc
31983199

31993200
### md_tfirst
@@ -3445,6 +3446,13 @@
34453446
- **Default**: 1.0
34463447
- **Unit**: fs
34473448

3449+
### md_csvr_tau
3450+
3451+
- **Type**: Real
3452+
- **Description**: The characteristic time scale for the CSVR (Canonical Sampling through Velocity Rescaling) thermostat. Larger values give weaker coupling (longer relaxation time), smaller values give stronger coupling (shorter relaxation time). Recommended value: 100 * md_dt.
3453+
- **Default**: 100.0
3454+
- **Unit**: fs
3455+
34483456
### md_tolerance
34493457

34503458
- **Type**: Real

source/source_io/module_parameter/md_parameter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ struct MD_para
5858

5959
double md_damp = 1.0; ///< Langevin damping parameter (time units)
6060

61+
double md_csvr_tau = 100.0; ///< CSVR thermostat characteristic time scale (in MD time units)
62+
6163
double md_tolerance = 100.0; ///< tolerance for velocity rescaling (K)
6264
int md_nraise = 1; ///< parameters used when md_type=nvt
6365

source/source_io/module_parameter/read_input_item_md.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,20 @@ Note: It is a system-dependent empirical parameter. An improper choice might lea
661661
read_sync_double(input.mdp.md_damp);
662662
this->add_item(item);
663663
}
664+
{
665+
Input_Item item("md_csvr_tau");
666+
item.annotation = "CSVR thermostat characteristic time scale";
667+
item.category = "Molecular dynamics";
668+
item.type = "Real";
669+
item.description = "The characteristic time scale for the CSVR (Canonical Sampling through Velocity "
670+
"Rescaling) thermostat. Larger values give weaker coupling, smaller values give "
671+
"stronger coupling. Recommended value: 100 * md_dt.";
672+
item.default_value = "100.0";
673+
item.unit = "fs";
674+
item.availability = "md_thermostat = csvr";
675+
read_sync_double(input.mdp.md_csvr_tau);
676+
this->add_item(item);
677+
}
664678
{
665679
Input_Item item("md_tolerance");
666680
item.annotation = "tolerance for velocity rescaling (K)";

source/source_md/test/verlet_test.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,26 @@ TEST_F(Verlet_test, rescale_v)
281281
EXPECT_NEAR(mdrun->vel[3].z, -2.8328663233253657e-05, doublethreshold);
282282
}
283283

284+
TEST_F(Verlet_test, CSVR)
285+
{
286+
mdrun->first_half(GlobalV::ofs_running);
287+
param_in.input.mdp.md_type = "nvt";
288+
param_in.input.mdp.md_thermostat = "csvr";
289+
param_in.input.mdp.md_csvr_tau = 100.0;
290+
param_in.input.mdp.md_seed = 12345;
291+
mdrun->second_half();
292+
293+
// Check that positions are updated correctly
294+
EXPECT_NEAR(mdrun->pos[0].x, -0.00054545529007222658, doublethreshold);
295+
EXPECT_NEAR(mdrun->pos[0].y, 0.00029590658162135359, doublethreshold);
296+
EXPECT_NEAR(mdrun->pos[0].z, -5.7952328034033513e-05, doublethreshold);
297+
298+
// Check that temperature is in reasonable range
299+
double temp = mdrun->t_current * ModuleBase::Hartree_to_K;
300+
EXPECT_GT(temp, 0.0);
301+
EXPECT_LT(temp, 1000.0);
302+
}
303+
284304
TEST_F(Verlet_test, write_restart)
285305
{
286306
mdrun->step_ = 1;

source/source_md/verlet.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ void Verlet::apply_thermostat(void)
100100
t_target = MD_func::target_temp(step_ + step_rst_, mdp.md_nstep, md_tfirst, md_tlast);
101101
thermalize(mdp.md_nraise, t_current, t_target);
102102
}
103+
else if (mdp.md_thermostat == "csvr")
104+
{
105+
t_target = MD_func::target_temp(step_ + step_rst_, mdp.md_nstep, md_tfirst, md_tlast);
106+
apply_csvr(t_current, t_target);
107+
}
103108
else
104109
{
105110
ModuleBase::WARNING_QUIT("Verlet", "No such thermostat!");
@@ -126,6 +131,62 @@ void Verlet::thermalize(const int& nraise, const double& current_temp, const dou
126131
}
127132

128133

134+
void Verlet::apply_csvr(const double& current_temp, const double& target_temp)
135+
{
136+
// CSVR thermostat: Canonical Sampling through Velocity Rescaling
137+
// Reference: G. Bussi, D. Donadio, M. Parrinello, J. Chem. Phys. 126, 014101 (2007)
138+
139+
if (current_temp <= 0.0 || target_temp <= 0.0)
140+
{
141+
return;
142+
}
143+
144+
// Get degrees of freedom (3N - frozen)
145+
int ndeg = 3 * ucell.nat - frozen_freedom_;
146+
147+
// Calculate kinetic energies
148+
double kin_energy = current_temp * ndeg * 0.5; // in Hartree
149+
double kin_target = target_temp * ndeg * 0.5; // in Hartree
150+
151+
// Calculate tau parameter (characteristic time scale / dt)
152+
double taut = mdp.md_csvr_tau / mdp.md_dt;
153+
154+
// Calculate decay factor
155+
double factor = 0.0;
156+
if (taut > 0.1)
157+
{
158+
factor = exp(-1.0 / taut);
159+
}
160+
161+
// Generate Gaussian random numbers using MD_func
162+
double rr = MD_func::gaussrand();
163+
164+
// Calculate sum of squared Gaussian random numbers (ndeg - 1)
165+
double sumnoises = 0.0;
166+
for (int i = 0; i < ndeg - 1; ++i)
167+
{
168+
double r = MD_func::gaussrand();
169+
sumnoises += r * r;
170+
}
171+
172+
// CSVR core formula (simplified)
173+
double factor2 = (1.0 - factor) * kin_target / kin_energy / ndeg;
174+
double resample = factor + factor2 * (rr * rr + sumnoises) + 2.0 * rr * sqrt(factor * factor2);
175+
176+
// Ensure non-negative
177+
resample = std::max(0.0, resample);
178+
179+
// Calculate scaling factor
180+
double scale = sqrt(resample);
181+
182+
// Apply velocity scaling
183+
for (int i = 0; i < ucell.nat; ++i)
184+
{
185+
vel[i] *= scale;
186+
}
187+
}
188+
189+
129190
void Verlet::print_md(std::ofstream& ofs, const bool& cal_stress)
130191
{
131192
MD_base::print_md(ofs, cal_stress);

source/source_md/verlet.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ class Verlet : public MD_base
3535
* @param target_temp the target temperature
3636
*/
3737
void thermalize(const int& nraise, const double& current_temp, const double& target_temp);
38+
39+
/**
40+
* @brief apply CSVR thermostat
41+
*
42+
* @param current_temp the current temperature
43+
* @param target_temp the target temperature
44+
*/
45+
void apply_csvr(const double& current_temp, const double& target_temp);
3846
};
3947

4048
#endif

0 commit comments

Comments
 (0)