7#if BOOST_VERSION < 107500
8#error "JsonConfigManager requires Boost 1.75 or newer"
14 static const std::string version =
15 std::to_string(MoFEM_VERSION_MAJOR) +
"." +
16 std::to_string(MoFEM_VERSION_MINOR);
22using JsonArray = boost::json::array;
23using JsonObject = boost::json::object;
24using JsonValue = boost::json::value;
26struct ParsedMeshEntry {
32constexpr int MAX_MESHSET_ATTRIBUTES = 10;
35 const std::string &file_name,
36 std::vector<ParsedMeshEntry> &meshes,
37 bool *has_meshsets =
nullptr);
39std::string toStdString(
const boost::json::string &value) {
40 return std::string(value.data(), value.size());
43std::string toLowerCopy(std::string value) {
44 std::transform(value.begin(), value.end(), value.begin(),
45 [](
unsigned char c) { return std::tolower(c); });
49std::string makeContext(
const std::string &parent,
const std::string &child) {
52 return parent +
"." + child;
55std::string jsonTypeName(
const JsonValue &value) {
58 if (value.is_object())
62 if (value.is_string())
66 if (value.is_int64() || value.is_uint64())
68 if (value.is_double())
74 const std::string &context,
75 const std::string &message) {
77 std::ostringstream oss;
78 if (!file_name.empty())
80 if (!context.empty()) {
81 if (!file_name.empty())
85 if (!message.empty()) {
86 if (!file_name.empty() || !context.empty())
94const JsonObject &requireObject(
const JsonValue &value,
95 const std::string &file_name,
96 const std::string &context) {
97 if (!value.is_object()) {
98 CHKERRABORT(PETSC_COMM_SELF,
99 failConfig(file_name, context,
100 "expected object, got " + jsonTypeName(value)));
102 return value.as_object();
105const JsonArray &requireArray(
const JsonValue &value,
const std::string &file_name,
106 const std::string &context) {
107 if (!value.is_array()) {
108 CHKERRABORT(PETSC_COMM_SELF,
109 failConfig(file_name, context,
110 "expected array, got " + jsonTypeName(value)));
112 return value.as_array();
115std::string requireString(
const JsonValue &value,
const std::string &file_name,
116 const std::string &context) {
117 if (!value.is_string()) {
118 CHKERRABORT(PETSC_COMM_SELF,
119 failConfig(file_name, context,
120 "expected string, got " + jsonTypeName(value)));
122 return toStdString(value.as_string());
125double requireNumber(
const JsonValue &value,
const std::string &file_name,
126 const std::string &context) {
127 if (value.is_double())
128 return value.as_double();
129 if (value.is_int64())
130 return static_cast<double>(value.as_int64());
131 if (value.is_uint64())
132 return static_cast<double>(value.as_uint64());
133 CHKERRABORT(PETSC_COMM_SELF,
134 failConfig(file_name, context,
135 "expected number, got " + jsonTypeName(value)));
139int requireInt(
const JsonValue &value,
const std::string &file_name,
140 const std::string &context) {
141 if (value.is_int64()) {
142 const auto val = value.as_int64();
143 if (val < std::numeric_limits<int>::min() ||
144 val > std::numeric_limits<int>::max()) {
147 failConfig(file_name, context,
"integer value out of int range"));
149 return static_cast<int>(val);
151 if (value.is_uint64()) {
152 const auto val = value.as_uint64();
153 if (val >
static_cast<std::uint64_t
>(std::numeric_limits<int>::max())) {
156 failConfig(file_name, context,
"integer value out of int range"));
158 return static_cast<int>(val);
160 CHKERRABORT(PETSC_COMM_SELF,
161 failConfig(file_name, context,
162 "expected integer, got " + jsonTypeName(value)));
166MoFEMErrorCode ensureBlocksetExists(MeshsetsManager &meshsets_manager,
int id,
167 const std::string &file_name,
168 const std::string &context) {
170 if (!meshsets_manager.checkMeshset(
id,
BLOCKSET)) {
171 std::ostringstream oss;
172 oss <<
"target BLOCKSET id " <<
id <<
" does not exist";
173 CHKERR failConfig(file_name, context, oss.str());
179 MeshsetsManager &meshsets_manager,
const std::string &config_file,
180 const std::set<int> &used_blockset_ids) {
183 std::vector<EntityHandle> handled_meshsets;
184 std::vector<int> blockset_ids;
185 std::vector<std::pair<int, std::string>> unused_blocksets;
187 for (
auto it = meshsets_manager.getBegin(); it != meshsets_manager.getEnd();
193 const auto meshset = it->getMeshset();
194 if (std::find(handled_meshsets.begin(), handled_meshsets.end(), meshset) !=
195 handled_meshsets.end()) {
198 handled_meshsets.push_back(meshset);
200 const auto meshset_id = it->getMeshsetId();
201 blockset_ids.push_back(meshset_id);
202 const bool is_used = used_blockset_ids.count(it->getMeshsetId()) != 0;
204 unused_blocksets.emplace_back(meshset_id, it->getName());
208 for (
const auto meshset_id : blockset_ids) {
209 if (used_blockset_ids.count(meshset_id) == 0) {
215 if (!unused_blocksets.empty()) {
216 std::ostringstream oss;
218 <<
": removing imported names/attributes from unused BLOCKSET meshsets:";
219 for (
const auto &entry : unused_blocksets) {
220 oss <<
" [id=" << entry.first;
221 if (!entry.second.empty()) {
222 oss <<
", name='" << entry.second <<
"'";
226 MOFEM_LOG(
"WORLD", Sev::warning) << oss.str();
232MoFEMErrorCode clearPetscParamFileOptionsForJson(
const std::string &config_file) {
235 char **argv = PETSC_NULLPTR;
236 CHKERR PetscGetArguments(&argv);
238 bool has_param_file_arg =
false;
239 std::vector<char *> filtered_argv;
240 for (PetscInt
i = 0; argv && argv[
i]; ++
i) {
241 if (std::strcmp(argv[
i],
"-param_file") == 0) {
242 has_param_file_arg =
true;
247 if (std::strcmp(argv[
i],
"-json_config") == 0) {
252 filtered_argv.push_back(argv[
i]);
256 if (has_param_file_arg) {
259 <<
": JSON config active, ignoring PETSc options loaded from -param_file";
260 }
else if (!petsc_options_file.empty()) {
262 << config_file <<
": JSON config active, ignoring PETSc options loaded "
263 <<
"from default PETSc options file '" << petsc_options_file <<
"'";
266 CHKERR PetscOptionsClear(PETSC_NULLPTR);
267 CHKERR PetscOptionsInsertArgs(PETSC_NULLPTR,
268 static_cast<int>(filtered_argv.size()),
269 filtered_argv.empty() ? PETSC_NULLPTR
270 : filtered_argv.data());
274std::string petscScalarToString(
const JsonValue &value,
275 const std::string &file_name,
276 const std::string &context) {
277 if (value.is_string())
278 return toStdString(value.as_string());
280 return value.as_bool() ?
"true" :
"false";
281 if (value.is_int64())
282 return boost::lexical_cast<std::string>(value.as_int64());
283 if (value.is_uint64())
284 return boost::lexical_cast<std::string>(value.as_uint64());
285 if (value.is_double()) {
286 std::ostringstream oss;
287 oss << std::setprecision(std::numeric_limits<double>::max_digits10)
288 << value.as_double();
291 CHKERRABORT(PETSC_COMM_SELF,
292 failConfig(file_name, context,
293 "PETSc option value must be scalar"));
297MoFEMErrorCode setPetscOptionIfUnset(
const std::string &option_name,
298 const char *option_value) {
300 PetscBool has_option = PETSC_FALSE;
301 CHKERR PetscOptionsHasName(PETSC_NULLPTR, PETSC_NULLPTR, option_name.c_str(),
304 CHKERR PetscOptionsSetValue(PETSC_NULLPTR, option_name.c_str(),
309MoFEMErrorCode setPetscOptionIfUnset(
const std::string &option_name,
310 const std::string &option_value) {
312 CHKERR setPetscOptionIfUnset(option_name, option_value.c_str());
317 const std::string &prefix,
318 const std::string &file_name,
319 const std::string &context,
320 std::map<std::string, std::string>
321 &seen_option_contexts) {
323 for (
const auto &item : obj) {
324 const auto key = toStdString(item.key());
325 const auto item_context = makeContext(context,
key);
326 const auto option_name =
327 prefix.empty() ?
"-" +
key : prefix +
"_" +
key;
329 if (item.value().is_object()) {
330 CHKERR flattenPetscObject(item.value().as_object(), option_name, file_name,
331 item_context, seen_option_contexts);
335 if (item.value().is_array() || item.value().is_null()) {
336 CHKERR failConfig(file_name, item_context,
337 "PETSc option value must be scalar or object");
340 if (
const auto seen_it = seen_option_contexts.find(option_name);
341 seen_it != seen_option_contexts.end()) {
343 << file_name <<
": duplicate PETSc option '" << option_name
344 <<
"' in JSON config; keeping first value from "
345 << seen_it->second <<
" and ignoring duplicate from " << item_context;
349 seen_option_contexts.emplace(option_name, item_context);
351 if (item.value().is_bool()) {
352 if (item.value().as_bool()) {
353 CHKERR setPetscOptionIfUnset(option_name, PETSC_NULLPTR);
355 CHKERR setPetscOptionIfUnset(option_name,
"false");
360 CHKERR setPetscOptionIfUnset(
361 option_name, petscScalarToString(item.value(), file_name, item_context));
387 : cOre(const_cast<
Core &>(core)) {}
396 std::ifstream input(file_name.c_str(), std::ifstream::in);
398 CHKERR failConfig(file_name,
"",
"unable to open JSON config file");
401 std::ostringstream buffer;
402 buffer << input.rdbuf();
404 boost::system::error_code ec;
405 auto root = boost::json::parse(buffer.str(), ec);
407 CHKERR failConfig(file_name,
"",
"invalid JSON: " + ec.message());
410 const auto &root_object = requireObject(root, file_name,
"root");
411 boost::json::value selected_config;
413 const auto &config_object = requireObject(selected_config, file_name,
"root");
415 bool has_version =
false;
416 for (
const auto &item : config_object) {
417 const auto key = toStdString(item.key());
418 if (
key ==
"$schema")
420 if (
key ==
"version") {
423 requireString(item.value(), file_name, makeContext(
"root",
key));
425 CHKERR failConfig(file_name, makeContext(
"root",
key),
426 "unsupported config version '" + version +
432 if (
key ==
"petsc") {
433 requireObject(item.value(), file_name, makeContext(
"root",
key));
436 if (
key ==
"mofem") {
437 requireObject(item.value(), file_name, makeContext(
"root",
key));
440 if (
key ==
"meshes" ||
key ==
"input_files") {
441 requireArray(item.value(), file_name, makeContext(
"root",
key));
444 if (
key ==
"file_name") {
446 file_name, makeContext(
"root",
key),
447 "legacy root-level file_name is not supported; use "
448 "root.input_files[0].file_name or root.meshes[0].file_name");
450 if (
key ==
"meshsets") {
452 file_name, makeContext(
"root",
key),
453 "legacy root-level meshsets is not supported; use "
454 "root.input_files[i].meshsets or root.meshes[i].meshsets");
456 CHKERR failConfig(file_name, makeContext(
"root",
key),
457 "unknown top-level key");
460 CHKERR failConfig(file_name,
"root.version",
461 "missing required version field");
465 std::vector<ParsedMeshEntry> new_meshes;
466 bool config_has_meshsets =
false;
468 &config_has_meshsets);
475 for (
const auto &entry : new_meshes) {
493 const auto &root_object =
configRoot.as_object();
494 std::map<std::string, std::string> seen_option_contexts;
495 const char *section_key =
nullptr;
496 if (
const auto *meshes_array =
498 meshes_array && !meshes_array->empty()) {
499 const auto primary_mesh_context =
meshContext(0, section_key);
500 const auto &primary_mesh = requireObject((*meshes_array)[0],
configFileName,
501 primary_mesh_context);
502 CHKERR setPetscOptionIfUnset(
505 makeContext(primary_mesh_context,
"file_name")));
508 if (
const auto petsc_it = root_object.find(
"petsc");
509 petsc_it != root_object.end()) {
510 CHKERR flattenPetscObject(petsc_it->value().as_object(),
"",
512 seen_option_contexts);
514 if (
const auto mofem_it = root_object.find(
"mofem");
515 mofem_it != root_object.end()) {
516 CHKERR flattenPetscObject(mofem_it->value().as_object(),
"",
518 seen_option_contexts);
529 if (
const auto *primary_meshsets =
531 primary_meshsets && !primary_meshsets->empty()) {
532 const char *section_key =
534 const auto primary_mesh_context =
meshContext(0, section_key);
535 std::vector<ParsedMeshsetEntry> parsed_primary_meshsets;
536 std::set<int> used_blockset_ids;
538 &meshsets_manager, parsed_primary_meshsets);
545 CHKERR discardResolvedBlocksetConfiguration(
552 const char *section_key =
nullptr;
553 if (
const auto *meshes_array =
556 for (
size_t mesh_index = 1; mesh_index != meshes_array->size();
558 if (
const auto *meshsets =
561 meshsets && !meshsets->empty()) {
588std::map<std::string, double>
590 int meshset_id)
const {
602 char json_file_name[PETSC_MAX_PATH_LEN] =
"";
603 PetscBool has_json = PETSC_FALSE;
605 json_file_name,
sizeof(json_file_name),
610 CHKERR clearPetscParamFileOptionsForJson(json_file_name);
614 char meshsets_cfg_file[PETSC_MAX_PATH_LEN] =
"";
615 PetscBool has_meshsets_cfg = PETSC_FALSE;
617 "-meshsets_config", meshsets_cfg_file,
618 sizeof(meshsets_cfg_file),
620 if (has_meshsets_cfg) {
623 "JSON meshsets cannot be used together with -meshsets_config");
std::string meshContext(const size_t mesh_index, const char *section_key=MESHES_KEY)
const JsonArray * getMeshsetsArray(const JsonObject &root_object, const std::string &file_name, const size_t mesh_index)
MoFEMErrorCode collectMeshes(const JsonObject &root_object, const std::string &file_name, std::vector< ParsedMeshEntry > &meshes, bool *has_meshsets)
bool isPythonScriptMeshType(const std::string &type)
const char * getMeshSectionKey(const JsonObject &root_object, const std::string &file_name)
const JsonArray * getMeshesArray(const JsonObject &root_object, const std::string &file_name, const char **section_key=nullptr)
constexpr auto MOAB_MESH_TYPE
MoFEMErrorCode validateMeshsets(const JsonArray &meshsets, const std::string &mesh_context, const std::string &file_name)
MoFEMErrorCode applyParsedMeshsets(MeshsetsManager &meshsets_manager, std::vector< ParsedMeshsetEntry > &parsed_meshsets, const std::string &file_name)
MoFEMErrorCode cacheResolvedBlocksetParams(const std::vector< ParsedMeshsetEntry > &parsed_meshsets, const std::string &file_name, BlocksetParamsByKey ¶ms_by_key, std::set< int > &used_blockset_ids)
MoFEMErrorCode parseMeshsets(const JsonArray &meshsets, const std::string &mesh_context, const std::string &file_name, MeshsetsManager *meshsets_manager_ptr, std::vector< ParsedMeshsetEntry > &parsed_meshsets)
MoFEMErrorCode cacheAppliedBlocksetParams(const std::vector< ParsedMeshsetEntry > &parsed_meshsets, const std::string &file_name, BlocksetParamsByKey ¶ms_by_key)
MoFEMErrorCode selectProgramConfig(const JsonObject &root_object, const std::string &file_name, boost::json::value &selected_config)
#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
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
#define MoFEMFunctionBeginHot
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
#define MOFEM_LOG(channel, severity)
Log.
FTensor::Index< 'i', SPACE_DIM > i
const double c
speed of light (cm/ns)
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
std::bitset< 32 > CubitBCType
implementation of Data Operators for Forces and Sources
PetscErrorCode PetscOptionsGetString(PetscOptions *, const char pre[], const char name[], char str[], size_t size, PetscBool *set)
static const std::string & getPetscOptionsFile()
static const std::string & supportedVersion()
MoFEMErrorCode applyMeshsets()
std::string getPythonScriptByKey(const std::string &key) const
std::map< std::string, std::string > moabMeshesByKey
MoFEMErrorCode applyPetscOptions()
std::string getMoabMeshByKey(const std::string &key) const
std::map< std::string, double > getParamsFromBlockset(const std::string &type_name, int meshset_id) const
MoFEMErrorCode query_interface(boost::typeindex::type_index type_index, UnknownInterface **iface) const
JsonConfigManager(const MoFEM::Core &core)
std::map< std::string, std::string > pythonScriptsByKey
MoFEMErrorCode loadFromFile(const std::string &file_name)
MoFEMErrorCode getSubInterfaceOptions()
std::map< MeshsetKey, MeshsetParams > primaryMeshsetParamsByKey
boost::json::value configRoot
MoFEMErrorCode loadFromCommandLine()
std::string configFileName
static MoFEMErrorCode getOptions()
Get logger option.
Interface for managing meshsets containing materials and boundary conditions.
base class for all interface classes
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.