40 {
41 if (!initial) {
42 throw std::runtime_error("ReplicaExchangeJob::runFromMatter: null Matter");
43 }
46
47 long samplingSteps =
48 static_cast<long>(
params.replica_exchange_options.sampling_time /
49 params.dynamics_options.time_step +
50 0.5);
51 long exchangePeriodSteps =
52 static_cast<long>(
params.replica_exchange_options.exchange_period /
53 params.dynamics_options.time_step +
54 0.5);
55 const double kB =
params.constants.kB;
56 if (samplingSteps <= 0)
57 samplingSteps = 1;
58 if (exchangePeriodSteps <= 0)
59 exchangePeriodSteps = 1;
60
61 QUILL_LOG_DEBUG(
log,
"Running Replica Exchange");
62
64
65 const long nReplicas =
params.replica_exchange_options.replicas;
66 std::vector<std::shared_ptr<Matter>> replica(nReplicas);
67 std::vector<std::unique_ptr<Dynamics>> replicaDynamics(nReplicas);
68
69
70
71 const bool perImage =
pot->needsPerImageInstance();
72 for (long i = 0; i < nReplicas; i++) {
74 replica[i] = std::make_shared<Matter>(replicaPot,
params);
76 replicaDynamics[i] = std::make_unique<Dynamics>(replica[i].
get(),
params);
77 }
78
79 std::vector<double> replicaTemperature(nReplicas);
80
81 QUILL_LOG_DEBUG(
log,
"Temperature distribution:");
82 if (
params.replica_exchange_options.temperature_distribution ==
"linear") {
83 for (long i = 0; i < nReplicas; i++) {
84 replicaTemperature[i] =
85 params.replica_exchange_options.temperature_low +
86 static_cast<double>(i) / static_cast<double>(nReplicas - 1) *
87 (
params.replica_exchange_options.temperature_high -
88 params.replica_exchange_options.temperature_low);
89 replicaDynamics[i]->setTemperature(replicaTemperature[i]);
90 }
91 }
else if (
params.replica_exchange_options.temperature_distribution ==
92 "exponential") {
93 double kTemp = std::log(
params.replica_exchange_options.temperature_high /
94 params.replica_exchange_options.temperature_low) /
95 static_cast<double>(nReplicas - 1);
96 for (long i = 0; i < nReplicas; i++) {
97 replicaTemperature[i] =
params.replica_exchange_options.temperature_low *
98 std::exp(kTemp * static_cast<double>(i));
99 replicaDynamics[i]->setTemperature(replicaTemperature[i]);
100 QUILL_LOG_DEBUG(
log,
"replica: {} temperature {:.0f}", i + 1,
101 replicaTemperature[i]);
102 }
103 }
104
105 QUILL_LOG_DEBUG(
106 log,
"Replica Exchange sampling for {:.0f} fs; {} steps; {} replicas.",
107 params.replica_exchange_options.sampling_time * 10.18, samplingSteps,
108 params.replica_exchange_options.replicas);
109
110
111 const bool canParallel =
params.main_options.parallel &&
112 (
pot->isSharedInstanceThreadSafe() || perImage);
113
114 for (long step = 1; step <= samplingSteps; step++) {
115 if (canParallel && nReplicas > 1) {
116
117 std::vector<std::thread> threads;
118 threads.reserve(nReplicas);
119 for (long i = 0; i < nReplicas; i++) {
120 threads.emplace_back([&, i] { replicaDynamics[i]->oneStep(); });
121 }
122 for (auto &t : threads) {
123 t.join();
124 }
125 } else {
126 for (long i = 0; i < nReplicas; i++) {
127 replicaDynamics[i]->oneStep();
128 }
129 }
130
131
132 if ((step % exchangePeriodSteps) == 0) {
133 for (long trial = 0;
134 trial <
params.replica_exchange_options.exchange_trials; trial++) {
136 double energyLow = replica[i]->getPotentialEnergy();
137 double energyHigh = replica[i + 1]->getPotentialEnergy();
138 double kbTLow = kB * replicaTemperature[i];
139 double kbTHigh = kB * replicaTemperature[i + 1];
140 double pAcc =
141 std::min(1.0, std::exp((energyHigh - energyLow) *
146 "step: {} trial swap, i {}, elow: {:.5f}, ehigh: "
147 "{:.5f}, pAcc: {:.5f}, rand: {}",
148 step, i, energyLow, energyHigh, pAcc, rnd);
149 if (rnd < pAcc) {
150 QUILL_LOG_INFO(
log,
"swap");
151 std::swap(replica[i], replica[i + 1]);
152 replicaDynamics[i]->setThermalVelocity();
153 replicaDynamics[i + 1]->setThermalVelocity();
154 } else {
155 QUILL_LOG_INFO(
log,
"no swap");
156 }
157 }
158 }
159 }
160
163 if (!replica.empty()) {
165 }
167}
static PotRegistry & get() noexcept
Process-lifetime singleton.
size_t total_force_calls() const noexcept
long randomInt(int lower, int upper)
std::shared_ptr< Potential > makePotential(const Parameters ¶ms)
quill::Logger * get() noexcept
Get or create the default "combi" logger.
constexpr double safe_recip(double x, double fallback=0.0)