31static std::unique_ptr<ThermalHydraulicSolver>
solver_ptr;
45 cout <<
"\n\n=== Simulation Interrupted (Ctrl+C) ===" << endl;
46 cout <<
" Sequences completed: " << seq_completed << endl;
48 cout <<
" 1. Continue simulation" << endl;
49 cout <<
" 2. Save checkpoint and exit" << endl;
50 cout <<
" 3. Exit without saving" << endl;
52 cout <<
"Choice [1]: " << flush;
55 if (!getline(cin, line) || line.empty())
return 1;
56 if (line[0] ==
'2')
return 2;
57 if (line[0] ==
'3')
return 3;
84 int cid = ri->getCorrespondingBranchClusterID();
89 int seq = cs_it->second;
99 std::string path =
restart_path(
"restart_seq_" + std::to_string(seq) +
".r");
101 std::cerr <<
"FATAL: checkpoint would embed a corrupt restart blob for CID "
102 << cid <<
" — aborting." << std::endl;
107 }
else if (!ri->relap_restart_buf.empty()) {
118 int cid = bc->getIDOfThisBranchCluster();
119 int curr = bc->getCurrentBranchNum();
120 int total =
static_cast<int>(bc->all_branch.size());
121 for (
int bi = curr + 1; bi < total; ++bi) {
152 const std::string pe_dir = (fs::path(project) /
"Output" /
"PE").
string() +
"/";
157 std::cout <<
"[MERGE-ONLY] Output/ is already finalized and no rank "
158 "directories remain; nothing to do" << std::endl;
161 std::cerr <<
"ERROR: no rank_N directories under " << project
162 <<
"; nothing to merge" << std::endl;
167 std::cout <<
"[MERGE-ONLY] Output/ is already finalized and the rank "
168 "directories hold no outputs; nothing to do" << std::endl;
171 std::cerr <<
"ERROR: Output/PE already carries a finalized run while rank "
172 "directories still hold outputs. Merging would corrupt the finalized "
173 "tree; move Output/ aside first" << std::endl;
178 <<
" manifest: salvage mode (merging whatever the rank directories hold)"
184 threads = std::min(8u, std::max(1u, std::thread::hardware_concurrency()));
185 const auto t0 = std::chrono::steady_clock::now();
187 const double secs = std::chrono::duration<double>(
188 std::chrono::steady_clock::now() - t0).count();
189 std::cout <<
"Merged " << seqs <<
" sequences, " << pe_count <<
" PE nodes into "
190 << (fs::path(project) /
"Output").
string() <<
" in " << std::fixed
191 << std::setprecision(1) << secs <<
" s (merge-only, " << ranks.size()
192 <<
" rank dirs, " << threads <<
" threads)" << std::endl;
196int main(
int argc,
char* argv[]){
199 for (
int i = 1; i < argc; ++i) {
200 std::string arg(argv[i]);
201 if (arg ==
"--help" || arg ==
"-h" || arg ==
"--version" || arg ==
"-v" || arg ==
"--info" || arg ==
"-i") {
208 for (
int i = 1; i < argc; ++i) {
209 if (std::string(argv[i]) ==
"--merge-only") {
210 if (std::getenv(
"OMPI_COMM_WORLD_SIZE") || std::getenv(
"PMI_SIZE")) {
211 std::cerr <<
"ERROR: --merge-only runs serially; launch it without mpirun\n";
219 parallelexec parallel;
221 (std::getenv(
"PMI_SIZE") != NULL);
224 MPI_Init(&argc, &argv);
225 parallel.parallel_init(&argc, &argv);
227 ParallelUtilities::print_message(
PG::rank,
"Parallel Execution finished...",
true);
238 string str, time_begin, time_end;
242 time_begin += ctime(&t_current);
244 srand((
unsigned) time(0));
276 cout <<
"Serial execution" << std::endl;
279 int sequences_completed = 0;
291 int max_issued_bid = -1;
292 bool has_zero =
false, has_negative =
false;
293 long nonneg_count = 0;
295 if (fs::is_directory(pe_dir))
296 for (
auto&
e : fs::recursive_directory_iterator(pe_dir)) {
297 std::string fname =
e.path().filename().string();
298 if (
e.is_regular_file() && fname.size() > 7 && fname.substr(0, 7) ==
"ZPENode"
299 && fname.back() ==
't') {
302 try { fid = std::stoi(fname.substr(fname.find(
'_') + 1, fname.find(
'.') - fname.find(
'_') - 1)); }
303 catch (...) {
continue; }
304 if (fid > max_issued_bid) max_issued_bid = fid;
305 if (fid == 0) has_zero =
true;
306 if (fid < 0) has_negative =
true;
307 if (fid >= 0) ++nonneg_count;
316 if (has_zero && !has_negative
317 && nonneg_count ==
static_cast<long>(max_issued_bid) + 1) {
318 std::cerr <<
"FATAL: the project Output/ already contains a finalized run\n"
319 <<
" (sequentially renamed PE files). This checkpoint's run has\n"
320 <<
" completed; resuming would corrupt the merged outputs.\n"
321 <<
" Move the existing Output/ aside to re-run from this checkpoint."
330 std::cerr <<
"FATAL: checkpoint configuration mismatch.\n"
331 <<
" checkpoint fingerprint: 0x" << std::hex << ckpt.config_fingerprint <<
"\n"
333 <<
" The input deck, Input/ tree, or ZCtrlPar.txt changed since this\n"
334 <<
" checkpoint was written. Restore the original inputs or start a fresh run."
339 std::cout <<
"[RESUME] Overriding --compress-queue to match the checkpoint ("
340 << (ckpt.compress_queue ?
"on" :
"off") <<
")" << std::endl;
352 int max_cid = -1, max_bid = -1;
354 int cid = bc->getIDOfThisBranchCluster();
355 if (cid > max_cid) max_cid = cid;
356 for (
auto& bp : bc->all_branch) {
357 int bid = bp->getIDOfThisBranch();
358 if (bid > max_bid) max_bid = bid;
367 constexpr int kEpochStride = 128 * 100000;
368 int true_max = std::max({max_bid, max_issued_bid, ckpt.max_branch_id, 0});
369 int epoch_base = (true_max / kEpochStride + 1) * kEpochStride;
370 std::cout <<
"[RESUME] Branch-ID epoch base = " << epoch_base
371 <<
" (max issued so far: " << true_max <<
")" << std::endl;
383 for (
auto& [cid, ri] : ckpt.restart_states) {
386 for (
auto& [cid, seq] : ckpt.cid_source)
388 for (
auto& [seq, blob] : ckpt.source_blobs)
390 ckpt.source_blobs.clear();
397 if (blob.empty())
continue;
398 auto raw =
lz4_decompress(blob.data(),
static_cast<int>(blob.size()));
399 blob = std::move(raw);
402 std::cout <<
"[RESUME] Stripped queue-layer compression from "
406 sequences_completed = ckpt.sequences_completed;
407 std::cout <<
"[RESUME] Loaded: " << ckpt.sequences_completed <<
" sequences done, "
408 << ckpt.pending_tasks.size() <<
" pending tasks, "
413 solver.setNewRestart(
false);
414 solver.setInputDeck(
"indeck.i");
417 auto wall_start = std::chrono::steady_clock::now();
418 bool stopped_at_checkpoint =
false;
421 cout <<
"*** Sequence " << i <<
" ****" << endl;
426 solver.setMembufMode(1);
436 auto buf = solver.checkpoint();
441 std::string backup =
restart_path(
"restart_seq_" + std::to_string(i) +
".r");
451 str =
"Sequence is terminated for unknown reason.";
452 cout <<
"Sequence is terminated for unknown reason." << endl;
458 solver.setInputDeck(
"indeck.i");
459 solver.setNewRestart(
false);
464 ++sequences_completed;
478 cout <<
"Resume with: --resume=" << path << endl;
479 stopped_at_checkpoint =
true;
481 }
else if (choice == 3) {
482 cout <<
"Exiting without saving." << endl;
496 ckpt.save(ckpt_path);
497 cout <<
"Resume with: --resume=" << ckpt_path << endl;
499 cout <<
"[CHECKPOINT] --checkpoint-stop: exiting after " << sequences_completed <<
" sequences" << endl;
500 stopped_at_checkpoint =
true;
506 cout <<
"terminateall: " << solver.isTerminateAll() << endl;
507 }
while (!solver.isTerminateAll());
511 std::string bkp =
restart_path(
"restart_seq_" + std::to_string(s) +
".r");
512 if (fs::exists(bkp)) fs::remove(bkp);
516 if (fs::exists(shm_r)) fs::remove(shm_r);
546 time_end += ctime(&t_current);
553 double wall_seconds = std::chrono::duration<double>(
554 std::chrono::steady_clock::now() - wall_start).count();
566 cout <<
"Simulation Finished!\nStart at: " << time_begin <<
"\nEnd at: "
577 if (cb && s) cb->onEvaluationPoint(*s);
583 if (cb && s) cb->onTimestepAction(*s);
589 if (cb && s) cb->onScenarioStart(*s);
595 if (cb && s) cb->onFirstInit(*s);
601 if (cb && s) cb->onBranchingPoint(*s);
625 ofstream fout(
"err messages.txt", ios::app);
626 fout <<
r5pars_.timesno <<
" " << str <<
'\n';
634 std::vector<std::string> path_parts;
637 while (cur_cid != 0) {
641 snprintf(buf,
sizeof(buf),
"%.4f:%d",
643 path_parts.emplace_back(buf);
645 if (mother_cid == 0)
break;
647 if (!mother_bc)
break;
648 int mother_branch_pos = -1;
649 for (
int i = 0; i < static_cast<int>(mother_bc->
all_branch.size()); ++i) {
650 if (mother_bc->
all_branch[i]->getIDOfTheChildBranchCluster() == cur_cid) {
651 mother_branch_pos = i;
655 cur_branch_pos = (mother_branch_pos >= 0) ? mother_branch_pos : 0;
656 cur_cid = mother_cid;
659 for (
int i =
static_cast<int>(path_parts.size()) - 1; i >= 0; --i) {
660 if (!result.empty()) result +=
">";
661 result += path_parts[i];
667 if (!getenv(
"ADSIDAC_BRANCH_DIAG"))
return;
671 if (!events.empty()) events +=
",";
673 snprintf(buf,
sizeof(buf),
"%.4f:%d", t, n);
676 if (events.empty()) events =
"none";
677 fprintf(stderr,
"[SEQ-FP] ancestry=%s events=%s term=%.4f\n",
678 ancestry.c_str(), events.c_str(), term_time);
void secini_()
Security initialization callback.
int main(int argc, char *argv[])
void outputErrMessageToFile(string str)
void initiationforrestart_()
Initialize restart mode for next sequence.
void interfc_()
Called every RELAP5 timestep from dtstep.f. Triggers crew/diagnosis decisions.
static int run_merge_only(int argc, char *argv[])
static ThermalHydraulicSolver * solver_raw
AdsidacCallback engine_callback
static std::string compute_ancestry_path()
static int show_interrupt_menu(int seq_completed)
static std::unique_ptr< ThermalHydraulicSolver > solver_ptr
static void log_seq_fingerprint(double term_time)
void initiate_branching_()
void setrnode_()
Set restart node information in r5pars_.
static volatile sig_atomic_t sigint_received
void restartforanewsequence_()
Find next unexplored branch, generate indeck.i restart input.
void actonce_()
Execute crew actions once per transient.
static volatile sig_atomic_t checkpoint_requested
void actte_()
Execute crew actions at each timestep.
static void sigusr1_handler(int)
static void sigint_handler(int)
static DETCheckpoint build_serial_checkpoint(int seq_completed)
Fortran callback declarations and SimulationContext for ADS-IDAC main().
Engine-side implementation of SolverCallback.
void write_file_or_die(const std::string &path, const char *data, size_t size)
Write an entire buffer to a binary file (truncating it); fatal on any failure.
bool read_file_verified(const std::string &path, std::vector< char > &out)
Read an entire binary file into out, verifying the byte count.
void send_completion_notification(const RunSummary &summary)
Send completion notifications to all configured targets. Non-fatal: prints a warning if curl fails,...
Send completion notifications via ntfy.sh or Discord webhooks.
Output consolidation for the standalone –merge-only invocation (rank discovery, threaded ES/KPS renam...
Pivotal-event output layouts (one file per node, or self-delimiting records in one aggregate per rank...
bool should_use_memory_restart()
Decide whether to use memory mode for this restart operation. memory_limit_mb == 0 → unlimited (alway...
std::string restart_path(const std::string &name)
Get restart file path (shm or local).
std::vector< char > lz4_decompress(const char *data, int size)
LZ4-decompress a buffer that has a 4-byte original-size header.
long get_rss_mb()
Get current process RSS in MB (Linux + macOS, returns 0 elsewhere).
void cleanup_fort_temps()
Clean up Fortran temporary files (fort*) left by RELAP5.
SolverCallback * getActiveSolverCallback()
Get the currently active SolverCallback (set during runSequence). Used by extern "C" trampoline funct...
ThermalHydraulicSolver * getActiveSolver()
Get the currently active ThermalHydraulicSolver (set during runSequence). Returns nullptr when no seq...
RELAP5 implementation of ThermalHydraulicSolver.
REST API implementation of ThermalHydraulicSolver.
Process-local masters of immutable configuration (state overlay).
SimulationContext g_sim
Global simulation context: the single instance, defined in SimulationContext.cpp.
ADS-IDAC engine's implementation of SolverCallback.
void initiationForRestart(ThermalHydraulicSolver &solver)
Restore the C++ simulation state for the branch about to run.
void restartForNewSequence(ThermalHydraulicSolver &solver)
Serial path: find the next unexplored branch and prepare its restart.
void addRecordWithTime(double, std::string)
int getIDOfMotherCluster()
std::vector< std::unique_ptr< BranchProbability > > all_branch
The alternatives of this branching point; index 0 is the one the generating sequence continues on.
The end state (ES node) that terminates one sequence of the DET.
void setTypeOfConsequence(Consequence)
void setConsequenceDescription(std::string)
void setIDOfRecentGeneratedES(int)
void addAnEndState(BranchEndState *)
Closes the running sequence with an end state and takes ownership of it.
int getIDOfRecentGeneratedES()
void clearBranchRecordAll()
Resets the whole record: clusters, end states, pending forks, the three branch definition tables and ...
void setIDOfRecentGeneratedBranchCluster(int)
void setIDOfRecentGeneratedBranch(int)
void endSimulation()
Writes ES_summary.txt in the ES directory from the end_state vector: a header line ES# Probability En...
std::vector< std::unique_ptr< BranchEndState > > end_state
End states of the finished sequences held by this record.
int getIDOfRecentGeneratedBranch()
BranchCluster * findBrnachCluster(int)
Finds a cluster by ID, searching from the most recent cluster.
void initiationForSimulation()
Loads the three branch definition tables: procedure action time branches, mental procedure activation...
std::vector< std::unique_ptr< BranchCluster > > branch_cluster
All branching points generated so far, in creation order.
void clearCommandControlCenteFromSimulation()
void clearCommunicationPlaceAllPointers()
void initiationForSimulation()
void outputHistoryToFile()
void clearOperatorATFinal()
void clearOperatorDMFinal()
EventSymptomMatrix diagnosis_matrix
void initiationForSimulation()
void initiationForSimulation()
std::string getProjectDirectory()
double getSequenceTruncationTime()
void parseOptions(int argc, char *argv[])
Parses the command line and ZiniADS.txt, fills this scheduler and g_sim.config, and reads ZCtrlPar....
std::string getPEDirectory()
void initiationForSimulation(ConsoleMode iMode=ConsoleMode::Console)
Prepares the RELAP5 bridge and the working directory for a run. Call it after parseOptions().
void initiationForSimulation()
Abstract interface for thermal-hydraulic solvers.
double getTimeAtWhichThisEventHappen() const
int rank
MPI rank of this process.
const char * manifest_basename()
File name of the sentinel written to Output/ by –merge=defer. Its presence means the rank directories...
std::pair< size_t, size_t > merge_project(const std::string &project_dir, bool aggregate, int threads)
The full standalone merge, in this order: ES/KPS moves (threaded over rank dirs), rank-ordered PE mov...
bool looks_finalized(const std::string &pe_dir)
True when pe_dir already carries the finalized signature: at least one node, ids dense 0....
bool manifest_exists(const std::string &project_dir)
True if <project_dir>/Output/MERGE_PENDING.txt exists.
bool manifest_aggregate(const std::string &project_dir, bool fallback)
The aggregate_pe flag the deferred run recorded in its manifest.
bool ranks_hold_outputs(const std::string &project_dir, const std::vector< int > &ranks)
True when any rank_N/Output tree still holds mergeable output files (names starting with ZESNode,...
std::vector< int > discover_ranks(const std::string &project_dir)
Worker rank numbers N for which <project_dir>/rank_N is a directory.
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::vector< char > > source_blobs
source key -> restart blob
int sequences_completed
Sequences finished when the checkpoint was taken.
uint64_t config_fingerprint
std::vector< PendingBranch > pending_tasks
Unexplored branches.
std::map< int, std::shared_ptr< RestartNodeInfo > > restart_states
cid -> snapshot; read by serial resume
void capture_branch_center(const BranchRecord &br)
Serialize br (Boost binary archive) into branch_center_blob, replacing its previous content.
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 branch_index
Index of the alternative within the cluster (1..N-1)
Summary of a completed simulation run.
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 resume_has_end_states
True if the BranchRecord restored by a serial resume held end states (checkpoint written by a serial ...
long memory_limit_mb
–memory-limit [MB]: 0 = unlimited; >0 = keep a restart buffer in memory only while process RSS is bel...
int is_mpi_environment
1 if OMPI_COMM_WORLD_SIZE or PMI_SIZE is set in the environment, 0 for serial
int thermo_cache_mode
0 = reuse cached steam tables (default), 2 = force reload every sequence (–no-thermo-cache)....
int merge_threads
–merge-threads for –merge-only, threads over rank dirs (0 or less = min(8, hardware threads))
bool compress_queue
–compress-queue (default off): LZ4-compress RELAP restart buffers held and sent between MPI ranks
std::string solver_url
REST solver URL (empty = use RELAP5). –solver=mock gives "http://localhost:<--solver-port,...
bool memory_restart
In-memory restart. parseOptions() sets it to !–no-memory-restart, so it is TRUE in every run unless –...
int seq_restart_branch_num
Branch index within that cluster.
Scheduler SScheduler
Simulation scheduling, parses CLI options.
Procedures All_procedure
Emergency operating procedures.
OperatorAT Action_Taker
Crew action execution model.
KeyParameterHistory KPS
Time-series parameter recording.
int seq_restart_cid
CID of restart-origin cluster.
std::vector< std::pair< double, int > > current_seq_events
(branching_time, num_branches) pairs
ControlPanel Indicator
Control room panel state.
OperatorDM Decision_Maker
Crew decision-making model.
SystemModule Plant
Maps RELAP5 card numbers to C++ objects.
std::map< int, std::vector< char > > restart_buffers
seq_id -> RELAP5 restart buffer
uint64_t config_fingerprint
FNV-1a of immutable config inputs (see ConfigFingerprint.h); 0 = not computed.
CommandControlCenter Command_handler
Command/control dispatch.
bool fresh_restart
One-shot flag for restart init.
bool branches_generated_this_sequence
True if branches generated this seq.
std::map< int, int > cid_to_source_sequence
Maps cluster ID to source sequence.
CommunicationPlace Communication_place
Inter-module communication.
AnalysisInfoContainer Aux_info
Analysis records.
BranchRecord Branch_center
Dynamic event tree state.
ConvertToString Translator
Parameter name/value conversion.
std::deque< std::shared_ptr< RestartNodeInfo > > restart_info
Restart checkpoints for FIFO.
bool expected_abort
Sequence ended abnormally.