← Documentation
ADS-IDAC Engine 5.0.0
ParallelUtilities.cpp
Go to the documentation of this file.
1//
2// Created by atezbas on 10/21/24.
3//
4
5#include "ParallelUtilities.h"
6#include <memory>
7#include <boost/iostreams/device/array.hpp>
8#include <boost/iostreams/device/back_inserter.hpp>
9#include <boost/iostreams/stream.hpp>
10using namespace std;
11
12// Function to get a consistent color based on rank
13std::ostream& ParallelUtilities::get_random_color(std::ostream& stream, const int rank) {
14 // Use function pointers to termcolor manipulators
15 static std::vector<std::ostream& (*)(std::ostream&)> colors = {
16 termcolor::green,
17 termcolor::yellow,
18 termcolor::blue,
19 termcolor::magenta,
20 termcolor::cyan,
21 termcolor::white,
22 termcolor::bright_green,
23 termcolor::bright_yellow,
24 termcolor::bright_blue,
25 termcolor::bright_magenta,
26 termcolor::bright_cyan,
27 termcolor::bright_white
28 };
29
30 // Map the rank to one of the colors
31 return colors[rank % colors.size()](stream);
32}
33
34// Function to print colored message based on rank
35void ParallelUtilities::print_message(const int rank, const std::string& message, const bool log) {
36 if (!log) {
37 return; // Do not print if log is false
38 }
39
40 if (rank == 0) {
41 std::cout << termcolor::colorize << termcolor::red << "S[" << rank << "] - " << message << termcolor::reset << std::endl;
42 } else {
43 // Apply the color to std::cout
44 std::cout << termcolor::colorize;
45 get_random_color(std::cout, rank);
46 // Continue with the message
47 std::cout << termcolor::colorize << "C[" << rank << "] - " << message << termcolor::reset << std::endl;
48 }
49}
50
51//
52// void parallel_restartforanewsequence_() { //reload resrat info
53// bool fresh_restart{false};
54// bool found_restart_point = false;
55// long n_count;
56// string content = "100 restart transnt\n101 run\n103 ";
57// vector<BranchCluster *>::reverse_iterator bc_ritr, bc_ritr_temp;
58// bc_ritr = Branch_center.branch_cluster.rbegin();
59// //identify restart branch cluster and branch
60// while (bc_ritr != Branch_center.branch_cluster.rend()
61// && !found_restart_point) {
62// if ((*bc_ritr)->getCurrentBranchNum()
63// < ((*bc_ritr)->all_branch.size() - 1)) {
64// cout << "[getCurrentBranchNum()] " << (*bc_ritr)->getCurrentBranchNum() << endl;
65// cout << "[all_branch.size()] " << (*bc_ritr)->all_branch.size() - 1 << endl;
66// n_count = (*bc_ritr)->getNCountForRestartRELAP5();
67// fresh_restart = true;
68// found_restart_point = true;
69// } else {
70// (*bc_ritr)->savePENodeToFile();
71// bc_ritr_temp = bc_ritr;
72// if ((*bc_ritr)->getIDOfThisBranchCluster() == 0) {//the final branchCluster; all branches have been simulated
73// r5pars_.terminateall = true;
74// } else {//current branch cluser is no longer needed; remove memory
75// bc_ritr++;
76// }
77// delete *bc_ritr_temp;
78// Branch_center.branch_cluster.pop_back();
79// }
80// }
81// if (found_restart_point) { // more sequence left to be simulated
82// //generate RELAP restart file
83// content += to_string(n_count);
84// content += "\n.";
85// //
86// ofstream fout("indeck.i", ios::out);
87// fout << content;
88// fout.close();
89// } else if (r5pars_.terminateall) {
90// cout << "All sequences have been simulated" << endl;
91// } else { //all seuences have been sinulated
92// ofstream fout("err messages.txt", ios::app);
93// cout << "restartForANewSequence: could not find the restart point."
94// << endl;
95// fout << r5pars_.timesno
96// << " restartForANewSequence: could not find the restart point.\n";
97// fout.close();
98// }
99// /*!
100// * dump all key parameter data of current sequence into ../output/KPS/KPSFolder_x/ZKPS_y.txt
101// */
102// // KPS.outputHistoryToFile();
103// /*!
104// * dump all activities records of current sequence into ../output/ES/scenario info.txt
105// */
106// // Aux_info.saveInfoToFile();
107// KPS.clearKeyParameterHistory(); /*!< release non-necessary memory */
108// Aux_info.clearAnalysisInfoContainerForRestart(); /*!< release non-necessary memory */
109// //Added by Yuandan LI
110// // Decision_Maker.m_RM->SaveSequenceInfoToFile();
111// }
112
113
114// Binary string restart information exchange
115template<typename T>
116std::string ParallelUtilities::serialize_object(const T& obj) {
117 // An uncaught throw between the size and payload sends would leave the
118 // matched receiver blocked forever, so serialize fully (and abort the job
119 // on failure) before any bytes hit the wire.
120 try {
121 std::string bytes;
122 {
123 // The archive writes straight into the string: no ostringstream
124 // and no final .str() copy of the full payload.
125 boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>> os(bytes);
126 boost::archive::binary_oarchive oa(os);
127 oa << obj;
128 } // archive and stream flush on destruction
129 // Object streams are LZ4-framed on the wire (the 4-byte original-size
130 // header rides inside the blob); receive_object strips the layer.
131 // onBranchingPoint calls this once per branch point and sends the
132 // same bytes to every receiver.
133 // Only payloads up to WIRE_LZ4_MAX (4 MiB) are compressed: small
134 // structured payloads such as state-only crew snapshots and branch
135 // records. Larger streams are dominated by the RELAP restart image,
136 // whose encoding belongs to the queue layer (--compress-queue,
137 // opt-in); compressing them here would put the compressor on the
138 // dispatch critical path. They ship raw behind a negative-size header.
139 static constexpr size_t WIRE_LZ4_MAX = size_t(4) << 20;
140 if (bytes.size() <= WIRE_LZ4_MAX) {
141 auto packed = lz4_compress(bytes.data(), static_cast<int>(bytes.size()));
142 return std::string(packed.data(), packed.size());
143 }
144 std::string framed(sizeof(int32_t) + bytes.size(), '\0');
145 const int32_t raw_marker = -static_cast<int32_t>(bytes.size());
146 std::memcpy(&framed[0], &raw_marker, sizeof(int32_t));
147 std::memcpy(&framed[sizeof(int32_t)], bytes.data(), bytes.size());
148 return framed;
149 }
150 catch (const std::exception& ex) {
151 std::cerr << "FATAL: serialization failed: " << ex.what() << std::endl;
152 MPI_Abort(MPI_COMM_WORLD, 1);
153 }
154 return {}; // unreachable — MPI_Abort does not return
155}
156
157void ParallelUtilities::send_bytes(const std::string& bytes, const int dest_rank, const int tag, MPI_Comm comm) {
158 // Send the size of the serialized payload, then the payload: the wire
159 // framing every receive_object expects.
160 int size = static_cast<int>(bytes.size());
161 MPI_Send(&size, 1, MPI_INT, dest_rank, tag, comm);
162 MPI_Send(bytes.data(), size, MPI_CHAR, dest_rank, tag, comm);
163}
164
165template<typename T>
166void ParallelUtilities::send_object(const T& obj, int dest_rank, int tag, MPI_Comm comm) {
167 send_bytes(serialize_object(obj), dest_rank, tag, comm);
168}
169
170template<typename T>
171void ParallelUtilities::receive_object(T& obj, int src_rank, int tag, MPI_Comm comm) {
172 // Receive the size of the serialized payload
173 int size = 0;
174 MPI_Recv(&size, 1, MPI_INT, src_rank, tag, comm, MPI_STATUS_IGNORE);
175
176 // Receive into an uninitialized buffer and deserialize in place: no
177 // zero-fill and no intermediate std::string or istringstream copy.
178 std::unique_ptr<char[]> buffer(new char[static_cast<size_t>(size)]);
179 MPI_Recv(buffer.get(), size, MPI_CHAR, src_rank, tag, comm, MPI_STATUS_IGNORE);
180
181 try {
182 // Strip the wire layer added by serialize_object. A negative size
183 // header marks an uncompressed frame (oversized streams, i.e. RELAP
184 // restart images, ship raw); deserialize those in place, zero-copy.
185 int32_t head = 0;
186 std::memcpy(&head, buffer.get(), sizeof(int32_t));
187 if (head < 0) {
188 const size_t raw_sz = static_cast<size_t>(-static_cast<int64_t>(head));
189 boost::iostreams::stream<boost::iostreams::array_source> is(
190 buffer.get() + sizeof(int32_t), raw_sz);
191 boost::archive::binary_iarchive ia(is);
192 ia >> obj;
193 } else {
194 auto raw = lz4_decompress(buffer.get(), size);
195 buffer.reset();
196 boost::iostreams::stream<boost::iostreams::array_source> is(raw.data(), raw.size());
197 boost::archive::binary_iarchive ia(is);
198 ia >> obj;
199 }
200 }
201 catch (const std::exception& ex) {
202 // A half-deserialized object would make the client simulate from torn
203 // state and report success, so abort the job instead.
204 std::cerr << "FATAL: deserialization failed (tag " << tag << " from rank "
205 << src_rank << "): " << ex.what() << std::endl;
206 MPI_Abort(comm, 1);
207 }
208}
209
210// Explicit instantiation for specific types
211template void ParallelUtilities::send_object<RestartNodeInfo>(const RestartNodeInfo& obj, int dest_rank, int tag, MPI_Comm comm);
212template void ParallelUtilities::send_object<BranchRecord>(const BranchRecord& obj, int dest_rank, int tag, MPI_Comm comm);
213template std::string ParallelUtilities::serialize_object<RestartNodeInfo>(const RestartNodeInfo& obj);
214template std::string ParallelUtilities::serialize_object<BranchRecord>(const BranchRecord& obj);
215//
216template void ParallelUtilities::receive_object<RestartNodeInfo>(RestartNodeInfo& obj, int src_rank, int tag, MPI_Comm comm);
217template void ParallelUtilities::receive_object<BranchRecord>(BranchRecord& obj, int src_rank, int tag, MPI_Comm comm);
218//
219template void ParallelUtilities::send_object<SequenceOutputData>(const SequenceOutputData& obj, int dest_rank, int tag, MPI_Comm comm);
220template void ParallelUtilities::receive_object<SequenceOutputData>(SequenceOutputData& obj, int src_rank, int tag, MPI_Comm comm);
221
222
223// TODO: add file-based and string stream information exchange
std::vector< char > lz4_compress(const char *data, int size)
LZ4-compress a buffer with a 4-byte original-size header.
std::vector< char > lz4_decompress(const char *data, int size)
LZ4-decompress a buffer that has a 4-byte original-size header.
Static MPI utility functions — colored logging, Boost object serialization over MPI.
bool log
Enable/disable rank-specific logging.
int size
Total number of MPI processes.
int rank
MPI rank of this process.
Per-sequence output payload sent from client to server via MPI.