Composable Thermo-Mechanics Tutorial

This demo builds a minimal composable thermo-mechanics problem. It registers solid and thermal fields, builds two systems, couples them with a thermoelastic material, and advances the combined system.

The full source code lives in examples/thermo_mechanics/composable_thermo_mechanics.cpp.

Includes and Initialization

#include "smith/infrastructure/application_manager.hpp"
#include "smith/physics/state/state_manager.hpp"
#include "smith/physics/mesh.hpp"
#include "smith/numerics/solver_config.hpp"
#include "smith/differentiable_numerics/nonlinear_block_solver.hpp"
#include "smith/differentiable_numerics/system_solver.hpp"
#include "smith/differentiable_numerics/solid_mechanics_system.hpp"
#include "smith/differentiable_numerics/thermal_system.hpp"
#include "smith/differentiable_numerics/thermo_mechanics_system.hpp"
#include "smith/differentiable_numerics/make_time_info_material.hpp"
#include "smith/differentiable_numerics/combined_system.hpp"
#include "smith/differentiable_numerics/differentiable_physics.hpp"
#include "smith/physics/materials/green_saint_venant_thermoelastic.hpp"
  smith::ApplicationManager application_manager(argc, argv);
  axom::sidre::DataStore datastore;
  smith::StateManager::initialize(datastore, "composable_thermo_mechanics");

Mesh Construction

  constexpr int dim = 3;
  constexpr int order = 1;

  auto mesh = std::make_shared<smith::Mesh>(
      mfem::Mesh::MakeCartesian3D(8, 2, 2, mfem::Element::HEXAHEDRON, 1.0, 0.1, 0.1), "mesh", 0, 0);
  mesh->addDomainOfBoundaryElements("left", smith::by_attr<dim>(3));
  mesh->addDomainOfBoundaryElements("right", smith::by_attr<dim>(5));

Field Registration

Registration declares all fields on one shared FieldStore before any system is built. Register parameters in the same phase with registerParameterFields(field_store, ...) and pass the returned bundle into the build step.

  smith::LinearSolverOptions linear_options{.linear_solver = smith::LinearSolver::SuperLU,
                                            .relative_tol = 1e-8,
                                            .absolute_tol = 1e-10,
                                            .max_iterations = 200,
                                            .print_level = 0};
  smith::NonlinearSolverOptions nonlinear_options{.nonlin_solver = smith::NonlinearSolver::NewtonLineSearch,
                                                  .relative_tol = 1e-7,
                                                  .absolute_tol = 1e-8,
                                                  .max_iterations = 20,
                                                  .max_line_search_iterations = 6,
                                                  .print_level = 0};

  auto field_store = std::make_shared<smith::FieldStore>(mesh, 100);

  auto solid_fields =
      smith::registerSolidMechanicsFields<dim, order, smith::QuasiStaticSecondOrderTimeIntegrationRule>(field_store);
  auto thermal_fields =
      smith::registerThermalFields<dim, order, smith::BackwardEulerFirstOrderTimeIntegrationRule>(field_store);

System Build and Coupling

The build step consumes each system's own field pack, then optional coupling and parameter bundles. combineSystems(...) returns the combined system used by makeDifferentiablePhysics(...).

  auto solid_solver =
      std::make_shared<smith::SystemSolver>(smith::buildNonlinearBlockSolver(nonlinear_options, linear_options, *mesh));
  auto thermal_solver =
      std::make_shared<smith::SystemSolver>(smith::buildNonlinearBlockSolver(nonlinear_options, linear_options, *mesh));

  auto solid_system = smith::buildSolidMechanicsSystem(solid_solver, smith::SolidMechanicsOptions{}, solid_fields,
                                                       smith::couplingFields(thermal_fields));
  auto thermal_system = smith::buildThermalSystem(thermal_solver, smith::ThermalOptions{}, thermal_fields,
                                                  smith::couplingFields(solid_fields));

  auto coupled_system = smith::combineSystems(solid_system, thermal_system);

  auto material = smith::makeTimeInfoMaterial(
      smith::thermomechanics::GreenSaintVenantThermoelasticMaterial{1.0, 100.0, 0.25, 1.0, 0.0025, 0.0, 0.05});
  smith::setCoupledThermoMechanicsMaterial(solid_system, thermal_system, material, mesh->entireBodyName());

Boundary Conditions and Loads

  solid_system->setDisplacementBC(mesh->domain("left"));
  thermal_system->setTemperatureBC(mesh->domain("left"), [](auto, auto) { return 1.0; });
  thermal_system->setTemperatureBC(mesh->domain("right"), [](auto, auto) { return 0.0; });

  solid_system->addTraction("right", [](double, auto X, auto, auto, auto, auto, auto... /*unused*/) {
    auto traction = 0.0 * X;
    traction[0] = -0.01;
    return traction;
  });

  thermal_system->addHeatSource(mesh->entireBodyName(), [](auto, auto, auto, auto... /*unused*/) { return 0.5; });

Advance the Coupled System

  auto physics = smith::makeDifferentiablePhysics(coupled_system, "composable_thermo_mechanics");
  for (int step = 0; step < 2; ++step) {
    physics->advanceTimestep(1.0);
  }

This demo is intentionally small. Use it as a template for:

  • adding parameter fields

  • enabling stress output

  • adding internal variables