Skip to content

Commit c297ba5

Browse files
authored
Merge pull request #328 from HaoZeke/fixPotCallLogger
feat(potcalls): rework with registries
2 parents d50148a + 4ede3f3 commit c297ba5

20 files changed

+572
-110
lines changed

client/BasinHoppingJob.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ std::vector<std::string> BasinHoppingJob::run(void) {
291291
params.basin_hopping_options.quenching_steps);
292292
fprintf(fileResults, "%d total_jump_steps\n", jump_count);
293293
fprintf(fileResults, "%d total_swap_steps\n", swap_count);
294-
// fprintf(fileResults, "%d total_force_calls\n", Potential::fcallsTotal);
294+
fprintf(fileResults, "%zu total_force_calls\n",
295+
PotRegistry::get().total_force_calls());
295296
fclose(fileResults);
296297

297298
std::string productFilename("min.con");

client/ClientEON.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "HelperFunctions.h"
2323
#include "Job.h"
2424
#include "Parameters.h"
25+
#include "PotRegistry.h"
2526
#include "Potential.h"
2627
#include "version.h"
2728

@@ -444,6 +445,7 @@ int main(int argc, char **argv) {
444445
return EXIT_FAILURE;
445446
}
446447

448+
PotRegistry::get().write_summary();
447449
filenames.push_back(std::string("client.log"));
448450

449451
// Finalize Timing Information

client/HessianJob.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ std::vector<std::string> HessianJob::run(void) {
5050

5151
fileResults = fopen(results_file.c_str(), "wb");
5252

53-
// fprintf(fileResults, "%d force_calls\n", Potential::fcalls);
53+
fprintf(fileResults, "%zu force_calls\n",
54+
PotRegistry::get().total_force_calls());
5455
fclose(fileResults);
5556

5657
return returnFiles;

client/MonteCarloJob.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,17 @@ std::vector<std::string> MonteCarloJob::run(void) {
4141
mc.run(params.monte_carlo_options.steps, params.main_options.temperature,
4242
params.monte_carlo_options.step_size);
4343

44-
// FILE *fileResults;
45-
4644
std::string resultsFilename("results.dat");
4745
returnFiles.push_back(resultsFilename);
48-
// fileResults = fopen(resultsFilename.c_str(), "wb");
49-
50-
// fprintf(fileResults, "%d termination_reason\n", status);
51-
// fprintf(fileResults, "minimization job_type\n");
52-
// fprintf(fileResults, "%s potential_type\n",
53-
// eonc::helpers::getPotentialName(params.potential_options.potential).c_str());
54-
// fprintf(fileResults, "%d total_force_calls\n", Potential::fcallsTotal);
55-
// if (status != STATUS_POTENTIAL_FAILED) {
56-
// fprintf(fileResults, "%f potential_energy\n",
57-
// pos->getPotentialEnergy());
58-
// }
59-
// fclose(fileResults);
46+
FILE *fileResults = fopen(resultsFilename.c_str(), "wb");
47+
fprintf(fileResults, "%s potential_type\n",
48+
std::string{magic_enum::enum_name<PotType>(
49+
params.potential_options.potential)}
50+
.c_str());
51+
fprintf(fileResults, "%zu total_force_calls\n",
52+
PotRegistry::get().total_force_calls());
53+
fprintf(fileResults, "%f potential_energy\n", matter->getPotentialEnergy());
54+
fclose(fileResults);
6055

6156
return returnFiles;
6257
}

client/NudgedElasticBandJob.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ using namespace std;
1818

1919
std::vector<std::string> NudgedElasticBandJob::run(void) {
2020
NudgedElasticBand::NEBStatus status;
21-
int f1;
21+
size_t f1;
2222

2323
string reactantFilename = eonc::helpers::getRelevantFile("reactant.con");
2424
string productFilename = eonc::helpers::getRelevantFile("product.con");
@@ -118,9 +118,9 @@ std::vector<std::string> NudgedElasticBandJob::run(void) {
118118
}
119119
}
120120

121-
// f1 = Potential::fcalls;
121+
f1 = PotRegistry::get().total_force_calls();
122122
status = neb->compute();
123-
// fCallsNEB += Potential::fcalls - f1;
123+
fCallsNEB += PotRegistry::get().total_force_calls() - f1;
124124

125125
if (status == NudgedElasticBand::NEBStatus::GOOD) {
126126
neb->printImageData();
@@ -150,8 +150,9 @@ void NudgedElasticBandJob::saveData(NudgedElasticBand::NEBStatus status,
150150
std::string{magic_enum::enum_name<PotType>(
151151
params.potential_options.potential)}
152152
.c_str());
153-
// fprintf(fileResults, "%ld total_force_calls\n", Potential::fcalls);
154-
// fprintf(fileResults, "%ld force_calls_neb\n", fCallsNEB);
153+
fprintf(fileResults, "%zu total_force_calls\n",
154+
PotRegistry::get().total_force_calls());
155+
fprintf(fileResults, "%zu force_calls_neb\n", fCallsNEB);
155156
fprintf(fileResults, "%f energy_reference\n",
156157
neb->path[0]->getPotentialEnergy());
157158
fprintf(fileResults, "%li number_of_images\n", neb->numImages);

client/ParallelReplicaJob.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,11 @@ std::vector<std::string> ParallelReplicaJob::run(void) {
137137
// perform the binary search for the transition structure
138138
if (params.parallel_replica_options.refine_transition) {
139139
QUILL_LOG_DEBUG(log, "[ParallelReplica] Refining transition time");
140-
// int tmpFcalls = Potential::fcalls;
140+
int tmpFcalls = PotRegistry::get().total_force_calls();
141141
int snapshotIndex = refineTransition(MDSnapshots);
142142

143-
// refineForceCalls += Potential::fcalls - tmpFcalls;
143+
refineForceCalls +=
144+
PotRegistry::get().total_force_calls() - tmpFcalls;
144145

145146
transitionTime = MDTimes[snapshotIndex];
146147
transitionStructure = *MDSnapshots[snapshotIndex];
@@ -167,9 +168,10 @@ std::vector<std::string> ParallelReplicaJob::run(void) {
167168
"[ParallelReplica] Simulation ended without seeing a transition");
168169
QUILL_LOG_DEBUG(
169170
log, "[ParallelReplica] Refining anyways to prevent bias...");
170-
int tmpFcalls = Potential::fcalls;
171+
int tmpFcalls = PotRegistry::get().total_force_calls();
171172
refineTransition(MDSnapshots, true);
172-
// refineForceCalls += Potential::fcalls - tmpFcalls;
173+
refineForceCalls +=
174+
PotRegistry::get().total_force_calls() - tmpFcalls;
173175
}
174176
transitionStructure = *trajectory;
175177
}
@@ -211,8 +213,9 @@ std::vector<std::string> ParallelReplicaJob::run(void) {
211213
fprintf(fileResults, "%ld random_seed\n", params.main_options.randomSeed);
212214
fprintf(fileResults, "%f potential_energy_reactant\n",
213215
reactant->getPotentialEnergy());
214-
// fprintf(fileResults, "%i force_calls_refine\n", refineForceCalls);
215-
// fprintf(fileResults, "%d total_force_calls\n", Potential::fcalls);
216+
fprintf(fileResults, "%i force_calls_refine\n", refineForceCalls);
217+
fprintf(fileResults, "%zu total_force_calls\n",
218+
PotRegistry::get().total_force_calls());
216219

217220
if (transitionTime == 0) {
218221
fprintf(fileResults, "0 transition_found\n");

client/PotRegistry.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
** This file is part of eOn.
3+
**
4+
** SPDX-License-Identifier: BSD-3-Clause
5+
**
6+
** Copyright (c) 2010--present, eOn Development Team
7+
** All rights reserved.
8+
**
9+
** Repo:
10+
** https://github.com/TheochemUI/eOn
11+
*/
12+
#include "PotRegistry.h"
13+
#include <fstream>
14+
#include <iomanip>
15+
#include <sstream>
16+
17+
namespace eonc {
18+
19+
namespace {
20+
std::string format_timepoint(PotRegistry::TimePoint tp) {
21+
auto time_t_val = PotRegistry::Clock::to_time_t(tp);
22+
auto us = std::chrono::duration_cast<std::chrono::microseconds>(
23+
tp.time_since_epoch()) %
24+
std::chrono::seconds{1};
25+
std::tm tm_buf{};
26+
#ifdef _WIN32
27+
localtime_s(&tm_buf, &time_t_val);
28+
#else
29+
localtime_r(&time_t_val, &tm_buf);
30+
#endif
31+
std::ostringstream oss;
32+
oss << std::put_time(&tm_buf, "%Y-%m-%dT%H:%M:%S");
33+
oss << '.' << std::setfill('0') << std::setw(6) << us.count();
34+
return oss.str();
35+
}
36+
37+
// Escape a string for JSON (handles quotes and backslashes)
38+
std::string json_escape(std::string_view sv) {
39+
std::string out;
40+
out.reserve(sv.size());
41+
for (char c : sv) {
42+
if (c == '"' || c == '\\')
43+
out += '\\';
44+
out += c;
45+
}
46+
return out;
47+
}
48+
} // namespace
49+
50+
PotRegistry &PotRegistry::get() noexcept {
51+
static PotRegistry instance;
52+
return instance;
53+
}
54+
55+
void PotRegistry::reset() {
56+
for (auto &ts : m_type_stats) {
57+
ts.force_calls.store(0, std::memory_order_relaxed);
58+
ts.created.store(0, std::memory_order_relaxed);
59+
ts.alive.store(0, std::memory_order_relaxed);
60+
}
61+
std::lock_guard<std::mutex> lock(m_records_mutex);
62+
m_records.clear();
63+
m_next_id.store(1, std::memory_order_relaxed);
64+
}
65+
66+
uint64_t PotRegistry::on_created(PotType t) noexcept {
67+
auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
68+
m_type_stats[idx].created.fetch_add(1, std::memory_order_relaxed);
69+
m_type_stats[idx].alive.fetch_add(1, std::memory_order_relaxed);
70+
return m_next_id.fetch_add(1, std::memory_order_relaxed);
71+
}
72+
73+
void PotRegistry::on_destroyed(uint64_t id, PotType t, size_t force_calls,
74+
TimePoint created_at) {
75+
auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
76+
m_type_stats[idx].alive.fetch_sub(1, std::memory_order_relaxed);
77+
78+
InstanceRecord rec{id, t, created_at, Clock::now(), force_calls};
79+
std::lock_guard<std::mutex> lock(m_records_mutex);
80+
m_records.push_back(rec);
81+
}
82+
83+
void PotRegistry::on_force_call(PotType t) noexcept {
84+
auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
85+
m_type_stats[idx].force_calls.fetch_add(1, std::memory_order_relaxed);
86+
}
87+
88+
size_t PotRegistry::type_force_calls(PotType t) const noexcept {
89+
auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
90+
return m_type_stats[idx].force_calls.load(std::memory_order_relaxed);
91+
}
92+
93+
size_t PotRegistry::total_force_calls() const noexcept {
94+
size_t total = 0;
95+
for (const auto &ts : m_type_stats) {
96+
total += ts.force_calls.load(std::memory_order_relaxed);
97+
}
98+
return total;
99+
}
100+
101+
size_t PotRegistry::type_alive(PotType t) const noexcept {
102+
auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
103+
return m_type_stats[idx].alive.load(std::memory_order_relaxed);
104+
}
105+
106+
void PotRegistry::write_summary(const std::string &path) const {
107+
std::vector<InstanceRecord> snapshot;
108+
{
109+
std::lock_guard<std::mutex> lock(const_cast<std::mutex &>(m_records_mutex));
110+
snapshot = m_records;
111+
}
112+
113+
std::ofstream ofs(path);
114+
if (!ofs.is_open())
115+
return;
116+
117+
ofs << "[\n";
118+
for (size_t i = 0; i < snapshot.size(); ++i) {
119+
const auto &r = snapshot[i];
120+
auto type_name = magic_enum::enum_name(r.type);
121+
ofs << " {\n";
122+
ofs << " \"id\": " << r.id << ",\n";
123+
ofs << " \"type\": \"" << json_escape(type_name) << "\",\n";
124+
ofs << " \"created_at\": \"" << format_timepoint(r.created_at)
125+
<< "\",\n";
126+
ofs << " \"destroyed_at\": \"" << format_timepoint(r.destroyed_at)
127+
<< "\",\n";
128+
ofs << " \"force_calls\": " << r.force_calls << "\n";
129+
ofs << " }";
130+
if (i + 1 < snapshot.size())
131+
ofs << ',';
132+
ofs << '\n';
133+
}
134+
ofs << "]\n";
135+
}
136+
137+
} // namespace eonc

client/PotRegistry.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
** This file is part of eOn.
3+
**
4+
** SPDX-License-Identifier: BSD-3-Clause
5+
**
6+
** Copyright (c) 2010--present, eOn Development Team
7+
** All rights reserved.
8+
**
9+
** Repo:
10+
** https://github.com/TheochemUI/eOn
11+
*/
12+
#pragma once
13+
14+
#include "BaseStructures.h"
15+
#include <array>
16+
#include <atomic>
17+
#include <chrono>
18+
#include <mutex>
19+
#include <string>
20+
#include <vector>
21+
22+
namespace eonc {
23+
24+
class PotRegistry {
25+
public:
26+
using Clock = std::chrono::system_clock;
27+
using TimePoint = Clock::time_point;
28+
29+
struct InstanceRecord {
30+
uint64_t id;
31+
PotType type;
32+
TimePoint created_at;
33+
TimePoint destroyed_at;
34+
size_t force_calls;
35+
};
36+
37+
private:
38+
struct TypeStats {
39+
std::atomic<size_t> force_calls{0};
40+
std::atomic<size_t> created{0};
41+
std::atomic<size_t> alive{0};
42+
};
43+
std::array<TypeStats, magic_enum::enum_count<PotType>()> m_type_stats{};
44+
45+
std::mutex m_records_mutex;
46+
std::vector<InstanceRecord> m_records;
47+
48+
std::atomic<uint64_t> m_next_id{1};
49+
50+
public:
51+
static PotRegistry &get() noexcept;
52+
void reset();
53+
54+
// Lifecycle events
55+
[[nodiscard]] uint64_t on_created(PotType t) noexcept;
56+
void on_destroyed(uint64_t id, PotType t, size_t force_calls,
57+
TimePoint created_at);
58+
void on_force_call(PotType t) noexcept;
59+
60+
// Per-type queries
61+
[[nodiscard]] size_t type_force_calls(PotType t) const noexcept;
62+
[[nodiscard]] size_t total_force_calls() const noexcept;
63+
[[nodiscard]] size_t type_alive(PotType t) const noexcept;
64+
65+
// JSON output
66+
void write_summary(const std::string &path = "_potcalls.json") const;
67+
};
68+
69+
} // namespace eonc
70+
71+
using eonc::PotRegistry;

client/Potential.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,6 @@
119119
#include <limits>
120120
using namespace std;
121121

122-
// TODO(rg): These aren't really used anymore, just there for eyecandy
123-
int Potential::fcalls = 0;
124-
int Potential::fcallsTotal = 0;
125-
int Potential::wu_fcallsTotal = 0;
126-
double Potential::totalUserTime = 0.0;
127-
128122
std::tuple<double, AtomMatrix> Potential::get_ef(const AtomMatrix &pos,
129123
const VectorXi &atmnrs,
130124
const Matrix3d &box) {
@@ -135,12 +129,10 @@ std::tuple<double, AtomMatrix> Potential::get_ef(const AtomMatrix &pos,
135129
this->force(nAtoms, pos.data(), atmnrs.data(), forces.data(), &energy, &var,
136130
box.data());
137131
forceCallCounter++;
138-
QUILL_LOG_TRACE_L3(m_log, "[{}] {} so far",
139-
magic_enum::enum_name<PotType>(getType()),
140-
forceCallCounter);
132+
PotRegistry::get().on_force_call(ptype);
141133

142134
return std::make_tuple(energy, forces);
143-
};
135+
}
144136

145137
namespace eonc::helpers {
146138
std::shared_ptr<Potential> makePotential(const Parameters &params) {

0 commit comments

Comments
 (0)