7#include "../System/SystemModule.h"
9#include "../Branch/BranchRecord.h"
11#include <boost/program_options.hpp>
13#include "../Global/ConfigFingerprint.h"
15namespace fs = std::filesystem;
16#include "VersionConfig.h"
17#include <boost/serialization/export.hpp>
18#include "../Global/SimulationContext.h"
20#include <boost/archive/binary_oarchive.hpp>
21#include <boost/archive/binary_iarchive.hpp>
29namespace po = boost::program_options;
61 const std::string RED =
"\033[31m";
62 const std::string GREEN =
"\033[32m";
63 const std::string MAGENTA =
"\033[35m";
64 const std::string UNDERLINE =
"\033[4m";
65 const std::string RESET =
"\033[0m";
68 const std::string VERSION_STRING =
"v" + std::to_string(ADSIDAC_VERSION_MAJOR) +
"." + std::to_string(ADSIDAC_VERSION_MINOR) +
"." + std::to_string(ADSIDAC_VERSION_PATCH);
70 po::options_description desc(
"Allowed options");
72 (
"help,h",
"produce help message")
73 (
"version,v",
"display version information")
74 (
"info,i",
"display detailed information about the application")
75 (
"project-dir", po::value<std::string>()->required(),
"set project directory")
76 (
"input-deck", po::value<std::string>()->required(),
"set input deck file")
77 (
"solver", po::value<std::string>()->default_value(
"relap"),
"solver backend: relap|0 (default) or mock|1 (REST API mock solver)")
78 (
"solver-port", po::value<int>()->default_value(9090),
"mock solver port (default 9090)")
79 (
"solver-url", po::value<std::string>()->default_value(
""),
"override mock solver URL (e.g., http://remote-host:8080)")
80 (
"no-thermo-cache", po::bool_switch()->default_value(
false),
"disable thermodynamic table caching (reload from file every sequence)")
81 (
"no-memory-restart", po::bool_switch()->default_value(
false),
"disable in-memory restart (use file-based restart)")
82 (
"memory-limit", po::value<long>()->default_value(0),
"RSS limit in MB for adaptive restart (0=unlimited)")
83 (
"compress-queue", po::bool_switch()->default_value(
false),
"LZ4-compress restart buffers on MPI server queue")
84 (
"aggregate-pe", po::bool_switch()->default_value(
false),
"write PE nodes as records in a single file per rank instead of one file each (default off: changes output layout)")
85 (
"viz-port", po::value<int>()->default_value(0),
"Port for real-time DET visualization web server (MPI only, 0=disabled)")
86 (
"viz-outputs", po::value<std::string>()->default_value(
""),
"Output types streamed to viz server: events,es,all (requires --viz-port)")
87 (
"notify-ntfy", po::value<std::string>()->implicit_value(
"auto"),
"Send ntfy.sh notification (auto-generates topic from hostname, or specify topic)")
88 (
"notify-discord", po::value<std::string>(),
"Send completion notification to Discord webhook URL")
89 (
"checkpoint-dir", po::value<std::string>()->default_value(
""),
"directory for DET checkpoint files")
90 (
"checkpoint-interval", po::value<int>()->default_value(0),
"checkpoint every N sequences (0=disabled)")
91 (
"checkpoint-stop", po::bool_switch()->default_value(
false),
"exit after writing checkpoint (for testing)")
92 (
"resume", po::value<std::string>()->default_value(
""),
"resume simulation from checkpoint file")
93 (
"truncation-time", po::value<double>()->default_value(0),
"override truncation time from ZCtrlPar.txt (0=use file value)")
94 (
"occupancy-log", po::value<std::string>()->default_value(
""),
"MPI only: append server queue depth and busy-worker count to a CSV on every change (empty=disabled)")
95 (
"merge", po::value<std::string>()->default_value(
"now"),
"MPI output merge mode: now (default, rename-based worker-parallel), serial (legacy rank-0 copy loop), or defer (end at tree completion, consolidate later with --merge-only)")
96 (
"merge-only", po::bool_switch()->default_value(
false),
"consolidate the per-rank outputs of a deferred (or interrupted) merge into Output/ and exit; run without mpirun")
97 (
"merge-threads", po::value<int>()->default_value(0),
"thread pool size for --merge-only rank-directory moves (0 = min(8, hardware))")
98 (
"enable-rm-cycle", po::bool_switch()->default_value(
false),
"enable ReasonMachine snapshot/restore/serialize machinery (dormant cycle; see docs)");
101 po::variables_map vm;
103 po::store(po::parse_command_line(argc, argv, desc), vm);
105 if (vm.count(
"help")) {
106 std::cout << desc <<
"\n";
110 if (vm.count(
"version")) {
111 std::cout << VERSION_STRING << std::endl;
115 if (vm.count(
"info")) {
116 std::cout <<
"ADS-IDAC\n" << std::endl;
117 std::cout <<
"Accident Dynamics Simulator paired with the Information, Decision, and Action in a Crew" << std::endl;
118 std::cout << VERSION_STRING << std::endl;
119 std::cout <<
"Maintained by: Probabilistic Risk Assessment Group of NC State Nuclear Engineering" << std::endl;
120 std::cout <<
"https://www.ne.ncsu.edu/prag/" << std::endl;
125 std::ifstream ifs(
"ZiniADS.txt");
127 po::store(po::parse_config_file(ifs, desc), vm);
131 }
catch (
const po::error &
e) {
132 std::cerr << RED <<
"ERROR: " << RESET <<
e.what() << RESET << std::endl;
133 std::cerr <<
"Check " << GREEN <<
"./adsidac --help" << RESET <<std::endl;
142 std::string solver_arg = vm[
"solver"].as<std::string>();
144 std::string explicit_url = vm[
"solver-url"].as<std::string>();
145 if (solver_arg ==
"relap" || solver_arg ==
"0") {
146 if (!explicit_url.empty()) {
147 std::cerr <<
"ERROR: --solver-url cannot be used with --solver=relap\n";
151 }
else if (solver_arg ==
"mock" || solver_arg ==
"1") {
152 if (!explicit_url.empty()) {
157 }
else if (!explicit_url.empty()) {
163 std::cerr <<
"ERROR: unknown solver '" << solver_arg <<
"'. Use relap|0 or mock|1\n";
171 std::cerr <<
"ERROR: --no-memory-restart and --memory-limit are mutually exclusive\n";
176 if (fs::exists(
"/dev/shm"))
179 g_sim.
config.
shm_prefix = fs::temp_directory_path().string() +
"/adsidac_" + std::to_string(getpid()) +
"_";
188 std::string vo = vm[
"viz-outputs"].as<std::string>();
191 std::cerr <<
"ERROR: --viz-outputs requires --viz-port to be set\n";
194 std::istringstream iss(vo);
196 while (std::getline(iss, tok,
',')) {
200 else { std::cerr <<
"ERROR: unknown viz output type '" << tok <<
"'\n"; exit(1); }
202 std::cout <<
"Viz outputs:";
205 std::cout << std::endl;
208 if (vm.count(
"notify-ntfy")) {
214 if (vm.count(
"notify-discord"))
230 std::cerr <<
"ERROR: --checkpoint-interval requires --checkpoint-dir\n";
234 std::cerr <<
"ERROR: --checkpoint-stop requires --checkpoint-interval\n";
248 const std::string mm = vm[
"merge"].as<std::string>();
253 std::cerr <<
"ERROR: unknown merge mode '" << mm <<
"'. Use now, serial, or defer\n";
260 std::cerr <<
"ERROR: --merge-only does not run a simulation; it cannot be combined "
261 "with --resume or checkpointing\n";
268 std::cerr <<
"ERROR: this project has a deferred, unmerged run "
269 "(Output/MERGE_PENDING.txt). Run --merge-only first; resuming now "
270 "would delete the rank directories that hold its outputs\n";
283 str1 =
"/Output/ES/";
287 str1 =
"/Output/PE/";
291 str1 =
"/Output/KPS/";
295 str1 =
"/Input/Crew/Procedures/";
299 str1 =
"/Input/Crew/DecisionMaker/";
303 str1 =
"/Input/Crew/ActionTaker/";
307 str1 =
"/Input/Crew/Consultant/";
311 str1 =
"/Input/HardwareReliability/";
315 str1 =
"/Input/Indicators/";
319 str1 =
"/Input/Scheduler/";
323 str1 =
"/Input/System/";
327 str1 =
"/Input/SupportSystemsReliability/";
333 str1 +=
"ZCtrlPar.txt";
334 ifstream fin1(str1.c_str(), ios::in);
335 if (!fin1.is_open()) {
336 cout <<
"File: " << str1 <<
" cound not be found" << endl;
337 ofstream fout2(
"err messages.txt", ios::app);
338 fout2 <<
r5pars_.timesno <<
" File: " << str1
339 <<
" cound not be found\n";
351 std::cout <<
"FATAL: malformed ZCtrlPar.txt — could not parse "
352 <<
"Truncation_probability / Truncation_time." << std::endl;
366 r5pars_.tsimmode =
static_cast<int>(iMode);
389 strcpy(
r5pars_.carg0,
"relap5");
390 strcpy(
r5pars_.carg4,
"out.o");
394 constexpr size_t kMaxRelapPath = 80;
397 if (shm_restart.size() > kMaxRelapPath) {
398 std::cout <<
"FATAL: restart path exceeds RELAP5's " << kMaxRelapPath
399 <<
"-character filename limit: " << shm_restart << std::endl;
402 strcpy(
r5pars_.carg6, shm_restart.c_str());
404 strcpy(
r5pars_.carg6,
"restart.r");
408 <<
" characters, exceeding RELAP5's " << kMaxRelapPath
410 <<
"\n Run from a shorter directory or pass a shorter relative path."
416 if (!deck_test.good()) {
417 std::cout <<
"FATAL: input deck not found or unreadable: "
445 str +=
"Scenario info.txt";
446 str1 +=
"Procedure data.txt";
447 str2 +=
"Scan data.txt";
449 remove(str1.c_str());
450 remove(str2.c_str());
454 remove(
"err messages.txt");
456 remove(
"dbg info.txt");
457 remove(
"Scenario info.txt");
458 remove(
"Procedure data.txt");
466 std::cout <<
"[CONFIG] fingerprint=0x" << std::hex
480 str1 =
"ZiniADS.txt";
481 ifstream fin(str1.c_str(), ios::in);
482 if (!fin.is_open()) {
483 cout <<
"File ZiniADS.txt cound not be found" << endl;
484 ofstream fout(
"err messages.txt", ios::app);
486 <<
" File ZIniADS.txt cound not be found\n";
493 }
while (ch !=
'\n');
498 }
while (ch !=
'\n');
502 }
while (ch !=
'\n');
507 }
while (ch !=
'\n');
523 str1 =
"/Output/ES/";
527 str1 =
"/Output/PE/";
531 str1 =
"/Output/KPS/";
535 str1 =
"/Input/Crew/Procedures/";
539 str1 =
"/Input/Crew/DecisionMaker/";
543 str1 =
"/Input/Crew/ActionTaker/";
547 str1 =
"/Input/Crew/Consultant/";
551 str1 =
"/Input/HardwareReliability/";
555 str1 =
"/Input/Indicators/";
559 str1 =
"/Input/Scheduler/";
563 str1 =
"/Input/System/";
567 str1 =
"/Input/SupportSystemsReliability/";
573 str1 +=
"ZCtrlPar.txt";
574 ifstream fin1(str1.c_str(), ios::in);
575 if (!fin1.is_open()) {
576 cout <<
"File: " << str1 <<
" cound not be found" << endl;
577 ofstream fout2(
"err messages.txt", ios::app);
578 fout2 <<
r5pars_.timesno <<
" File: " << str1
579 <<
" cound not be found\n";
587 std::cout <<
"FATAL: malformed ZCtrlPar.txt — could not parse "
588 <<
"Truncation_probability / Truncation_time." << std::endl;
695 bool terminate_current_sequence =
false;
699 vector<BranchEndState *>::reverse_iterator es_ritr;
703 terminate_current_sequence =
true;
710 str =
"Sequence probability reached truncation value: ";
714 std::ostringstream streamObj;
716 str += streamObj.str();
723 terminate_current_sequence =
true;
731 str =
"Sequence reached simulation truncation time";
734 terminate_current_sequence =
true;
755 terminate_current_sequence =
true;
773 terminate_current_sequence =
true;
782 terminate_current_sequence =
true;
787 return terminate_current_sequence;
835 int i_sequence_terminated_by_alarm = 0;
836 int i_sequence_terminated_by_non_response_action = 0;
838 i_sequence_terminated_by_alarm = 1;
840 i_sequence_terminated_by_non_response_action = 1;
853 << i_sequence_terminated_by_alarm <<
' '
855 << i_sequence_terminated_by_non_response_action <<
' '
857 << file_name_restart << '\n' << file_name_ES << '\n' << static_cast<int>(
m_iMode)
861 int i_sequence_terminated_by_alarm = 0;
862 int i_sequence_terminated_by_non_response_action = 0;
866 if (str !=
"Scheduler") {
867 ofstream fout(
"err messages.txt", ios::app);
869 <<
"Scheduler::retrieveSchedulerFromFile File retrieval error in previous function"
872 <<
" Scheduler::retrieveSchedulerFromFile retrieval error in previous function\n";
876 "Scheduler::retrieveSchedulerFromFile Prior error");
891 >> i_sequence_terminated_by_alarm
893 >> i_sequence_terminated_by_non_response_action
898 if (i_sequence_terminated_by_alarm == 0) {
904 if (i_sequence_terminated_by_non_response_action == 0) {
Command/control dispatch — routes crew decisions to plant actions and branch generation.
std::string notify_discord_webhook
std::string auto_ntfy_topic()
Generate a default ntfy topic name: "ads" + 4 hex chars from hostname hash. Example: "ads7f3a".
std::string notify_ntfy_topic
Global notification targets (set by Scheduler::parseOptions).
Simulation configuration, directory management, CLI options, and truncation control.
SimulationContext g_sim
Global simulation context: the single instance, defined in SimulationContext.cpp.
void addRecord(std::string, int)
The end state (ES node) that terminates one sequence of the DET.
void setTypeOfConsequence(Consequence)
void setConsequenceDescription(std::string)
std::string getTempESMessage()
void setTempTypeOfConsequence(Consequence)
Stores the consequence of the end state the running sequence will get.
void addAnEndState(BranchEndState *)
Closes the running sequence with an end state and takes ownership of it.
Consequence getTempTypeOfConsequence()
double getRecentUnconditionalBranchProbability()
int getIDOfMotherBranch()
void setTempESMessage(std::string)
Stores the description of the end state the running sequence will get if it is terminated (set by mod...
void displayInformation(std::string strInformation)
std::string getOATDirectory()
void setFileNameNodeMultiProcessor(std::string)
void setODMDirectory(std::string)
std::string info_sequence_terminated_by_non_response_action
bool getSequenceTerminatedByAlarm()
void setInfoOfSequenceTerminatedByAlarm(std::string)
std::string file_name_restart
void readInfoFromFile(ConsoleMode iMode=ConsoleMode::Console)
Line-based alternative to parseOptions(); the engine's entry points do not call it.
void setFileNameRestart(std::string)
void setKPSDirectory(std::string)
std::string OAT_directory
bool checkForTerminatingCurrentSequence()
Decides whether the running sequence ends at this evaluation point.
double sequence_truncation_probabiliy
void setESDirectory(std::string)
std::string getFileNameIndeck()
double getODMActionTimeMultiplier()
void setInfoSequenceTerminatedByNonResponseAction(std::string)
Scheduler()
Constructs a scheduler in the state set by resetScheduler().
std::string kps_directory
std::string getProjectDirectory()
double getSequenceTruncationTime()
void setSchedulerDirectory(std::string)
void setSequenceTerminatedByNonResponseAction(bool)
void setODMActionTimeMultiplier(double)
void setHardwareReliabilityDirectory(std::string)
void setSystemDirectory(std::string)
std::string getIndicatorsDirectory()
double getSequenceTruncationProbability()
void setFileNameIndeck(std::string)
std::string getESDirectory()
std::string getFileNameNodeMultiProcessor()
std::string getSupportSystemsDirectory()
void setSequenceTerminatedByAlarm(bool)
std::string info_of_sequence_terminated_by_alarm
std::string getHardwareReliabilityDirectory()
std::string getSchedulerDirectory()
void saveSchedulerToFile(std::ofstream &)
Writes the scheduler to a text restart stream, starting with the label Scheduler. Strings are written...
bool getSequenceTerminatedByNonResponseAction()
std::string input_deck_file_directory_and_name
void setEOPDirectory(std::string)
void copyScheduler(Scheduler &)
Copies directories, truncation settings, multipliers, termination flags, file names and mode from ano...
std::string getFileNameRestart()
std::string getInfoSequenceTerminatedByNonResponseAction()
std::string project_directory
std::string OCT_directory
double OCT_action_time_multiplier
void setOCTDirectory(std::string)
std::string ODM_directory
double sequence_truncation_time
void endSimulation()
Does nothing.
std::string file_name_NodeMultiProcessor
void retrieveSchedulerFromFile(std::ifstream &)
Reads the layout written by saveSchedulerToFile. A stream that does not start with the Scheduler labe...
std::string getOCTDirectory()
std::string hardware_reliability_directory
std::string getFileNameES()
std::string system_directory
void setProjectDirectory(std::string)
void setIndicatorsDirectory(std::string)
void setOATActionTimeMultiplier(double)
bool sequence_terminated_by_non_response_action
void setSupportSystemsDirectory(std::string)
void setPEDirectory(std::string)
std::string getInfoOfSequenceTerminatedByAlarm()
void parseOptions(int argc, char *argv[])
Parses the command line and ZiniADS.txt, fills this scheduler and g_sim.config, and reads ZCtrlPar....
double ODM_action_time_multiplier
void setOCTActionTimeMultiplier(double)
std::string getPEDirectory()
int getSolverPort() const
void setFileNameES(std::string)
void setOATDirectory(std::string)
bool sequence_terminated_by_alarm
void initiationForSimulation(ConsoleMode iMode=ConsoleMode::Console)
Prepares the RELAP5 bridge and the working directory for a run. Call it after parseOptions().
void setMiMode(ConsoleMode)
std::string output_directory
double getOATActionTimeMultiplier()
std::string getSystemDirectory()
std::string getOutputDirectory()
std::string support_systems_directory
double OAT_action_time_multiplier
std::string file_name_indeck
void setOutputDirectory(std::string)
std::string getInputDeckFileDirectoryAndName()
std::string scheduler_directory
std::string getEOPDirectory()
std::string getODMDirectory()
std::string getKPSDirectory()
void resetScheduler()
Resets every member to its default: multipliers 1.0, termination flags off, all directories,...
void setInputDeckFileDirectoryAndName(std::string)
std::string indicators_directory
double getOCTActionTimeMultiplier()
std::string eop_directory
uint64_t compute_config_fingerprint(const std::string &project_dir, const std::string &deck_path)
Digest the configuration inputs of a project.
std::string resume_file
–resume checkpoint file as an absolute path (empty = fresh start)
bool rm_cycle_enabled
–enable-rm-cycle: ReasonMachine snapshot/restore/serialize/tbi machinery (see docs/developer/internal...
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
std::string shm_prefix
Path prefix for memory-restart files: "/dev/shm/adsidac_<pid>_" when /dev/shm exists,...
long memory_limit_mb
–memory-limit [MB]: 0 = unlimited; >0 = keep a restart buffer in memory only while process RSS is bel...
double truncation_time_override
CLI –truncation-time override [s] (0 = use ZCtrlPar.txt value). Not stored in checkpoints.
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
bool viz_output_end_states
Stream end states to viz server (–viz-outputs=es or all)
bool viz_output_events
Stream PE events to viz server (–viz-outputs=events or all)
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 –...
std::string occupancy_log
–occupancy-log path: CSV of server queue/worker occupancy (empty = disabled, MPI only)....
uint64_t config_fingerprint
FNV-1a of immutable config inputs (see ConfigFingerprint.h); 0 = not computed.
CommandControlCenter Command_handler
Command/control dispatch.
AnalysisInfoContainer Aux_info
Analysis records.
BranchRecord Branch_center
Dynamic event tree state.
bool expected_abort
Sequence ended abnormally.