Smith  0.1
Smith is an implicit thermal structural mechanics simulation code.
smith.cpp
Go to the documentation of this file.
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Smith Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
15 #include <fstream>
16 #include <iostream>
17 #include <memory>
18 #include <string>
19 
20 #include "axom/core.hpp"
21 #include "mfem.hpp"
22 
28 #include "smith/infrastructure/application_manager.hpp"
34 #include "smith/smith_config.hpp"
35 
36 namespace smith {
37 
43 void defineInputFileSchema(axom::inlet::Inlet& inlet)
44 {
45  // Simulation time parameters
46  inlet.addDouble("t_final", "Final time for simulation.").defaultValue(1.0);
47  inlet.addDouble("dt", "Time step.").defaultValue(0.25);
48 
49  // The mesh options
50  auto& mesh_table = inlet.addStruct("main_mesh", "The main mesh for the problem");
52 
53  // The solid mechanics options
54  auto& solid_solver_table = inlet.addStruct("solid", "Finite deformation solid mechanics module");
56 
57  // The heat transfer options
58  auto& heat_transfer_solver_table = inlet.addStruct("heat_transfer", "Heat transfer module");
59  HeatTransferInputOptions::defineInputFileSchema(heat_transfer_solver_table);
60 
61  // Verify the input file
62  if (!inlet.verify()) {
63  SLIC_ERROR_ROOT("Input file failed to verify.");
64  }
65 }
66 
67 } // namespace smith
68 
82 std::unique_ptr<smith::BasePhysics> createPhysics(
83  int dim, int order, std::optional<smith::SolidMechanicsInputOptions> solid_mechanics_options,
84  std::optional<smith::HeatTransferInputOptions> heat_transfer_options, std::string mesh_tag, int cycle, double t)
85 {
86  std::unique_ptr<smith::BasePhysics> main_physics;
87 
88  if (solid_mechanics_options) {
89  if (order == 1) {
90  if (dim == 2) {
91  main_physics =
92  std::make_unique<smith::SolidMechanics<1, 2>>(*solid_mechanics_options, "smith", mesh_tag, cycle, t);
93  } else if (dim == 3) {
94  main_physics =
95  std::make_unique<smith::SolidMechanics<1, 3>>(*solid_mechanics_options, "smith", mesh_tag, cycle, t);
96  }
97  } else if (order == 2) {
98  if (dim == 2) {
99  main_physics =
100  std::make_unique<smith::SolidMechanics<2, 2>>(*solid_mechanics_options, "smith", mesh_tag, cycle, t);
101  } else if (dim == 3) {
102  main_physics =
103  std::make_unique<smith::SolidMechanics<2, 3>>(*solid_mechanics_options, "smith", mesh_tag, cycle, t);
104  }
105  } else if (order == 3) {
106  if (dim == 2) {
107  main_physics =
108  std::make_unique<smith::SolidMechanics<3, 2>>(*solid_mechanics_options, "smith", mesh_tag, cycle, t);
109  } else if (dim == 3) {
110  main_physics =
111  std::make_unique<smith::SolidMechanics<3, 3>>(*solid_mechanics_options, "smith", mesh_tag, cycle, t);
112  }
113  }
114  } else if (heat_transfer_options) {
115  if (order == 1) {
116  if (dim == 2) {
117  main_physics = std::make_unique<smith::HeatTransfer<1, 2>>(*heat_transfer_options, "smith", mesh_tag, cycle, t);
118  } else if (dim == 3) {
119  main_physics = std::make_unique<smith::HeatTransfer<1, 3>>(*heat_transfer_options, "smith", mesh_tag, cycle, t);
120  }
121  } else if (order == 2) {
122  if (dim == 2) {
123  main_physics = std::make_unique<smith::HeatTransfer<2, 2>>(*heat_transfer_options, "smith", mesh_tag, cycle, t);
124  } else if (dim == 3) {
125  main_physics = std::make_unique<smith::HeatTransfer<2, 3>>(*heat_transfer_options, "smith", mesh_tag, cycle, t);
126  }
127  } else if (order == 3) {
128  if (dim == 2) {
129  main_physics = std::make_unique<smith::HeatTransfer<3, 2>>(*heat_transfer_options, "smith", mesh_tag, cycle, t);
130  } else if (dim == 3) {
131  main_physics = std::make_unique<smith::HeatTransfer<3, 3>>(*heat_transfer_options, "smith", mesh_tag, cycle, t);
132  }
133  }
134  } else {
135  SLIC_ERROR_ROOT("Neither solid or heat_transfer blocks specified in the input file.");
136  }
137  return main_physics;
138 }
139 
148 int getOrder(std::optional<smith::SolidMechanicsInputOptions> solid_mechanics_options,
149  std::optional<smith::HeatTransferInputOptions> heat_transfer_options)
150 {
151  int order = 0;
152  if (solid_mechanics_options) {
153  order = solid_mechanics_options->order;
154  } else if (heat_transfer_options) {
155  order = heat_transfer_options->order;
156  } else {
157  SLIC_ERROR_ROOT("Neither solid or heat_transfer blocks specified in the input file.");
158  }
159  SLIC_ERROR_ROOT_IF(order < 1 || order > 3,
160  std::format("Invalid solver order '{0}' given. Valid values are 1, 2, or 3.", order));
161  return order;
162 }
163 
172 int main(int argc, char* argv[])
173 {
174  smith::ApplicationManager applicationManager(argc, argv);
175 
176  // Handle Command line
177  std::unordered_map<std::string, std::string> cli_opts =
178  smith::cli::defineAndParse(argc, argv, "Smith: a high order nonlinear thermomechanical simulation code");
179 
180  // Optionally, print about info and quit
181  // TODO: add option for just version and a longer for about?
182  bool print_version = cli_opts.find("version") != cli_opts.end();
183  if (print_version) {
184  SLIC_INFO(smith::about());
185  return 0;
186  }
187 
188  // Output helpful run information
190  smith::cli::printGiven(cli_opts);
191 
192  // Read input file
193  std::string input_file_path = "";
194  auto search = cli_opts.find("input-file");
195  if (search != cli_opts.end()) {
196  input_file_path = search->second;
197  }
198 
199  // Output directory used for all files written to the file system.
200  // Example of outputted files:
201  //
202  // * Inlet docs + input file value file
203  // * StateManager state files
204  // * Summary file
205  std::string output_directory = "";
206  search = cli_opts.find("output-directory");
207  if (search != cli_opts.end()) {
208  output_directory = search->second;
209  }
210  axom::utilities::filesystem::makeDirsForPath(output_directory);
211 
212  search = cli_opts.find("paraview-directory");
213 
214  std::optional<std::string> paraview_output_dir = {};
215  if (search != cli_opts.end()) {
216  paraview_output_dir = search->second;
217  axom::utilities::filesystem::makeDirsForPath(*paraview_output_dir);
218  }
219 
220  // Check if a restart was requested
221  std::optional<int> restart_cycle;
222  if (auto cycle = cli_opts.find("restart-cycle"); cycle != cli_opts.end()) {
223  restart_cycle = std::stoi(cycle->second);
224  }
225 
226  // Create DataStore
227  axom::sidre::DataStore datastore;
228 
229  // Intialize MFEMSidreDataCollection
230  smith::StateManager::initialize(datastore, output_directory);
231 
232  // Initialize Inlet and read input file
233  auto inlet = smith::input::initialize(datastore, input_file_path);
235 
236  // Optionally, create input file documentation and quit
237  bool create_input_file_docs = cli_opts.find("create-input-file-docs") != cli_opts.end();
238  if (create_input_file_docs) {
239  std::string input_docs_path = axom::utilities::filesystem::joinPath(output_directory, "smith_input.rst");
240  inlet.write(axom::inlet::SphinxWriter(input_docs_path));
241  return 0;
242  }
243 
244  // Optionally, print unused entries in input file and quit
245  if (cli_opts.find("print-unused") != cli_opts.end()) {
246  const std::vector<std::string> all_unexpected_names = inlet.unexpectedNames();
247  if (all_unexpected_names.size() != 0) {
248  SLIC_INFO("Printing unused entries in input file:");
249  for (auto& x : all_unexpected_names) {
250  SLIC_INFO(" " << x);
251  }
252  } else {
253  SLIC_INFO("No unused entries in input file.");
254  }
255  return 0;
256  }
257 
258  // Save input values to file
259  std::string input_values_path = axom::utilities::filesystem::joinPath(output_directory, "smith_input_values.json");
260  datastore.getRoot()->getGroup("input_file")->save(input_values_path, "json");
261 
262  // Initialize/set the time information
263  double t = 0;
264  double t_final = inlet["t_final"];
265  double dt = inlet["dt"];
266  int cycle = 0;
267 
268  std::string mesh_tag{"mesh"};
269 
270  // Not restarting, so we need to create the mesh and register it with the StateManager
271  if (!restart_cycle) {
272  // Build the mesh
273  auto mesh_options = inlet["main_mesh"].get<smith::mesh::InputOptions>();
274  if (const auto file_opts = std::get_if<smith::mesh::FileInputOptions>(&mesh_options.extra_options)) {
275  file_opts->absolute_mesh_file_name =
276  smith::input::findMeshFilePath(file_opts->relative_mesh_file_name, input_file_path);
277  }
278  auto mesh = smith::mesh::buildParallelMesh(mesh_options);
279  smith::StateManager::setMesh(std::move(mesh), mesh_tag);
280  } else {
281  // If restart_cycle is non-empty, then this is a restart run and the data will be loaded here
282  t = smith::StateManager::load(*restart_cycle, mesh_tag);
283  cycle = *restart_cycle;
284  }
285 
286  // Create nullable containers for the solid and heat transfer input file options
287  std::optional<smith::SolidMechanicsInputOptions> solid_mechanics_options;
288  std::optional<smith::HeatTransferInputOptions> heat_transfer_options;
289 
290  // If the blocks exist, read the appropriate input file options
291  if (inlet.isUserProvided("solid")) {
292  solid_mechanics_options = inlet["solid"].get<smith::SolidMechanicsInputOptions>();
293  }
294  if (inlet.isUserProvided("heat_transfer")) {
295  heat_transfer_options = inlet["heat_transfer"].get<smith::HeatTransferInputOptions>();
296  }
297 
298  // Get dimension and order of problem
299  int dim = smith::StateManager::mesh(mesh_tag).Dimension();
300  SLIC_ERROR_ROOT_IF(dim < 2 || dim > 3,
301  std::format("Invalid mesh dimension '{0}' provided. Valid values are 2 or 3.", dim));
302  int order = getOrder(solid_mechanics_options, heat_transfer_options);
303 
304  // Create the physics object
305  auto main_physics = createPhysics(dim, order, solid_mechanics_options, heat_transfer_options, mesh_tag, cycle, t);
306 
307  // Complete the solver setup
308  main_physics->completeSetup();
309 
310  main_physics->initializeSummary(datastore, t_final, dt);
311 
312  // Enter the time step loop.
313  bool last_step = false;
314  while (!last_step) {
315  // Flush all messages held by the logger
316  smith::logger::flush();
317 
318  // Compute the real timestep. This may be less than dt for the last timestep.
319  double dt_real = std::min(dt, t_final - t);
320 
321  // Compute current time
322  t = t + dt_real;
323 
324  // Print the timestep information
325  SLIC_INFO_ROOT("step " << cycle << ", t = " << t);
326 
327  // Solve the physics module appropriately
328  main_physics->advanceTimestep(dt_real);
329 
330  // Output a visualization file
331  main_physics->outputStateToDisk(paraview_output_dir);
332 
333  // Save curve data to Sidre datastore to be output later
334  main_physics->saveSummary(datastore, t);
335 
336  // Determine if this is the last timestep
337  last_step = (t >= t_final - 1e-8 * dt);
338 
339  // Increment cycle
340  cycle++;
341  }
342 
343  // Output summary file (basic run info and curve data)
344  smith::output::outputSummary(datastore, output_directory);
345 
346  return 0;
347 }
This file contains the interface used for retrieving information about how the driver is configured.
RAII Application Manager class. Initializes MPI and other important libraries as well as automaticall...
static mfem::ParMesh & setMesh(std::unique_ptr< mfem::ParMesh > pmesh, const std::string &mesh_tag)
Gives ownership of mesh to StateManager.
static double load(const int cycle_to_load, const std::string &mesh_tag)
Loads an existing DataCollection.
static mfem::ParMesh & mesh(const std::string &mesh_tag)
Returns a non-owning reference to mesh held by StateManager.
static void initialize(axom::sidre::DataStore &ds, const std::string &output_directory)
Initializes the StateManager with a sidre DataStore (into which state will be written/read)
This file contains the all the necessary functions and macros required for interacting with the comma...
This file contains the declaration of an equation solver wrapper.
An object containing the solver for a heat transfer PDE.
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...
This file contains helper functions for importing and managing various mesh objects.
std::unordered_map< std::string, std::string > defineAndParse(int argc, char *argv[], std::string app_description)
Defines command line options and parses the found values.
Definition: cli.cpp:22
void printGiven(std::unordered_map< std::string, std::string > &cli_opts)
Prints all given command line options to the screen.
Definition: cli.cpp:107
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:22
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:48
std::unique_ptr< mfem::ParMesh > buildParallelMesh(const InputOptions &options, const MPI_Comm comm)
Constructs an MFEM parallel mesh from a set of input options.
Definition: mesh_utils.cpp:435
void outputSummary(const axom::sidre::DataStore &datastore, const std::string &output_directory, const FileFormat file_format)
Outputs simulation summary data from the datastore to the given file only on rank 0.
Definition: output.cpp:31
Accelerator functionality.
Definition: smith.cpp:36
std::string about()
Returns a string about the configuration of Smith.
Definition: about.cpp:56
void defineInputFileSchema(axom::inlet::Inlet &inlet)
Define the input file structure for the driver code.
Definition: smith.cpp:43
SMITH_HOST_DEVICE auto min(dual< gradient_type > a, double b)
Implementation of min for dual numbers.
Definition: dual.hpp:255
void printRunInfo()
Outputs basic run information to the screen.
Definition: about.cpp:194
This file contains the all the necessary functions for outputting simulation data.
std::unique_ptr< smith::BasePhysics > createPhysics(int dim, int order, std::optional< smith::SolidMechanicsInputOptions > solid_mechanics_options, std::optional< smith::HeatTransferInputOptions > heat_transfer_options, std::string mesh_tag, int cycle, double t)
Constructs the appropriate physics object using the input file options.
Definition: smith.cpp:82
int main(int argc, char *argv[])
The main smith driver code.
Definition: smith.cpp:172
int getOrder(std::optional< smith::SolidMechanicsInputOptions > solid_mechanics_options, std::optional< smith::HeatTransferInputOptions > heat_transfer_options)
Return and check correctness of the order of discretization.
Definition: smith.cpp:148
Tools for tagging a set of components of a vector field for boundary condition enforcement.
This file contains the declaration of the StateManager class.
Stores all information held in the input file that is used to configure the solver.
static void defineInputFileSchema(axom::inlet::Container &container)
Input file parameters specific to this class.
Stores all information held in the input file that is used to configure the solver.
static void defineInputFileSchema(axom::inlet::Container &container)
Input file parameters specific to this class.
Container for the mesh input options.
Definition: mesh_utils.hpp:27
static void defineInputFileSchema(axom::inlet::Container &container)
Input file parameters for mesh generation.
Definition: mesh_utils.cpp:406