v0.16.3
Loading...
Searching...
No Matches
EshelbianRestart.cpp
Go to the documentation of this file.
1/**
2 * @file EshelbianRestart.cpp
3 * @brief Checked sidecar metadata for native restart vectors.
4 */
5
6#include <MoFEM.hpp>
7using namespace MoFEM;
8
10#include <EshelbianRestart.hpp>
11
12#include <cerrno>
13#include <cstdio>
14#include <cstring>
15#include <filesystem>
16#include <fstream>
17#include <limits>
18#include <set>
19#include <sstream>
20
21namespace EshelbianPlasticity {
22namespace {
23
24using JsonObject = boost::json::object;
25
26// Complete rank-local I/O and validation before any rank returns an error.
27MoFEMErrorCode checkCollectiveLayoutError(MPI_Comm comm,
28 const std::string &local_error) {
30 int rank, mpi_size;
31 CHKERR MPI_Comm_rank(comm, &rank);
32 CHKERR MPI_Comm_size(comm, &mpi_size);
33 const int local_rank = local_error.empty() ? mpi_size : rank;
34 int error_rank = mpi_size;
35 CHKERR MPI_Allreduce(&local_rank, &error_rank, 1, MPI_INT, MPI_MIN, comm);
36 if (error_rank != mpi_size) {
37 std::string message;
38 if (rank == error_rank)
39 message = local_error.substr(0, 16384);
40 int length = static_cast<int>(message.size());
41 CHKERR MPI_Bcast(&length, 1, MPI_INT, error_rank, comm);
42 message.resize(length);
43 CHKERR MPI_Bcast(message.data(), length, MPI_CHAR, error_rank, comm);
44 SETERRQ(comm, MOFEM_DATA_INCONSISTENCY, "%s", message.c_str());
45 }
47}
48
49MoFEMErrorCode getRestartLayout(const EshelbianCore &ep, Vec state,
50 JsonObject &layout) {
52 const auto comm = ep.mField.get_comm();
53 CHKERR checkCollectiveLayoutError(
54 comm, state && ep.dmElastic
55 ? ""
56 : "Restart layout requires an elastic DM and vector");
57 int comparison = MPI_UNEQUAL;
58 CHKERR MPI_Comm_compare(
59 comm, PetscObjectComm(reinterpret_cast<PetscObject>(state)), &comparison);
60 CHKERR checkCollectiveLayoutError(
61 comm, comparison == MPI_IDENT || comparison == MPI_CONGRUENT
62 ? ""
63 : "Restart vector and elastic DM use different communicators");
64
65 int mpi_size;
66 CHKERR MPI_Comm_size(comm, &mpi_size);
67 PetscInt global_size, local_size;
68 CHKERR VecGetSize(state, &global_size);
69 CHKERR VecGetLocalSize(state, &local_size);
70 std::vector<PetscInt> partition_sizes(mpi_size);
71 CHKERR MPI_Allgather(&local_size, 1, MPIU_INT, partition_sizes.data(), 1,
72 MPIU_INT, comm);
73 boost::json::array partitions;
74 for (const auto size : partition_sizes)
75 partitions.emplace_back(static_cast<std::int64_t>(size));
76
77 const Problem *problem_ptr = nullptr;
78 CHKERR DMMoFEMGetProblemPtr(ep.dmElastic.get(), &problem_ptr);
79 CHKERR checkCollectiveLayoutError(
80 comm, problem_ptr && problem_ptr->getNumeredRowDofsPtr()
81 ? ""
82 : "Restart elastic DM has no numbered row DOFs");
83 CHKERR checkCollectiveLayoutError(
84 comm, problem_ptr->getNbDofsRow() == global_size
85 ? ""
86 : "Restart vector size differs from the elastic DM layout");
87
88 const auto &features = ep.physicalEquations->getFeatures();
89 layout["version"] = 1;
90 layout["formulation"] =
92 ? "auxiliary_logarithmic_stress"
94 ? "no_stretch"
95 : "direct_stretch");
96 // Native material IDs must remain independent of option-list positions.
97 const auto material_model = ep.physicalEquations->getMaterialModel();
98 layout["material_model"] = material_model == PhysicalEquations::Meta
99 ? 5
100 : static_cast<int>(material_model);
101 // Keep legacy restart values independent of the feature bit positions.
102 layout["stretch_handling"] =
104 ? 2
106 layout["stretch_selector"] = static_cast<int>(ep.stretchSelector);
107 layout["gradient_approximator"] = static_cast<int>(ep.gradApproximator);
108 layout["rotation_selector"] = static_cast<int>(ep.rotSelector);
109 layout["space_order"] = ep.spaceOrder;
110 layout["broken_hdiv_basis"] = ApproximationBaseNames[ep.brokenHdivBase];
111 layout["l2_user_base_scale"] = static_cast<bool>(ep.l2UserBaseScale);
112 layout["vector_global_size"] = static_cast<std::int64_t>(global_size);
113 layout["mpi_ranks"] = mpi_size;
114 layout["vector_partition_sizes"] = std::move(partitions);
115
116 std::set<std::string> field_names;
117 for (const auto &dof : *problem_ptr->getNumeredRowDofsPtr())
118 if (dof->getPetscGlobalDofIdx() >= 0)
119 field_names.insert(dof->getName());
120 std::string local_names;
121 for (const auto &name : field_names)
122 local_names += name + '\n';
123 CHKERR checkCollectiveLayoutError(
124 comm, local_names.size() <= std::numeric_limits<int>::max()
125 ? ""
126 : "Restart field names exceed the MPI metadata size limit");
127 const int local_length = static_cast<int>(local_names.size());
128 std::vector<int> lengths(mpi_size), offsets(mpi_size);
129 CHKERR MPI_Allgather(&local_length, 1, MPI_INT, lengths.data(), 1, MPI_INT,
130 comm);
131 std::size_t total_length = 0;
132 for (int rank = 0; rank != mpi_size; ++rank) {
133 offsets[rank] = static_cast<int>(total_length);
134 total_length += lengths[rank];
135 }
136 CHKERR checkCollectiveLayoutError(
137 comm, total_length <= std::numeric_limits<int>::max()
138 ? ""
139 : "Restart field layout exceeds the MPI metadata size limit");
140 std::string all_names(total_length, '\0');
141 CHKERR MPI_Allgatherv(local_names.data(), local_length, MPI_CHAR,
142 all_names.data(), lengths.data(), offsets.data(),
143 MPI_CHAR, comm);
144 std::istringstream names_stream(all_names);
145 for (std::string name; std::getline(names_stream, name);)
146 if (!name.empty())
147 field_names.insert(name);
148
149 const auto material_fields =
150 ep.physicalEquations->getMaterialFieldDefinitions(ep);
151 boost::json::array material_names;
152 for (const auto &field : material_fields) {
153 if (field.order < 0)
154 continue;
155 CHKERR checkCollectiveLayoutError(
156 comm, field_names.count(field.name)
157 ? ""
158 : "Restart material field has no active DOFs: " + field.name);
159 material_names.emplace_back(field.name);
160 }
161 layout["material_fields"] = std::move(material_names);
162
163 boost::json::array fields;
164 for (const auto &field_name : field_names) {
165 CHKERR checkCollectiveLayoutError(comm, ep.mField.check_field(field_name)
166 ? ""
167 : "Restart field is missing: " +
168 field_name);
169 const auto field_ptr = ep.mField.get_field_structure(field_name);
170 IS raw_is = nullptr;
172 field_name.c_str(), &raw_is);
173 SmartPetscObj<IS> field_is(raw_is);
174 PetscInt field_size, local_field_size;
175 CHKERR ISGetSize(field_is, &field_size);
176 CHKERR ISGetLocalSize(field_is, &local_field_size);
177 const auto &dofs =
178 problem_ptr->getNumeredRowDofsPtr()->get<PetscGlobalIdx_mi_tag>();
179 const PetscInt *indices = nullptr;
180 CHKERR ISGetIndices(field_is, &indices);
181 int minimum_order = std::numeric_limits<int>::max();
182 int maximum_order = -1;
183 std::string local_error;
184 for (PetscInt n = 0; n != local_field_size; ++n) {
185 const auto dof = dofs.find(indices[n]);
186 if (dof == dofs.end()) {
187 local_error = "Restart DM is missing a numbered DOF of " + field_name;
188 break;
189 }
190 const int order = (*dof)->getDofOrder();
191 minimum_order = std::min(minimum_order, order);
192 maximum_order = std::max(maximum_order, order);
193 }
194 CHKERR ISRestoreIndices(field_is, &indices);
195 CHKERR checkCollectiveLayoutError(comm, local_error);
196 CHKERR MPI_Allreduce(MPI_IN_PLACE, &minimum_order, 1, MPI_INT, MPI_MIN,
197 comm);
198 CHKERR MPI_Allreduce(MPI_IN_PLACE, &maximum_order, 1, MPI_INT, MPI_MAX,
199 comm);
200 CHKERR checkCollectiveLayoutError(
201 comm, field_size > 0 && minimum_order >= 0
202 ? ""
203 : "Restart field has no active DOFs: " + field_name);
204
205 JsonObject field{
206 {"name", field_name},
207 {"components", static_cast<int>(field_ptr->getNbOfCoeffs())},
208 {"space", field_ptr->getSpaceName()},
209 {"basis", field_ptr->getApproxBaseName()},
210 {"continuity", field_ptr->getContinuityName()},
211 {"minimum_dof_order", minimum_order},
212 {"maximum_dof_order", maximum_order},
213 {"global_dofs", static_cast<std::int64_t>(field_size)}};
214 const auto material_field = std::find_if(
215 material_fields.begin(), material_fields.end(), [&](const auto &entry) {
216 return entry.order >= 0 && entry.name == field_name;
217 });
218 if (material_field != material_fields.end()) {
219 field["order_policy"] = "FieldOrders::stretch(space_order)";
220 field["requested_order"] = material_field->order;
221 }
222 fields.emplace_back(std::move(field));
223 }
224 layout["fields"] = std::move(fields);
226}
227
228std::string compareRestartLayout(const boost::json::value &stored,
229 const JsonObject &expected,
230 const std::string &file_name) {
231 if (!stored.is_object())
232 return "Restart metadata is not a JSON object: " + file_name;
233 const auto &object = stored.as_object();
234 for (const auto &entry : expected) {
235 const auto stored_value = object.if_contains(entry.key());
236 if (!stored_value || *stored_value != entry.value())
237 return "Restart layout mismatch for '" +
238 std::string(entry.key().data(), entry.key().size()) + "' in " +
239 file_name +
240 "; use the formulation, fields, orders and MPI partition of the "
241 "saved native restart";
242 }
243 if (object.size() != expected.size())
244 return "Unexpected entries in restart layout metadata: " + file_name;
245 return "";
246}
247
248} // namespace
249
251 const std::string &binary_file) {
253 JsonObject layout;
254 CHKERR getRestartLayout(ep, state, layout);
255 const auto comm = ep.mField.get_comm();
256 int rank;
257 CHKERR MPI_Comm_rank(comm, &rank);
258 const std::string file_name = binary_file + ".layout.json";
259 std::string local_error;
260 if (rank == 0) {
261 const std::string temporary_file = file_name + ".tmp";
262 try {
263 std::ofstream output(temporary_file, std::ios::out | std::ios::trunc);
264 output << boost::json::serialize(layout) << '\n';
265 output.flush();
266 const bool written = output.good();
267 output.close();
268 if (!written || output.fail())
269 local_error = "Cannot write restart layout metadata: " + file_name;
270 else if (std::rename(temporary_file.c_str(), file_name.c_str()))
271 local_error = "Cannot install restart layout metadata " + file_name +
272 ": " + std::strerror(errno);
273 } catch (const std::exception &error) {
274 local_error = "Cannot write restart layout metadata " + file_name + ": " +
275 error.what();
276 }
277 if (!local_error.empty())
278 std::remove(temporary_file.c_str());
279 }
280 CHKERR checkCollectiveLayoutError(comm, local_error);
282}
283
285 const std::string &binary_file) {
287 const auto comm = ep.mField.get_comm();
288 int rank;
289 CHKERR MPI_Comm_rank(comm, &rank);
290 const std::string file_name = binary_file + ".layout.json";
291 int exists = 0;
292 std::string contents, local_error;
293 if (rank == 0) {
294 try {
295 std::error_code error;
296 exists = std::filesystem::exists(file_name, error);
297 if (error) {
298 local_error = "Cannot inspect restart layout metadata " + file_name +
299 ": " + error.message();
300 } else if (exists) {
301 std::ifstream input(file_name);
302 std::ostringstream buffer;
303 buffer << input.rdbuf();
304 if (!input.is_open() || input.bad() || buffer.fail())
305 local_error = "Cannot read restart layout metadata: " + file_name;
306 else
307 contents = buffer.str();
308 if (contents.size() >
309 static_cast<std::size_t>(std::numeric_limits<int>::max()))
310 local_error = "Restart layout metadata is too large: " + file_name;
311 }
312 } catch (const std::exception &error) {
313 local_error = "Cannot read restart layout metadata " + file_name + ": " +
314 error.what();
315 }
316 }
317 CHKERR checkCollectiveLayoutError(comm, local_error);
318 CHKERR MPI_Bcast(&exists, 1, MPI_INT, 0, comm);
319 if (!exists) {
320 CHKERR checkCollectiveLayoutError(
321 comm, ep.physicalEquations->getFeatures().test(
323 ? "Auxiliary logarithmic-stress restart requires " +
324 file_name +
325 "; an old raw stretch vector cannot be loaded into "
326 "the D/theta/Td fields"
327 : "");
329 }
330
331 int length = static_cast<int>(contents.size());
332 CHKERR MPI_Bcast(&length, 1, MPI_INT, 0, comm);
333 contents.resize(length);
334 CHKERR MPI_Bcast(contents.data(), length, MPI_CHAR, 0, comm);
335 JsonObject expected;
336 CHKERR getRestartLayout(ep, state, expected);
337 try {
338 boost::system::error_code parse_error;
339 const auto stored = boost::json::parse(contents, parse_error);
340 local_error = parse_error
341 ? "Invalid restart layout JSON in " + file_name + ": " +
342 parse_error.message()
343 : compareRestartLayout(stored, expected, file_name);
344 } catch (const std::exception &error) {
345 local_error = "Cannot validate restart layout metadata " + file_name +
346 ": " + error.what();
347 }
348 CHKERR checkCollectiveLayoutError(comm, local_error);
350}
351
352} // namespace EshelbianPlasticity
Eshelbian plasticity interface.
Native restart vector layout validation.
@ ROW
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
@ MOFEM_DATA_INCONSISTENCY
Definition definitions.h:31
static const char *const ApproximationBaseNames[]
Definition definitions.h:72
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
constexpr int order
const char features[]
PetscErrorCode DMMoFEMGetProblemPtr(DM dm, const MoFEM::Problem **problem_ptr)
Get pointer to problem data structure.
Definition DMMoFEM.cpp:422
PetscErrorCode DMMoFEMGetFieldIS(DM dm, RowColData rc, const char field_name[], IS *is)
get field is in the problem
Definition DMMoFEM.cpp:1507
virtual const Field * get_field_structure(const std::string &name, enum MoFEMTypes bh=MF_EXIST) const =0
get field structure
virtual bool check_field(const std::string &name) const =0
check if field is in database
const double n
refractive index of diffusive medium
MoFEM::MoFEMErrorCode writeRestartLayout(const EshelbianCore &ep, Vec state, const std::string &binary_file)
MoFEM::MoFEMErrorCode validateRestartLayout(const EshelbianCore &ep, Vec state, const std::string &binary_file)
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
constexpr auto field_name
static enum StretchSelector stretchSelector
MoFEM::Interface & mField
static PetscBool l2UserBaseScale
static enum RotSelector rotSelector
static enum RotSelector gradApproximator
static FieldApproximationBase brokenHdivBase
boost::shared_ptr< PhysicalEquations > physicalEquations
SmartPetscObj< DM > dmElastic
Elastic problem.
@ NO_STRETCH_LINEAR
No-stretch linear formulation.
@ AUXILIARY_LOGARITHMIC_STRESS
Auxiliary logarithmic stress formulation.
@ NO_STRETCH_NONLINEAR
No-stretch nonlinear formulation.
virtual MPI_Comm & get_comm() const =0
keeps basic data about problem
DofIdx getNbDofsRow() const
auto & getNumeredRowDofsPtr() const
get access to numeredRowDofsPtr storing DOFs on rows
intrusive_ptr for managing petsc objects