Skip to content

Commit 7f1a0f5

Browse files
authored
Save minimum energy configuration from systemenergy analysis (#440)
* Add `save_min_conf` to `systemenergy` analysis This enables saving of the minimum observed energy configuration. - [x] docs - [x] json schema * fix decimal * Fix clang17 compilation (constexpr name)
1 parent 7bb33d4 commit 7f1a0f5

File tree

4 files changed

+41
-3
lines changed

4 files changed

+41
-3
lines changed

docs/_docs/analysis.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,8 @@ All units in $k\_BT$.
644644
-------------- | -------------------------------------------
645645
`file` | Output filename (`.dat`, `.csv`, `.dat.gz`)
646646
`nstep` | Interval between samples
647+
`nskip=0` | Number of initial steps excluded from the analysis
648+
`save_min_conf=false` | Dump minimum energy configuration to `PQR` file
647649

648650

649651
### Penalty function

docs/schema.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,10 @@ properties:
12061206
file: {type: string}
12071207
nstep: {type: integer}
12081208
nskip: {type: integer, default: 0, description: Initial steps to skip}
1209+
save_min_conf:
1210+
type: boolean
1211+
default: false
1212+
description: "Save minimum energy configuration to PQR file"
12091213
required: [file, nstep]
12101214
additionalProperties: false
12111215
type: object

src/analysis.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "aux/eigensupport.h"
1313
#include "aux/arange.h"
1414
#include "aux/matrixmarket.h"
15+
#include <cmath>
1516
#include <exception>
1617
#include <iterator>
1718
#include <range/v3/numeric/accumulate.hpp>
@@ -241,12 +242,33 @@ void SystemEnergy::normalize() {
241242
}
242243
}
243244

245+
/**
246+
* @brief Checks if the current energy is the lowest so far and saves the configuration if so.
247+
*
248+
* The output is hardcoded to PQR format, tagged with the step number and energy.
249+
*
250+
* @return True if a new minimum energy was encountered
251+
*/
252+
bool SystemEnergy::updateMinimumEnergy(const double current_energy) {
253+
if (!dump_minimum_energy_configuration || current_energy >= minimum_energy) {
254+
return false;
255+
}
256+
minimum_energy = current_energy;
257+
const auto filename = MPI::prefix + "mininum_energy.pqr";
258+
faunus_logger->debug("{}: saving {} ({:.2f} kT) at step {}", name, filename, minimum_energy, getNumberOfSteps());
259+
PQRWriter(PQRWriter::Style::PQR).save(filename, spc.groups, spc.geometry.getLength());
260+
return true;
261+
}
262+
244263
void SystemEnergy::_sample() {
245264
const auto energies = calculateEnergies(); // current energy from all terms in Hamiltonian
246265
const auto total_energy = ranges::accumulate(energies, 0.0);
266+
updateMinimumEnergy(total_energy);
247267
if (std::isfinite(total_energy)) {
248268
mean_energy += total_energy;
249269
mean_squared_energy += total_energy * total_energy;
270+
} else {
271+
faunus_logger->warn("{}: non-finite energy excluded from averages at step {}", name, getNumberOfSteps());
250272
}
251273
*output_stream << fmt::format("{:10d}{}{:.6E}", getNumberOfSteps(), separator, total_energy);
252274
for (auto energy : energies) {
@@ -256,19 +278,26 @@ void SystemEnergy::_sample() {
256278
}
257279

258280
void SystemEnergy::_to_json(json& j) const {
259-
j = {{"file", file_name}, {"init", initial_energy}, {"final", calculateEnergies()}};
281+
j = {{"file", file_name},
282+
{"init", initial_energy},
283+
{"final", calculateEnergies()},
284+
{"save_min_conf", dump_minimum_energy_configuration}};
260285
if (!mean_energy.empty()) {
261286
j["mean"] = mean_energy.avg();
262287
j["Cv/kB"] = mean_squared_energy.avg() - std::pow(mean_energy.avg(), 2);
263288
}
264289
roundJSON(j, 5);
265290
}
266291

267-
void SystemEnergy::_from_json(const json& j) { file_name = MPI::prefix + j.at("file").get<std::string>(); }
292+
void SystemEnergy::_from_json(const json& j) {
293+
file_name = MPI::prefix + j.at("file").get<std::string>();
294+
dump_minimum_energy_configuration = j.value("save_min_conf", false);
295+
}
268296

269297
void SystemEnergy::createOutputStream() {
270298
output_stream = IO::openCompressedOutputStream(file_name, true);
271-
if (const auto suffix = file_name.substr(file_name.find_last_of('.') + 1); suffix == "csv") {
299+
const bool is_csv_file = file_name.substr(file_name.find_last_of('.') + 1) == "csv";
300+
if (is_csv_file) {
272301
separator = ",";
273302
} else {
274303
separator = " ";

src/analysis.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,10 @@ class SystemEnergy : public Analysis {
510510
Average<double> mean_squared_energy;
511511
Table2D<double, double> energy_histogram; // Density histograms
512512
double initial_energy = 0.0;
513+
double minimum_energy = std::numeric_limits<double>::infinity(); //!< Tracks minimum energy
514+
bool dump_minimum_energy_configuration = false; //!< Dump minimum energy config to disk
513515

516+
bool updateMinimumEnergy(double current_energy);
514517
void createOutputStream();
515518
void normalize();
516519
void _sample() override;

0 commit comments

Comments
 (0)