6#include "../Global/RestartTrim.h"
7#include "../Global/SharedConfig.h"
8#include "../Solver/RelapSolver.h"
9#include "../Solver/RestApiSolver.h"
10#include "../Global/KeyParameterHistory.h"
11#include "../Scheduler/AnalysisInfoContainer.h"
15#include <unordered_set>
16#include "../Global/SimulationContext.h"
17#include "../Global/CheckedIO.h"
18#include "../Global/PEOutput.h"
24 if (!getenv(
"ADSIDAC_BRANCH_DIAG"))
return;
30 std::vector<std::string> parts;
33 while (cur_cid != 0) {
38 fprintf(stderr,
"\n");
43 parts.emplace_back(buf);
45 if (mother_cid == 0)
break;
47 if (!mother_bc)
break;
49 for (
int i = 0; i < static_cast<int>(mother_bc->
all_branch.size()); ++i) {
50 if (mother_bc->
all_branch[i]->getIDOfTheChildBranchCluster() == cur_cid) {
57 for (
int i =
static_cast<int>(parts.size()) - 1; i >= 0; --i) {
58 if (!ancestry.empty()) ancestry +=
">";
64 if (!events.empty()) events +=
",";
66 snprintf(buf,
sizeof(buf),
"%.4f:%d", t, n);
69 if (events.empty()) events =
"none";
70 fprintf(stderr,
"[SEQ-FP] rank=%d ancestry=%s events=%s term=%.4f restart_cid=%d restart_bn=%d\n",
80 ev.
time = rec->getTime();
82 ev.
pe_id = rec->getAssociatedBranchID();
83 data.
events.push_back(std::move(ev));
109 bc->savePENodeToFile();
122 std::vector<char> buf;
139 std::cerr <<
"FATAL: rank " <<
rank <<
" received a base-less restart buffer "
141 <<
"restoring it would abend in RELAP. Aborting." << std::endl;
142 MPI_Abort(MPI_COMM_WORLD, 1);
145 if (spliced.empty()) {
146 std::cerr <<
"FATAL: rank " <<
rank <<
" could not splice the restart base "
147 <<
"onto a transfer — aborting." << std::endl;
148 MPI_Abort(MPI_COMM_WORLD, 1);
150 raw = std::move(spliced);
168 std::vector<char> buf;
172 std::cerr <<
"FATAL: cannot restore RELAP5 state from " << file_path
173 <<
" — aborting." << std::endl;
174 MPI_Abort(MPI_COMM_WORLD, 1);
195 report.end_time =
solver_->currentTime();
197 report.worker_rank =
rank;
199 PU::print_message(
rank,
"Sent the finished signal to Server",
PG::log);
211 solver_ = std::make_unique<RelapSolver>();
217 std::string local_out = fs::current_path().string() +
"/Output/";
252 signal(SIGINT, SIG_IGN);
258 signal(SIGUSR1, SIG_IGN);
261 PU::print_message(
rank,
"Checking for signals",
PG::log);
266 MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &msg_ready, &
status_client);
269 MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &msg_ready, &
status_client);
274 PU::print_message(
rank,
"Received a new sequence to simulate",
PG::log);
299 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
300 std::vector<char> path_buf(path_len + 1,
'\0');
302 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
303 std::string server_file_path(path_buf.data());
315 solver_->setInputDeck(
"indeck.i");
337 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
338 std::vector<char> path_buf(path_len + 1,
'\0');
340 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
341 std::string file_path(path_buf.data());
345 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
351 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
353 MPI_COMM_WORLD, MPI_STATUS_IGNORE);
355 PU::print_message(
rank,
"[PEER] Received from C[" + std::to_string(source_rank)
356 +
"] seq=" + std::to_string(seq_id) +
" bi=" + std::to_string(branch_index),
PG::log);
365 solver_->setInputDeck(
"indeck.i");
374 int local_id, target_rank, branch_index, seq_id, parent_seq_id;
387 std::cerr <<
"FATAL: rank " <<
rank <<
" was asked to forward held branch data "
388 <<
"for lid=" << local_id <<
" but holds none — aborting." << std::endl;
389 MPI_Abort(MPI_COMM_WORLD, 1);
391 auto& held = it->second;
392 PU::print_message(
rank,
"[PEER] Deferred send lid=" + std::to_string(local_id)
393 +
" -> C[" + std::to_string(target_rank) +
"] bi="
394 + std::to_string(branch_index) +
" seq=" + std::to_string(seq_id),
PG::log);
398 int path_len =
static_cast<int>(held.file_path.size());
400 MPI_Send(held.file_path.c_str(), path_len, MPI_CHAR,
407 if (held.ref_count <= 0)
held_data_.erase(it);
414 int local_id, branch_index, seq_id;
415 MPI_Recv(&local_id, 1, MPI_INT, 0,
PG::client_run_held, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
416 MPI_Recv(&branch_index, 1, MPI_INT, 0,
PG::client_run_held, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
422 PU::print_message(
rank,
"[PEER] ERROR: no held data for lid=" + std::to_string(local_id),
PG::log);
426 err_report.worker_rank =
rank;
431 PU::print_message(
rank,
"[PEER] Self-run lid=" + std::to_string(local_id)
432 +
" bi=" + std::to_string(branch_index) +
" seq=" + std::to_string(seq_id),
PG::log);
435 auto& held = it->second;
438 if (held.ref_count <= 1) {
454 if (held.ref_count <= 0)
held_data_.erase(it);
457 solver_->setInputDeck(
"indeck.i");
470 std::vector<int> inflated_lids;
472 if (held.rinfo->relap_restart_buf.empty() && !held.file_path.empty()) {
476 std::cerr <<
"FATAL: cannot embed restart data for lid=" << lid
477 <<
" from " << held.file_path <<
" — aborting." << std::endl;
478 MPI_Abort(MPI_COMM_WORLD, 1);
480 inflated_lids.push_back(lid);
481 PU::print_message(
rank,
"[CHECKPOINT] Read "
482 + std::to_string(held.rinfo->relap_restart_buf.size())
483 +
" bytes from " + held.file_path,
PG::log);
487 int count =
static_cast<int>(
held_data_.size());
497 for (
int lid : inflated_lids) {
498 auto& buf =
held_data_[lid].rinfo->relap_restart_buf;
502 PU::print_message(
rank,
"[CHECKPOINT] Sent " + std::to_string(count) +
" held entries to server",
PG::log);
519 for (
auto& entry : fs::directory_iterator(
".", ec)) {
520 auto fname = entry.path().filename().string();
521 if (fname.rfind(
"restart_", 0) == 0 &&
522 fname.size() > 2 && fname.compare(fname.size() - 2, 2,
".r") == 0)
523 fs::remove(entry.path(), ec);
531 MPI_Barrier(MPI_COMM_WORLD);
550 const fs::path local_out =
"Output";
551 if (!fs::exists(local_out, ec))
return;
552 static const std::vector<std::string> append_files = {
553 "Scenario info.txt",
"Procedure data.txt",
"Scan data.txt",
555 const std::string suffix =
".rank_" + std::to_string(
rank);
556 std::unordered_set<std::string> made_dirs;
557 auto move_file = [&](
const fs::path& src,
const fs::path& dest) {
558 if (made_dirs.insert(dest.parent_path().string()).second)
559 fs::create_directories(dest.parent_path(), ec);
561 fs::rename(src, dest, mec);
564 fs::copy(src, dest, fs::copy_options::overwrite_existing, mec);
565 if (!mec) fs::remove(src, mec);
571 std::vector<std::pair<fs::path, fs::path>> append_moves;
572 for (
auto& entry : fs::recursive_directory_iterator(local_out, ec)) {
574 if (!entry.is_regular_file())
continue;
575 const fs::path rel = fs::relative(entry.path(), local_out);
576 const std::string fname = entry.path().filename().string();
577 bool is_append =
false;
578 for (
auto& af : append_files)
579 if (fname == af) { is_append =
true;
break; }
581 append_moves.emplace_back(entry.path(),
582 shared_out / rel.parent_path() /
"parts" / (fname + suffix));
583 else if (rel.begin()->string() ==
"PE")
586 move_file(entry.path(), shared_out / rel);
588 for (
auto& [src, dest] : append_moves) {
598 if (fs::exists(
"err messages.txt", ec))
599 move_file(
"err messages.txt", shared_out /
"ES" /
"parts" / (
"err messages.txt" + suffix));
608 PU::print_message(
rank,
"CID=" + std::to_string(cid)
609 +
" branch_index=" + std::to_string(branch_index)
610 +
" n_count=" + std::to_string(n_count),
PG::log);
613 string content =
"100 restart transnt\n101 run\n103 ";
614 content += to_string(n_count);
616 ofstream fout(
"indeck.i", ios::out);
658 if (gc.get() != target) {
659 gc->setCurrentBranchNum(
static_cast<int>(gc->all_branch.size()) - 1);
688 auto br_copy = std::make_shared<BranchRecord>();
689 br_copy->copyBranchRecord(br);
690 held_data_[cid] = {rinfo, br_copy, path, refs};
691 PU::print_message(
rank,
"[PEER] Holding data for CID=" + std::to_string(cid)
692 +
" refs=" + std::to_string(refs)
bool read_file_verified(const std::string &path, std::vector< char > &out)
Read an entire binary file into out, verifying the byte count.
static std::string restart_base_path()
static void save_sequence_outputs(int seq_id)
static SequenceOutputData extract_sequence_outputs(int seq_id)
static void log_seq_fingerprint_mpi(int rank, double term_time)
MPI client (rank > 0): executes simulation sequences.
bool viz_outputs_any()
True if any viz output streaming is enabled.
std::vector< char > lz4_decompress(const char *data, int size)
LZ4-decompress a buffer that has a 4-byte original-size header.
void cleanup_fort_temps()
Clean up Fortran temporary files (fort*) left by RELAP5.
SimulationContext g_sim
Global simulation context: the single instance, defined in SimulationContext.cpp.
void clearAnalysisInfoContainerForRestart()
const std::vector< std::unique_ptr< AnalysisInfoContainerUnit > > & getRecords() const
int getNCountForRestartRELAP5()
int getIDOfMotherCluster()
void setCurrentBranchNum(int)
Sets the index (within all_branch) of the most recently started alternative. Alternatives with a larg...
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)
void setIDOfMotherBranch(int)
void setIDOfRecentGeneratedBranchCluster(int)
void setIDOfRecentGeneratedBranch(int)
std::vector< std::unique_ptr< BranchEndState > > end_state
End states of the finished sequences held by this record.
int getIDOfRecentGeneratedBranch()
void setRecentUnconditionalBranchProbability(double)
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 setIDOfMotherCluster(int)
void execute_relap()
Run one sequence on this client's solver: sets the thermo cache mode from the configuration,...
void hold_branch_data(int cid, std::shared_ptr< RestartNodeInfo > rinfo, BranchRecord &br, const std::string &path, int refs)
Store branch data for deferred peer transfer or a later self-run.
AdsidacCallback callback_
Per-rank callback instance.
int rank
MPI rank of this client.
int Work()
Main client work loop: receive tasks, execute, report back.
void restore_buffer(std::vector< char > raw)
Splice the base on if raw lacks it (MPI_Abort if impossible), then solver_->restore()
void self_merge_outputs()
Client(int rank)
Create the solver, initialize the engine modules and redirect the scheduler's output directories to <...
MPI_Status status_client
Status of the message being handled by Work()
int parent_seq_id_
Parent sequence ID for current run (received from server)
double branch_time_
Sim time when parent branched to create this sequence.
bool ensure_base_cache()
Load <project>/restart_base.bin if not already resident; false if unavailable.
int branches_this_seq_
Count of branching events during current sequence.
std::unordered_map< int, HeldBranchData > held_data_
Rank-local branch-point id -> data held for deferred peer sends and self-runs.
void parallel_restartforanewsequence_(int branch_index)
Install the received branch tree as the global one and prepare the restart of one branch alternative.
void restore_relap_state(const std::string &file_path, bool clear_buf)
Restore the solver state from restart_info_'s buffer or a restart file.
std::shared_ptr< RestartNodeInfo > restart_info_
Snapshot of the task being started (immutable after creation)
std::unique_ptr< ThermalHydraulicSolver > solver_
Per-rank solver instance.
void complete_sequence(int seq_id)
Post-sequence completion: write the sequence's ES, KPS, analysis and PE node files,...
int local_branch_counter_
Per-client branch-point counter (see getNextLocalBranchID)
std::shared_ptr< BranchRecord > branch_center_
Branch record of the task being started (consumed by parallel_restartforanewsequence_)
std::vector< char > base_cache_
[header][complete restart base], cached once per rank.
void initiationForSimulation()
void outputHistoryToFile()
void clearKeyParameterHistory()
EventSymptomMatrix diagnosis_matrix
void initiationForSimulation()
void initiationForSimulation()
void setKPSDirectory(std::string)
void setESDirectory(std::string)
std::string getProjectDirectory()
std::string getESDirectory()
void setPEDirectory(std::string)
void initiationForSimulation(ConsoleMode iMode=ConsoleMode::Console)
Prepares the RELAP5 bridge and the working directory for a run. Call it after parseOptions().
void setOutputDirectory(std::string)
std::string getKPSDirectory()
void initiationForSimulation()
double getTimeAtWhichThisEventHappen() const
bool log
Enable/disable rank-specific logging.
@ client_send_to_peer
Server -> Client A: send held data to another client.
@ client_run_held
Server -> Client A: run task using own held data.
@ client_shutdown
Server -> Client: no more work, shut down.
@ 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_peer_restart
Client A -> Client B: direct peer-to-peer restart data.
@ client_checkpoint
Server -> Client: send held_data for checkpoint gather.
@ server_output_data
Client -> Server: SequenceOutputData (before report)
int rank
MPI rank of this process.
void move_file(const fs::path &src, const fs::path &dest, std::unordered_set< std::string > &made_dirs)
Move one file: rename (replacing an existing dest), with copy+remove fallback for EXDEV or exotic fil...
const char * aggregate_basename()
Basename of the aggregate file ("ZPENodes_agg.txt"). It is in the MPI merge's append-files list,...
std::pair< size_t, size_t > compact_aggregate(const fs::path &agg_path)
Rewrite one rank's aggregate file last-wins per id, sorted by id.
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)
std::vector< char > splice_base(const std::vector< char > &hdr_base, const std::vector< char > &body)
Put the cached base back in front of a base-less transfer.
std::vector< char > extract_header_and_base(const std::vector< char > &buf)
[header][complete base], the run-wide constant that is cached per rank and published as <project>/res...
bool has_complete_restart(const std::vector< char > &buf)
Does this buffer contain a complete restart dump (the base)?
Per-sequence output payload sent from client to server via MPI.
std::vector< SequenceOutputEvent > events
SequenceOutputEndState end_state
std::string consequence_description
Lightweight PE event record (extracted from AnalysisInfoContainerUnit).
int pe_id
associated_branch_ID → PE_N
Report sent by client when a sequence completes (for DET visualization).
int thermo_cache_mode
0 = reuse cached steam tables (default), 2 = force reload every sequence (–no-thermo-cache)....
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,...
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.
int id_epoch_base
Branch-ID epoch for resumed MPI runs; 0 = fresh run.
bool fresh_restart
One-shot flag for restart init.
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.