Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
input.cpp
1 // Copyright (c) 2019-2024, Lawrence Livermore National Security, LLC and
2 // other Serac Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
8 
9 #include <stdlib.h>
10 #include <algorithm>
11 
12 #include "axom/core.hpp"
13 
17 
18 namespace serac::input {
19 
20 axom::inlet::Inlet initialize(axom::sidre::DataStore& datastore, const std::string& input_file_path,
21  const Language language, const std::string& sidre_path)
22 {
23  // Initialize Inlet
24  std::unique_ptr<axom::inlet::Reader> reader;
25  if (language == Language::Lua) {
26  reader = std::make_unique<axom::inlet::LuaReader>();
27  } else if (language == Language::JSON) {
28  reader = std::make_unique<axom::inlet::JSONReader>();
29  } else if (language == Language::YAML) {
30  reader = std::make_unique<axom::inlet::YAMLReader>();
31  }
32 
33  if (axom::utilities::filesystem::pathExists(input_file_path)) {
34  reader->parseFile(input_file_path);
35  }
36 
37  // Store inlet data under its own group
38  if (datastore.getRoot()->hasGroup(sidre_path)) {
39  // If this is a restart, wipe out the previous input file
40  datastore.getRoot()->destroyGroup(sidre_path);
41  }
42  axom::sidre::Group* inlet_root = datastore.getRoot()->createGroup(sidre_path);
43  return axom::inlet::Inlet(std::move(reader), inlet_root);
44 }
45 
46 std::string findMeshFilePath(const std::string& mesh_path, const std::string& input_file_path)
47 {
48  using namespace axom::utilities;
49 
50  // Check if given path exists
51  if (filesystem::pathExists(mesh_path)) {
52  return mesh_path;
53  }
54 
55  // Check relative to input file
56  std::string input_file_dir = fullDirectoryFromPath(input_file_path);
57  std::string possible_path = filesystem::joinPath(input_file_dir, mesh_path);
58  if (filesystem::pathExists(possible_path)) {
59  return possible_path;
60  }
61 
62  // Failed to find mesh file
63  std::string msg = axom::fmt::format("Input file: Given mesh file does not exist: '{0}'", mesh_path);
64  SLIC_ERROR_ROOT(msg);
65  return "";
66 }
67 
68 std::string fullDirectoryFromPath(const std::string& path)
69 {
70  char actualpath[PATH_MAX + 1];
71  char* ptr = realpath(path.c_str(), actualpath);
72  if (ptr == nullptr) {
73  SLIC_ERROR_ROOT("Failed to find absolute path from input file.");
74  }
75  std::string dir;
76  axom::utilities::filesystem::getDirName(dir, std::string(actualpath));
77  return dir;
78 }
79 
80 std::string getInputFileName(const std::string& file_path)
81 {
82  axom::Path path(file_path);
83  std::string basename = path.baseName();
84  std::string name;
85 
86  std::size_t index = basename.find_last_of(".");
87  if (index != std::string::npos) {
88  name = basename.substr(0, index);
89  } else {
90  name = basename;
91  }
92 
93  return name;
94 }
95 
96 void defineVectorInputFileSchema(axom::inlet::Container& container)
97 {
98  // TODO: I had to remove the required tag on x as we now have an optional vector input in the coefficients. IT would
99  // be nice to support "If this exists, this subcomponent is required."
100  container.addDouble("x", "x-component of vector");
101  container.addDouble("y", "y-component of vector");
102  container.addDouble("z", "z-component of vector");
103 }
104 
105 void BoundaryConditionInputOptions::defineInputFileSchema(axom::inlet::Container& container)
106 {
107  container.addIntArray("attrs", "Boundary attributes to which the BC should be applied");
109 }
110 
112 {
113  return vector_function || vector_constant || (!vector_pw_const.empty());
114 }
115 
116 std::unique_ptr<mfem::VectorCoefficient> CoefficientInputOptions::constructVector(const int dim) const
117 {
118  SLIC_ERROR_ROOT_IF(!isVector(), "Cannot construct a vector coefficient from scalar input");
119 
120  if (vector_function) {
121  return std::make_unique<mfem::VectorFunctionCoefficient>(dim, vector_function);
122  } else if (vector_constant) {
123  return std::make_unique<mfem::VectorConstantCoefficient>(*vector_constant);
124  } else if (!vector_pw_const.empty()) {
125  // Find the maximum mesh attribute
126  auto max_attr_elem = std::max_element(vector_pw_const.begin(), vector_pw_const.end(),
127  [](auto a, auto b) { return a.first < b.first; });
128 
129  // Create the vector array coefficient. We will use this as an array of piecewise constant scalars
130  auto vec_pw_coeff = std::make_unique<mfem::VectorArrayCoefficient>(max_attr_elem->second.Size());
131 
132  // Loop over each spatial dimension
133  for (int i = 0; i < max_attr_elem->second.Size(); ++i) {
134  // Create an mfem vector for the attributes
135  // Note that this vector expects zero indexing
136  mfem::Vector pw_constants(max_attr_elem->first);
137  pw_constants = 0.0;
138 
139  for (auto& entry : vector_pw_const) {
140  pw_constants(entry.first - 1) = entry.second[i];
141  }
142 
143  // Set the spatial dimension coefficient to a newly constructed scalar piecewise coefficient
144  vec_pw_coeff->Set(i, new mfem::PWConstCoefficient(pw_constants));
145  }
146  return vec_pw_coeff;
147 
148  } else {
149  SLIC_ERROR(
150  "Trying to build a vector coefficient without specifying a vector_function, vector_constant, or "
151  "vector_piecewise_constant.");
152  return nullptr;
153  }
154 }
155 
156 std::unique_ptr<mfem::Coefficient> CoefficientInputOptions::constructScalar() const
157 {
158  SLIC_ERROR_ROOT_IF(isVector(), "Cannot construct a scalar coefficient from vector input");
159 
160  if (scalar_function) {
161  return std::make_unique<mfem::FunctionCoefficient>(scalar_function);
162  } else if (scalar_constant) {
163  return std::make_unique<mfem::ConstantCoefficient>(*scalar_constant);
164  } else if (!scalar_pw_const.empty()) {
165  // First, find the element with the maximum attribute key
166  auto max_attr_elem = std::max_element(scalar_pw_const.begin(), scalar_pw_const.end(),
167  [](auto a, auto b) { return a.first < b.first; });
168 
169  // Check for a valid iterator to avoid compiler warnings
170  if (max_attr_elem != scalar_pw_const.end()) {
171  // Create an mfem vector for the attributes
172  // Note that this vector expects zero indexing
173  mfem::Vector pw_constants(max_attr_elem->first);
174  pw_constants = 0.0;
175 
176  for (auto& entry : scalar_pw_const) {
177  pw_constants(entry.first - 1) = entry.second;
178  }
179 
180  // Create the MFEM coefficient
181  return std::make_unique<mfem::PWConstCoefficient>(pw_constants);
182  }
183  }
184 
185  SLIC_ERROR_ROOT(
186  "Trying to build a scalar coefficient without specifying a scalar_function, constant, or piecewise_constant.");
187  return nullptr;
188 }
189 
190 void CoefficientInputOptions::defineInputFileSchema(axom::inlet::Container& container)
191 {
192  // Vectors are implemented as lua usertypes and can be converted to/from mfem::Vector
193  container.addFunction("vector_function", axom::inlet::FunctionTag::Vector,
194  {axom::inlet::FunctionTag::Vector, axom::inlet::FunctionTag::Double},
195  "The function to use for an mfem::VectorFunctionCoefficient");
196  container.addFunction("scalar_function", axom::inlet::FunctionTag::Double,
197  {axom::inlet::FunctionTag::Vector, axom::inlet::FunctionTag::Double},
198  "The function to use for an mfem::FunctionCoefficient");
199  container.addInt("component", "The vector component to which the scalar coefficient should be applied");
200 
201  container.addDouble("constant", "The constant scalar value to use as the coefficient");
202 
203  auto& vector_container = container.addStruct("vector_constant", "The constant vector to use as the coefficient");
205 
206  container.addDoubleArray("piecewise_constant",
207  "Map of mesh attributes to constant values to use as a piecewise coefficient");
208 
209  auto& pw_vector_container = container.addStructArray(
210  "vector_piecewise_constant", "Map of mesh attributes to constant vectors to use as a piecewise coefficient");
211  serac::input::defineVectorInputFileSchema(pw_vector_container);
212 }
213 
214 } // namespace serac::input
215 
216 mfem::Vector FromInlet<mfem::Vector>::operator()(const axom::inlet::Container& base)
217 {
218  mfem::Vector result(3); // Allocate up front since it's small
219  result[0] = base["x"];
220  if (base.contains("y")) {
221  result[1] = base["y"];
222  if (base.contains("z")) {
223  result[2] = base["z"];
224  } else {
225  result.SetSize(2); // Shrink to a 2D vector, leaving the data intact
226  }
227  } else {
228  result.SetSize(1); // Shrink to a 1D vector, leaving the data intact
229  }
230  return result;
231 }
232 
234  const axom::inlet::Container& base)
235 {
237  .coef_opts = base.get<serac::input::CoefficientInputOptions>()};
238  // Build a set with just the values of the map
239  auto bdr_attr_map = base["attrs"].get<std::unordered_map<int, int>>();
240  for (const auto& [_, val] : bdr_attr_map) {
241  result.attrs.insert(val);
242  }
243  return result;
244 }
245 
247  const axom::inlet::Container& base)
248 {
250 
251  // Create a counter for definition of the coefficient
252  int coefficient_definitions = 0;
253 
254  // Check if functions have been assigned and store them appropriately
255  if (base.contains("vector_function")) {
256  auto func = base["vector_function"]
257  .get<std::function<axom::inlet::FunctionType::Vector(axom::inlet::FunctionType::Vector, double)>>();
258  result.vector_function = [func(std::move(func))](const mfem::Vector& input, double t, mfem::Vector& output) {
259  auto ret = func(axom::inlet::FunctionType::Vector{input.GetData(), input.Size()}, t);
260  // Copy from the primal vector into the MFEM vector
261  std::copy(ret.vec.data(), ret.vec.data() + input.Size(), output.GetData());
262  };
263  coefficient_definitions++;
264  }
265 
266  if (base.contains("scalar_function")) {
267  auto func = base["scalar_function"].get<std::function<double(axom::inlet::FunctionType::Vector, double)>>();
268  result.scalar_function = [func(std::move(func))](const mfem::Vector& input, double t) {
269  return func(axom::inlet::FunctionType::Vector{input.GetData(), input.Size()}, t);
270  };
271  coefficient_definitions++;
272  }
273 
274  if (base.contains("constant")) {
275  result.scalar_constant = base["constant"];
276  coefficient_definitions++;
277  }
278 
279  if (base.contains("vector_constant")) {
280  result.vector_constant = base["vector_constant"].get<mfem::Vector>();
281  coefficient_definitions++;
282  }
283 
284  if (base.contains("piecewise_constant")) {
285  result.scalar_pw_const = base["piecewise_constant"].get<std::unordered_map<int, double>>();
286  coefficient_definitions++;
287  }
288 
289  if (base.contains("vector_piecewise_constant")) {
290  result.vector_pw_const = base["vector_piecewise_constant"].get<std::unordered_map<int, mfem::Vector>>();
291  coefficient_definitions++;
292  }
293 
294  // If scalar valued, check of a component
295  if (result.scalar_constant || result.scalar_function || !result.scalar_pw_const.empty()) {
296  // If component input exists, set it in the option struct
297  if (base.contains("component")) {
298  result.component = base["component"];
299  }
300  }
301 
302  SLIC_ERROR_ROOT_IF(coefficient_definitions > 1,
303  "Coefficient has multiple definitions. Please use only one of (constant, vector_constant, "
304  "piecewise_constant, vector_piecewise_constant, scalar_function, vector_function");
305  SLIC_ERROR_ROOT_IF(coefficient_definitions == 0, "Coefficient definition does not contain known type.");
306 
307  return result;
308 }
This file contains the all the necessary functions for reading input files.
This file contains the all the necessary functions and macros required for logging as well as a helpe...
The input related helper functions and objects.
Definition: input.cpp:18
Language
The input file languages supported by Inlet.
Definition: input.hpp:33
axom::inlet::Inlet initialize(axom::sidre::DataStore &datastore, const std::string &input_file_path, const Language language, const std::string &sidre_path)
Initializes Inlet with the given datastore and input file.
Definition: input.cpp:20
void defineVectorInputFileSchema(axom::inlet::Container &container)
Defines the schema for a vector in R^{1,2,3} space.
Definition: input.cpp:96
std::string findMeshFilePath(const std::string &mesh_path, const std::string &input_file_path)
Returns the absolute path of the given mesh either relative to CWD or the input file.
Definition: input.cpp:46
std::string fullDirectoryFromPath(const std::string &path)
Returns the absolute directory of the given file path.
Definition: input.cpp:68
std::string getInputFileName(const std::string &file_path)
Returns the name of the input file (base name with file extension removed).
Definition: input.cpp:80
This file contains enumerations and record types for physics solver configuration.
mfem::Vector operator()(const axom::inlet::Container &base)
Returns created object from Inlet container.
Definition: input.cpp:216
serac::input::BoundaryConditionInputOptions operator()(const axom::inlet::Container &base)
Returns created object from Inlet container.
Definition: input.cpp:233
serac::input::CoefficientInputOptions operator()(const axom::inlet::Container &base)
Returns created object from Inlet container.
Definition: input.cpp:246
The information required from the input file for a boundary condition.
Definition: input.hpp:155
std::set< int > attrs
The mesh attributes on which to apply the boundary condition.
Definition: input.hpp:159
static void defineInputFileSchema(axom::inlet::Container &container)
Input file parameters specific to this class.
Definition: input.cpp:105
The information required from the input file for an mfem::(Vector)(Function)Coefficient.
Definition: input.hpp:88
std::unordered_map< int, mfem::Vector > vector_pw_const
Vector piecewise constant definition map.
Definition: input.hpp:128
static void defineInputFileSchema(axom::inlet::Container &container)
Defines the input file schema on the provided inlet container.
Definition: input.cpp:190
bool isVector() const
Returns whether the contained function corresponds to a vector coefficient.
Definition: input.cpp:111
std::unordered_map< int, double > scalar_pw_const
Scalar piecewise constant definition map.
Definition: input.hpp:123
VecFunc vector_function
The vector std::function corresponding to a function coefficient.
Definition: input.hpp:108
std::optional< int > component
The component to which a scalar coefficient should be applied.
Definition: input.hpp:133
std::optional< double > scalar_constant
The scalar constant associated with the coefficient.
Definition: input.hpp:113
std::unique_ptr< mfem::VectorCoefficient > constructVector(const int dim=3) const
Constructs a vector coefficient with the requested dimension.
Definition: input.cpp:116
std::optional< mfem::Vector > vector_constant
The vector constant associated with the coefficient.
Definition: input.hpp:118
ScalarFunc scalar_function
The scalar std::function corresponding to a function coefficient.
Definition: input.hpp:103
std::unique_ptr< mfem::Coefficient > constructScalar() const
Constructs a scalar coefficient.
Definition: input.cpp:156
Helper functions for exiting Serac cleanly.