46 throw std::runtime_error(
"ReplicaExchangeJob::runFromMatter: null Matter");
52 if (rex.replicas < 1) {
53 throw std::invalid_argument(
54 "ReplicaExchangeJob: replica_exchange.replicas must be >= 1");
56 if (rex.temperature_low <= 0.0) {
57 rex.temperature_low =
params.main_options().temperature > 0.0
58 ?
params.main_options().temperature
61 if (rex.temperature_high <= rex.temperature_low) {
62 rex.temperature_high = rex.temperature_low * 1.5;
66 static_cast<long>(
params.replica_exchange_options().sampling_time /
67 params.dynamics_options().time_step +
69 long exchangePeriodSteps =
70 static_cast<long>(
params.replica_exchange_options().exchange_period /
71 params.dynamics_options().time_step +
73 const double kB =
params.constants().kB;
74 if (samplingSteps <= 0)
76 if (exchangePeriodSteps <= 0)
77 exchangePeriodSteps = 1;
79 QUILL_LOG_DEBUG(
log,
"Running Replica Exchange");
83 const long nReplicas =
params.replica_exchange_options().replicas;
84 std::vector<std::shared_ptr<Matter>> replica(nReplicas);
85 std::vector<std::unique_ptr<Dynamics>> replicaDynamics(nReplicas);
89 const bool perImage =
pot->needsPerImageInstance();
90 for (
long i = 0; i < nReplicas; i++) {
92 replica[i] = std::make_shared<Matter>(replicaPot,
params);
94 replica[i]->setPotential(replicaPot);
95 replicaDynamics[i] = std::make_unique<Dynamics>(replica[i].get(),
params);
98 std::vector<double> replicaTemperature(nReplicas);
100 QUILL_LOG_DEBUG(
log,
"Temperature distribution:");
102 replicaTemperature[0] =
params.replica_exchange_options().temperature_low;
103 replicaDynamics[0]->setTemperature(replicaTemperature[0]);
104 replicaDynamics[0]->setThermalVelocity();
105 }
else if (
params.replica_exchange_options().temperature_distribution ==
107 for (
long i = 0; i < nReplicas; i++) {
108 replicaTemperature[i] =
109 params.replica_exchange_options().temperature_low +
110 static_cast<double>(i) /
static_cast<double>(nReplicas - 1) *
111 (
params.replica_exchange_options().temperature_high -
112 params.replica_exchange_options().temperature_low);
113 replicaDynamics[i]->setTemperature(replicaTemperature[i]);
114 replicaDynamics[i]->setThermalVelocity();
116 }
else if (
params.replica_exchange_options().temperature_distribution ==
118 double kTemp = std::log(
params.replica_exchange_options().temperature_high /
119 params.replica_exchange_options().temperature_low) /
120 static_cast<double>(nReplicas - 1);
121 for (
long i = 0; i < nReplicas; i++) {
122 replicaTemperature[i] =
123 params.replica_exchange_options().temperature_low *
124 std::exp(kTemp *
static_cast<double>(i));
125 replicaDynamics[i]->setTemperature(replicaTemperature[i]);
126 replicaDynamics[i]->setThermalVelocity();
127 QUILL_LOG_DEBUG(
log,
"replica: {} temperature {:.0f}", i + 1,
128 replicaTemperature[i]);
131 throw std::invalid_argument(
132 "replica_exchange.temperature_distribution must be linear or "
137 log,
"Replica Exchange sampling for {:.0f} fs; {} steps; {} replicas.",
138 params.replica_exchange_options().sampling_time * 10.18, samplingSteps,
139 params.replica_exchange_options().replicas);
142 const bool canParallel =
params.main_options().parallel &&
145 for (
long step = 1; step <= samplingSteps; step++) {
146 if (canParallel && nReplicas > 1) {
148 std::vector<std::thread> threads;
149 threads.reserve(nReplicas);
150 for (
long i = 0; i < nReplicas; i++) {
151 threads.emplace_back([&, i] {
154 const long userSeed =
params.main_options().randomSeed;
155 const long base = (userSeed > 0) ? userSeed : 1;
157 replicaDynamics[i]->oneStep();
160 for (
auto &t : threads) {
164 for (
long i = 0; i < nReplicas; i++) {
165 replicaDynamics[i]->oneStep();
170 if (nReplicas >= 2 && (step % exchangePeriodSteps) == 0) {
172 trial <
params.replica_exchange_options().exchange_trials; trial++) {
174 double energyLow = replica[i]->getPotentialEnergy();
175 double energyHigh = replica[i + 1]->getPotentialEnergy();
176 double kbTLow = kB * replicaTemperature[i];
177 double kbTHigh = kB * replicaTemperature[i + 1];
179 std::min(1.0, std::exp((energyHigh - energyLow) *
184 "step: {} trial swap, i {}, elow: {:.5f}, ehigh: "
185 "{:.5f}, pAcc: {:.5f}, rand: {}",
186 step, i, energyLow, energyHigh, pAcc, rnd);
188 QUILL_LOG_INFO(
log,
"swap");
190 auto potLow = replica[i]->getPotential();
191 auto potHigh = replica[i + 1]->getPotential();
193 *replica[i] = *replica[i + 1];
194 *replica[i + 1] = tmp;
195 replica[i]->setPotential(potLow);
196 replica[i + 1]->setPotential(potHigh);
197 replicaDynamics[i]->setThermalVelocity();
198 replicaDynamics[i + 1]->setThermalVelocity();
200 QUILL_LOG_INFO(
log,
"no swap");
207 if (!replica.empty()) {