6#include "../Global/SharedConfig.h"
8#include "../Scheduler/Scheduler.h"
9#include "../Global/DETCheckpoint.h"
10#include "../Global/PEOutput.h"
11#include "../Global/OutputMerge.h"
16#include <unordered_set>
19#include "../Global/SimulationContext.h"
32 std::cout <<
"\n\n=== Simulation Interrupted (Ctrl+C) ===" << std::endl;
33 std::cout <<
" Sequences completed: " << seq_completed << std::endl;
34 std::cout <<
" Queue depth: " << queue_depth << std::endl;
35 std::cout <<
" Busy clients: " << busy_clients << std::endl;
36 std::cout << std::endl;
37 std::cout <<
" 1. Continue simulation" << std::endl;
38 std::cout <<
" 2. Save checkpoint and exit" << std::endl;
39 std::cout <<
" 3. Exit without saving" << std::endl;
40 std::cout << std::endl;
41 std::cout <<
"Choice [1]: " << std::flush;
44 if (!std::getline(std::cin, line) || line.empty())
return 1;
45 if (line[0] ==
'2')
return 2;
46 if (line[0] ==
'3')
return 3;
65 PU::print_message(
rank,
"Visualization server at http://localhost:" + std::to_string(
viz_port_),
PG::log);
67 std::cout <<
"DET visualization: http://localhost:" <<
viz_port_ << std::endl;
71 PU::print_message(
rank,
"Server initiated..",
PG::log);
78 PU::print_message(0,
"Sending shutdown signal to clients..",
PG::log);
79 for (
int who = 1; who <
PG::size; who++)
81 PU::print_message(0,
"Shutdown signal sent to all clients..",
PG::log);
88 PU::print_message(
rank,
"Started the simulation",
PG::log);
118 occupancy_log_ <<
"elapsed_s,queue_depth,busy_workers,total_workers,sequences_completed\n";
120 PU::print_message(
rank,
"WARNING: cannot open occupancy log "
127 auto update_viz_stats = [
this]() {
128 const int queue =
static_cast<int>(
tasks_queue.size());
140 std::lock_guard<std::mutex> lock(
viz_state_->mu);
155 PU::print_message(
rank,
"Sending the tag for initial simulation to Client " + std::to_string(free_rank),
PG::log);
161 bool user_exit =
false;
176 PU::print_message(
rank,
"Checkpoint+exit requested, waiting for "
177 + std::to_string(busy) +
" clients to finish",
PG::log);
178 }
else if (choice == 3) {
179 PU::print_message(
rank,
"Exit requested by user",
PG::log);
193 MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &has_msg, &
status_server);
203 PU::print_message(0,
"Finished the parallel simulation",
PG::log);
217 std::lock_guard<std::mutex> lock(
viz_state_->mu);
226 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
231 +
" (S" + std::to_string(report.sequence_id) +
" from C["
236 std::lock_guard<std::mutex> lock(
viz_state_->mu);
244 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
249 +
" (legacy, no report, from C[" + std::to_string(
status_server.MPI_SOURCE) +
"])"
255 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
258 PU::print_message(
rank,
"[PEER] Send complete from C[" + std::to_string(
status_server.MPI_SOURCE)
263 int local_id, num_branches;
265 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
267 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
272 int needed = num_branches - 1;
283 int queued = needed - direct;
285 PU::print_message(
rank,
"[PEER] C[" + std::to_string(source)
286 +
"] lid=" + std::to_string(local_id) +
" branches=" + std::to_string(num_branches)
287 +
" parent_seq=" + std::to_string(parent_seq)
288 +
" -> direct=" + std::to_string(direct)
289 +
" queued=" + std::to_string(queued),
PG::log);
293 for (
int i = 0; i < direct; i++) {
295 int branch_index = i + 1;
305 PU::print_message(
rank,
"[PEER] Direct: C[" + std::to_string(source)
306 +
"] -> C[" + std::to_string(free_rank) +
"] bi="
307 + std::to_string(branch_index) +
" seq=" + std::to_string(seq_id),
PG::log);
312 for (
int bi = direct + 1; bi < num_branches; ++bi) {
315 task.local_id = local_id;
317 task.branch_index = bi;
318 task.parent_sequence_id = parent_seq;
341 PU::print_message(
rank,
"[CHECKPOINT] Pending — waiting for "
351 PU::print_message(
rank,
"[CHECKPOINT] --checkpoint-stop: exiting after "
369 PU::print_message(0,
"Finished the parallel simulation",
PG::log);
378 for (
int i = 0; i <
PG::size - 1; ++i) {
393 for (
int i = 0; i <
PG::size - 1; ++i) {
395 if (
clients_busy[candidate] == 0 && candidate != exclude_rank) {
412 for (
size_t i = 0; i < initial_size && num_free_clients > 0; i++) {
422 if (receiver != -1) {
434 PU::print_message(
rank,
"[PEER] Deferred: C[" + std::to_string(task.
source_rank)
435 +
"] -> C[" + std::to_string(receiver) +
"] lid=" + std::to_string(task.
local_id)
449 PU::print_message(
rank,
"[PEER] Self-run: C[" + std::to_string(task.
source_rank)
450 +
"] lid=" + std::to_string(task.
local_id)
455 PU::print_message(
rank,
"[MEM] RSS=" + std::to_string(
get_rss_mb())
456 +
"MB, queue=" + std::to_string(
tasks_queue.size())
462 const double t_merge = MPI_Wtime();
471 fs::create_directories(dest_es);
472 fs::create_directories(dest_pe);
473 fs::create_directories(dest_kps);
484 for (
int r = 1; r <
PG::size; ++r) {
485 std::string rank_out =
"../rank_" + std::to_string(r) +
"/Output/";
486 if (!fs::exists(rank_out))
continue;
492 static const std::vector<std::string> append_files = {
493 "Scenario info.txt",
"Procedure data.txt",
"Scan data.txt",
496 std::unordered_set<std::string> made_dirs;
497 for (
auto& entry : fs::recursive_directory_iterator(rank_out)) {
498 if (!entry.is_regular_file())
continue;
499 fs::path rel = fs::relative(entry.path(), rank_out);
501 if (made_dirs.insert(dest.parent_path().string()).second)
502 fs::create_directories(dest.parent_path());
504 std::string fname = entry.path().filename().string();
505 bool should_append =
false;
506 for (
auto& af : append_files)
507 if (fname == af) { should_append =
true;
break; }
510 std::ifstream in(entry.path());
511 std::ofstream out(dest, std::ios::app);
514 fs::copy(entry.path(), dest, fs::copy_options::overwrite_existing);
521 fs::rename(entry.path(), dest, mec);
525 fs::copy(entry.path(), dest, fs::copy_options::overwrite_existing, mec);
527 PU::print_message(
rank,
"[MERGE] failed to move " + entry.path().string()
528 +
": " + mec.message(),
PG::log);
530 fs::remove(entry.path(), mec);
543 const std::vector<std::pair<fs::path, fs::path>> part_sets = {
544 {fs::path(dest_es) /
"parts", fs::path(dest_es)},
545 {fs::path(dest_pe) /
"parts", fs::path(dest_pe)}};
546 for (
auto& [parts_dir, out_dir] : part_sets) {
548 if (!fs::exists(parts_dir, pec))
continue;
549 std::set<std::string> bases;
550 for (
auto&
e : fs::directory_iterator(parts_dir, pec)) {
551 const std::string f =
e.path().filename().string();
552 const auto pos = f.rfind(
".rank_");
553 if (pos != std::string::npos) bases.insert(f.substr(0, pos));
555 for (
const auto& base : bases) {
557 std::ofstream out(out_dir / base,
558 std::ios::binary | (is_agg ? std::ios::app : std::ios::trunc));
559 for (
int r = 1; r <
PG::size; ++r) {
560 std::ifstream in(parts_dir / (base +
".rank_" + std::to_string(r)),
562 if (in) out << in.rdbuf();
565 fs::remove_all(parts_dir, pec);
569 if (fs::exists(
"err messages.txt", sec)) {
570 std::ifstream in(
"err messages.txt", std::ios::binary);
571 std::ofstream out(fs::path(dest_es) /
"err messages.txt",
572 std::ios::binary | std::ios::app);
598 PU::print_message(
rank,
"[CHECKPOINT] Intermediate stop — PE files keep natural IDs for resume",
PG::log);
602 std::ostringstream oss;
603 oss <<
"Merged " << merged_seq_count <<
" sequences, " << pe_count
605 <<
" in " << std::fixed << std::setprecision(1)
606 << (MPI_Wtime() - t_merge) <<
" s ("
607 << (legacy ?
"serial copy" :
"rename") <<
")";
614 PU::print_message(
rank,
"[CHECKPOINT] Gathering held_data from " + std::to_string(
PG::size - 1) +
" clients",
PG::log);
617 struct GatheredEntry {
621 std::shared_ptr<RestartNodeInfo> rinfo;
622 std::string branch_record_blob;
624 std::vector<GatheredEntry> all_held;
626 for (
int c = 1; c <
PG::size; c++) {
630 for (
int i = 0; i < count; i++) {
631 GatheredEntry entry{};
632 entry.source_rank = c;
634 entry.rinfo = std::make_shared<RestartNodeInfo>();
639 std::ostringstream oss(std::ios::binary);
640 boost::archive::binary_oarchive oa(oss);
642 entry.branch_record_blob = oss.str();
643 entry.cid = entry.rinfo->getCorrespondingBranchClusterID();
644 all_held.push_back(std::move(entry));
646 PU::print_message(
rank,
"[CHECKPOINT] Client " + std::to_string(c) +
": " + std::to_string(count) +
" entries",
PG::log);
652 std::set<int> seen_cids;
653 int next_unique_cid = 0;
655 for (
auto& entry : all_held)
656 if (entry.cid > next_unique_cid) next_unique_cid = entry.cid;
657 next_unique_cid += 1000;
659 for (
auto& entry : all_held) {
660 if (seen_cids.count(entry.cid)) {
661 int old_cid = entry.cid;
662 int new_cid = next_unique_cid++;
663 PU::print_message(
rank,
"[CHECKPOINT] CID collision: renumbering CID="
664 + std::to_string(old_cid) +
" -> " + std::to_string(new_cid),
PG::log);
667 entry.rinfo->setCorrespondingBranchClusterID(new_cid);
671 { std::istringstream iss(entry.branch_record_blob, std::ios::binary);
672 boost::archive::binary_iarchive ia(iss); ia >> br; }
674 if (bc->getIDOfThisBranchCluster() == old_cid)
675 bc->setIDOfThisBranchCluster(new_cid);
677 { std::ostringstream oss(std::ios::binary);
678 boost::archive::binary_oarchive oa(oss); oa << br;
679 entry.branch_record_blob = oss.str(); }
683 seen_cids.insert(entry.cid);
693 for (
auto& entry : all_held) {
704 if (ri && !ri->relap_restart_buf.empty()) {
705 ckpt.
source_blobs[cid] = std::move(ri->relap_restart_buf);
706 ri->relap_restart_buf.clear();
707 ri->relap_restart_buf.shrink_to_fit();
714 while (!temp.empty()) {
715 auto& task = temp.front();
722 std::shared_ptr<RestartNodeInfo> task_rinfo;
723 for (
auto& entry : all_held) {
724 if (entry.source_rank == task.source_rank && entry.local_id == task.local_id) {
726 task_rinfo = entry.rinfo;
743 PU::print_message(
rank,
"[CHECKPOINT] Carrying over " + std::to_string(
resume_pending_.size())
744 +
" undispatched inherited tasks",
PG::log);
748 std::shared_ptr<RestartNodeInfo> ri =
757 int carried_key = -(cs->second) - 1;
780 std::map<int, int> pending_min_bi;
782 auto it = pending_min_bi.find(pt.cid);
783 if (it == pending_min_bi.end() || pt.branch_index < it->second)
784 pending_min_bi[pt.cid] = pt.branch_index;
788 std::map<int, std::unique_ptr<BranchCluster>> best;
789 std::map<int, int> reserved_cid;
792 return bc.all_branch.empty() ? -1 : bc.all_branch.front()->getIDOfThisBranch();
799 for (
auto& bp : bc.all_branch)
800 if (bp && bp->getIDOfThisBranch() > 0) ++n;
807 size_t n = std::min(w.
all_branch.size(), l.all_branch.size());
808 for (
size_t i = 0; i < n; ++i) {
810 auto* lb = l.all_branch[i].get();
811 if (!wb || !lb)
continue;
812 if (wb->getIDOfThisBranch() <= 0 && lb->getIDOfThisBranch() > 0)
813 wb->setIDOfThisBranch(lb->getIDOfThisBranch());
814 for (
int c : lb->child_IDs) {
816 for (
int wc : wb->child_IDs)
817 if (wc == c) { have =
true;
break; }
818 if (!have) wb->child_IDs.push_back(c);
822 auto absorb = [&](
BranchRecord& br,
int own_target_cid) {
825 int ident = identity_of(*bc);
826 if (own_target_cid != -1 && bc->getIDOfThisBranchCluster() == own_target_cid
827 && !reserved_cid.count(ident))
828 reserved_cid[ident] = own_target_cid;
829 auto it = best.find(ident);
830 if (it == best.end()) {
831 best[ident] = std::move(bc);
832 }
else if (known_ids(*bc) > known_ids(*it->second)) {
833 merge_versions(*bc, *it->second);
834 it->second = std::move(bc);
836 merge_versions(*it->second, *bc);
845 std::istringstream iss(blob, std::ios::binary);
846 boost::archive::binary_iarchive ia(iss);
857 std::set<int> carried_targets;
860 carried_targets.insert(pt.cid);
862 std::istringstream iss(
resume_ckpt_->branch_center_blob, std::ios::binary);
863 boost::archive::binary_iarchive ia(iss);
867 int ident = identity_of(*bc);
868 int c = bc->getIDOfThisBranchCluster();
869 if (carried_targets.count(c) && !reserved_cid.count(ident))
870 reserved_cid[ident] = c;
871 auto it = best.find(ident);
872 if (it == best.end()) {
873 best[ident] = std::move(bc);
874 }
else if (known_ids(*bc) > known_ids(*it->second)) {
875 merge_versions(*bc, *it->second);
876 it->second = std::move(bc);
878 merge_versions(*it->second, *bc);
890 std::set<int> used_cids;
892 for (
auto& [ident, bc] : best)
893 next_cid = std::max(next_cid, bc->getIDOfThisBranchCluster());
894 for (
auto& [ident, cid] : reserved_cid) {
895 used_cids.insert(cid);
896 next_cid = std::max(next_cid, cid);
900 std::map<int, std::pair<int, BranchCluster*>> branch_to_cluster;
901 for (
auto& [ident, bc] : best) {
903 auto rit = reserved_cid.find(ident);
904 if (rit != reserved_cid.end()) {
905 final_cid = rit->second;
907 final_cid = bc->getIDOfThisBranchCluster();
908 if (used_cids.count(final_cid)) {
910 final_cid = next_cid++;
911 PU::print_message(
rank,
"[CHECKPOINT] CID " + std::to_string(old)
912 +
" names two physical clusters — renumbering non-target copy to "
913 + std::to_string(final_cid),
PG::log);
915 used_cids.insert(final_cid);
917 bc->setIDOfThisBranchCluster(final_cid);
918 for (
auto& bp : bc->all_branch)
919 if (bp && bp->getIDOfThisBranch() > 0)
920 branch_to_cluster[bp->getIDOfThisBranch()] = {final_cid, bc.get()};
926 for (
auto& [ident, bc] : best) {
927 int mb = bc->getIDOfTheMotherBranch();
928 auto mit = branch_to_cluster.find(mb);
929 if (mb > 0 && mit != branch_to_cluster.end()) {
930 bc->setIDOfMotherCluster(mit->second.first);
931 for (
auto& bp : mit->second.second->all_branch)
932 if (bp && bp->getIDOfThisBranch() == mb) {
933 bp->setIDOfTheChildBranchCluster(bc->getIDOfThisBranchCluster());
939 for (
auto& [ident, bc] : best) {
940 auto pit = pending_min_bi.find(bc->getIDOfThisBranchCluster());
941 if (pit != pending_min_bi.end()) {
942 bc->setCurrentBranchNum(pit->second - 1);
944 bc->setCurrentBranchNum(
static_cast<int>(bc->all_branch.size()) - 1);
954 max_cid = std::max(max_cid, bc->getIDOfThisBranchCluster());
964 int orig_max_branch_id = -1;
966 for (
auto& bp : bc->all_branch)
967 if (bp->getIDOfThisBranch() > orig_max_branch_id)
968 orig_max_branch_id = bp->getIDOfThisBranch();
973 std::ostringstream oss(std::ios::binary);
974 boost::archive::binary_oarchive oa(oss);
981 std::lock_guard<std::mutex> lock(
viz_state_->mu);
988 ckpt.
save(ckpt_path);
991 PU::print_message(
rank,
"[CHECKPOINT] pending: CID=" + std::to_string(pt.cid)
992 +
" bi=" + std::to_string(pt.branch_index)
993 +
" seq=" + std::to_string(pt.sequence_id),
PG::log);
995 PU::print_message(
rank,
"[CHECKPOINT] Saved: " + ckpt_path
999 std::cout <<
"Resume with: --resume=" << ckpt_path << std::endl;
1007 std::ostringstream msg;
1008 msg <<
"FATAL: checkpoint configuration mismatch (checkpoint fingerprint 0x"
1009 << std::hex << ckpt.config_fingerprint <<
", current inputs 0x"
1011 <<
"). The input deck, Input/ tree, or ZCtrlPar.txt changed since this "
1012 <<
"checkpoint was written. Restore the original inputs or start a fresh run.";
1014 std::cerr << msg.str() << std::endl;
1015 MPI_Abort(MPI_COMM_WORLD, 1);
1018 PU::print_message(
rank,
"[RESUME] Overriding --compress-queue to match the checkpoint ("
1019 + std::string(ckpt.compress_queue ?
"on" :
"off") +
")",
PG::log);
1027 std::lock_guard<std::mutex> lock(
viz_state_->mu);
1028 viz_state_->completed = ckpt.completed_reports;
1031 PU::print_message(
rank,
"[RESUME] Loaded: " + std::to_string(ckpt.sequences_completed)
1032 +
" sequences done, " + std::to_string(ckpt.pending_tasks.size()) +
" pending tasks"
1033 +
" seq_counter=" + std::to_string(ckpt.sequence_counter),
PG::log);
1045 int max_issued_bid = ckpt.max_branch_id;
1046 int max_file_bid = -1;
1047 bool has_zero =
false, has_negative =
false;
1048 long nonneg_count = 0;
1050 if (fs::is_directory(pe_dir))
1051 for (
auto&
e : fs::recursive_directory_iterator(pe_dir)) {
1052 std::string fname =
e.path().filename().string();
1053 if (
e.is_regular_file() && fname.size() > 7 && fname.substr(0, 7) ==
"ZPENode"
1054 && fname.back() ==
't') {
1057 try { fid = std::stoi(fname.substr(fname.find(
'_') + 1, fname.find(
'.') - fname.find(
'_') - 1)); }
1058 catch (...) {
continue; }
1059 if (fid > max_issued_bid) max_issued_bid = fid;
1060 if (fid > max_file_bid) max_file_bid = fid;
1061 if (fid == 0) has_zero =
true;
1062 if (fid < 0) has_negative =
true;
1063 if (fid >= 0) ++nonneg_count;
1071 if (has_zero && !has_negative
1072 && nonneg_count ==
static_cast<long>(max_file_bid) + 1) {
1073 std::string msg =
"FATAL: the project Output/ already contains a finalized run "
1074 "(sequentially renamed PE files). This checkpoint's run has completed; "
1075 "resuming would corrupt the merged outputs. Move the existing Output/ "
1076 "aside to re-run from this checkpoint.";
1078 std::cerr << msg << std::endl;
1079 MPI_Abort(MPI_COMM_WORLD, 1);
1081 constexpr int kEpochStride = 128 * 100000;
1082 int epoch_base = (std::max(max_issued_bid, 0) / kEpochStride + 1) * kEpochStride;
1083 for (
auto& ri : ckpt.task_restart_states)
1084 if (ri) ri->id_epoch_base = epoch_base;
1085 PU::print_message(
rank,
"[RESUME] Branch-ID epoch base = " + std::to_string(epoch_base)
1086 +
" (max issued so far: " + std::to_string(max_issued_bid) +
")",
PG::log);
1091 for (
auto& pt : ckpt.pending_tasks) {
1092 auto cs = ckpt.cid_source.find(pt.cid);
1093 if (cs != ckpt.cid_source.end())
1099 resume_ckpt_ = std::make_unique<DETCheckpoint>(std::move(ckpt));
1110 if (free_rank < 0)
return false;
1122 if (ti < 0 || ti >=
static_cast<int>(
resume_ckpt_->task_restart_states.size()) ||
1124 PU::print_message(
rank,
"[RESUME] ERROR: no data for task index=" + std::to_string(ti),
PG::log);
1142 rinfo->relap_restart_buf = sb->second;
1146 rinfo->relap_restart_buf.clear();
1147 rinfo->relap_restart_buf.shrink_to_fit();
1166 std::istringstream iss(
resume_ckpt_->branch_center_blob, std::ios::binary);
1167 boost::archive::binary_iarchive ia(iss);
1180 PU::print_message(
rank,
"[RESUME] Dispatched CID=" + std::to_string(task.cid)
1181 +
" bi=" + std::to_string(task.branch_index) +
" seq=" + std::to_string(seq_id)
1182 +
" -> C[" + std::to_string(free_rank) +
"]",
PG::log);
long get_rss_mb()
Get current process RSS in MB (Linux + macOS, returns 0 elsewhere).
static void mpi_sigint_handler(int)
static void mpi_sigusr1_handler(int)
static volatile sig_atomic_t mpi_sigint_received
static int show_mpi_interrupt_menu(int seq_completed, int queue_depth, int busy_clients)
static volatile sig_atomic_t mpi_checkpoint_requested
MPI server (rank 0): dispatches DET sequences to worker clients.
SimulationContext g_sim
Global simulation context: the single instance, defined in SimulationContext.cpp.
HTTP server for real-time DET visualization on MPI rank 0.
std::vector< std::unique_ptr< BranchProbability > > all_branch
The alternatives of this branching point; index 0 is the one the generating sequence continues on.
void setIDOfRecentGeneratedES(int)
int getIDOfRecentGeneratedES()
void setIDOfRecentGeneratedBranchCluster(int)
void setIDOfRecentGeneratedBranch(int)
std::vector< std::unique_ptr< BranchCluster > > branch_cluster
All branching points generated so far, in creation order.
void initiationForSimulation()
EventSymptomMatrix diagnosis_matrix
void initiationForSimulation()
void initiationForSimulation()
std::string getProjectDirectory()
std::string getESDirectory()
std::string getPEDirectory()
void initiationForSimulation(ConsoleMode iMode=ConsoleMode::Console)
Prepares the RELAP5 bridge and the working directory for a run. Call it after parseOptions().
std::string getOutputDirectory()
std::string getKPSDirectory()
void merge_outputs()
Merge per-rank Output/ directories into the shared project Output/.
bool dispatch_one_resume_task()
Dispatch the last task of resume_pending_ to a free client.
std::shared_ptr< TreeState > viz_state_
std::unordered_map< int, int > rank_current_sequence_
rank -> sequence_id being run
int next_candidate
Round-robin pointer (starts at rank 1)
void Shutdown()
Send client_shutdown to every client rank and stop the visualization server. Under –merge=now each cl...
std::queue< RestartTask > tasks_queue
Pending metadata-only restart tasks.
Server(int rank, int viz_port=0)
Set up the client table and, when viz_port is nonzero, start the visualization server; also runs Sche...
int rank
This process rank (always 0)
std::unique_ptr< DETCheckpoint > resume_ckpt_
Loaded –resume checkpoint (null in a fresh run)
int find_next_free_client_round_robin()
Claim the next free client, round-robin from next_candidate. The returned client is marked busy and n...
~Server()
Defined in Server.cpp (VisualizationServer complete type required)
int sequence_counter_
Next sequence ID to assign (0 is the initial sequence)
int find_free_client_except(int exclude_rank)
Claim the next free client via round-robin, skipping exclude_rank.
void Simulation()
Main server loop: poll for messages, dispatch tasks, until the tree is complete.
std::map< int, int > resume_source_refs_
Undispatched resume tasks per checkpoint source blob.
void dispatch_available_tasks()
Make one pass over the task queue.
int num_free_clients
Number of currently idle clients.
int last_busy_
last logged busy-worker count
std::ofstream occupancy_log_
double occupancy_t0_
MPI_Wtime() when the log was opened [s].
int last_queue_
last logged depth, to suppress no-op rows
bool stopped_at_checkpoint_
Run ended at a checkpoint: outputs are intermediate.
int last_checkpoint_seq_
Sequence count at the last checkpoint written from the message path (-1 = none)
void write_checkpoint()
Gather held branch data from every client and write <checkpoint_dir>/checkpoint_seq_<sequences_comple...
int sequences_completed_
Total sequences finished so far.
bool checkpoint_pending_
Checkpoint requested, waiting for clients to drain.
std::vector< PendingBranch > resume_pending_
Undispatched tasks; consumed from the back, index-aligned with resume_ckpt_->task_restart_states.
void dispatch_resume_tasks()
Load the –resume checkpoint and dispatch its pending tasks.
std::unique_ptr< int[]> clients_busy
Per-client busy flag (0=free, 1=busy)
std::unique_ptr< VisualizationServer > viz_server_
void initiationForSimulation()
bool log
Enable/disable rank-specific logging.
@ client_send_to_peer
Server -> Client A: send held data to another client.
@ server_branch_meta
Client A -> Server: branch metadata only (rank-local branch id + num_branches)
@ client_run_held
Server -> Client A: run task using own held data.
@ client_peer_assign
Server -> Client A: peer assignment list for direct sends.
@ client_shutdown
Server -> Client: no more work, shut down.
@ server_done_simulating
Client -> Server: empty completion signal, client is free. The server handles it; Client....
@ server_done_with_report
Client -> Server: sequence completed with SequenceReport payload.
@ server_send_complete
Client -> Server: data send finished, client is free again.
@ client_simulate_this
Server -> Client: run the initial (first) sequence.
@ client_restart_run
Server -> Client: run a pending task of a resumed checkpoint with server-provided data.
@ client_checkpoint
Server -> Client: send held_data for checkpoint gather.
@ server_output_data
Client -> Server: SequenceOutputData (before report)
int size
Total number of MPI processes.
int rank
MPI rank of this process.
size_t build_es_summary(const std::string &dest_es)
Rebuild <dest_es>ES_summary.txt by scanning the destination ES tree.
std::vector< std::pair< int, std::string > > collect(const std::string &pe_dir)
Collect every PE node under pe_dir (recursively), from either layout, reading all node content into m...
size_t rewrite_sequential(const std::string &pe_dir, std::vector< std::pair< int, std::string > > entries, bool aggregate)
Replace the contents of pe_dir with entries renumbered 0..N-1 in ascending id order (the content path...
const char * aggregate_basename()
Basename of the aggregate file ("ZPENodes_agg.txt"). It is in the MPI merge's append-files list,...
size_t finalize_sequential(const std::string &pe_dir, bool aggregate)
End-of-run finalize: renumber every PE node 0..N-1 in id order, ending in the same layout rewrite_seq...
void set_shared_procedures(const Procedures *master)
void set_shared_kb(int operator_type, const KB *master)
void set_shared_control_panel(const ControlPanel *master)
void set_shared_diagnosis_matrix(int operator_type, const EventSymptomMatrix *master)
Complete state of a dynamic event tree run at a sequence boundary.
std::map< int, std::string > branch_record_blobs
cid -> serialized BranchRecord of the holding client; filled by the MPI writer only and used there to...
std::map< int, std::vector< char > > source_blobs
source key -> restart blob
std::vector< SequenceReport > completed_reports
Reports of finished sequences (filled only when the visualization server is on)
int sequences_completed
Sequences finished when the checkpoint was taken.
std::string branch_center_blob
uint64_t config_fingerprint
void save(const std::string &path)
Write the checkpoint to path.
std::vector< PendingBranch > pending_tasks
Unexplored branches.
std::map< int, std::shared_ptr< RestartNodeInfo > > restart_states
cid -> snapshot; read by serial resume
static DETCheckpoint load(const std::string &path)
Read a checkpoint file.
std::map< int, int > cid_source
std::vector< std::shared_ptr< RestartNodeInfo > > task_restart_states
Snapshot per pending task (null = none); read by MPI resume.
One unexplored branch recorded in a checkpoint.
int cid
Branch cluster ID the branch belongs to.
int sequence_id
Global sequence ID already assigned by the MPI server; 0 = assign at dispatch (serial checkpoints)
int parent_sequence_id
Sequence that generated the branch point (-1 = unknown)
int branch_index
Index of the alternative within the cluster (1..N-1)
Per-sequence output payload sent from client to server via MPI.
Report sent by client when a sequence completes (for DET visualization).
A metadata-only restart task: the data stays on the source client.
int sequence_id
Assigned sequence number.
int local_id
Rank-local branch-point ID, the key of the data held on the source client.
int parent_sequence_id
Sequence that branched to create this task.
int source_rank
Client that holds the branch data.
int branch_index
Which alternative branch to run (1..N-1)
std::string resume_file
–resume checkpoint file as an absolute path (empty = fresh start)
std::string checkpoint_dir
–checkpoint-dir as an absolute path, created at startup (empty = disabled)
bool checkpoint_stop
–checkpoint-stop: exit after the first checkpoint written; requires checkpoint_interval > 0
int checkpoint_interval
–checkpoint-interval: checkpoint every N completed sequences (0 = disabled); requires checkpoint_dir
bool compress_queue
–compress-queue (default off): LZ4-compress RELAP restart buffers held and sent between MPI ranks
std::string occupancy_log
–occupancy-log path: CSV of server queue/worker occupancy (empty = disabled, MPI only)....
Scheduler SScheduler
Simulation scheduling, parses CLI options.
Procedures All_procedure
Emergency operating procedures.
OperatorAT Action_Taker
Crew action execution model.
ControlPanel Indicator
Control room panel state.
OperatorDM Decision_Maker
Crew decision-making model.
SystemModule Plant
Maps RELAP5 card numbers to C++ objects.
uint64_t config_fingerprint
FNV-1a of immutable config inputs (see ConfigFingerprint.h); 0 = not computed.
ConvertToString Translator
Parameter name/value conversion.