Program Listing for File elastodynamics_semidiscrete.h

Return to documentation for file (include/immersx/physics/elastodynamics_semidiscrete.h)

// ---------------------------------------------------------------------
//
// Copyright (C) 2026 by Luca Heltai
//
// This file is part of the ImmersX application, based on the deal.II
// library.
//
// ---------------------------------------------------------------------

#ifndef immersx_elastodynamics_semidiscrete_h
#define immersx_elastodynamics_semidiscrete_h

#include <immersx/algebra/local_preconditioner.h>
#include <immersx/core/contributor.h>
#include <immersx/core/semidiscrete_pde_models.h>
#include <immersx/physics/elastodynamics.h>

namespace ImmersX
{
  struct ElastodynamicsFields
  {
    FieldId displacement;
    FieldId velocity;
  };

  template <typename Builder, int dim, int spacedim = dim>
  ElastodynamicsFields
  contribute(Builder                                   &builder,
             const ElastodynamicsSolver<dim, spacedim> &problem)
  {
    using VectorType = typename ElastodynamicsSolver<dim, spacedim>::VectorType;

    const auto free_components =
      [](const dealii::IndexSet                  &owned,
         const dealii::AffineConstraints<double> &constraints) {
        dealii::IndexSet result(owned.size());
        for (const auto index : owned)
          if (!constraints.is_constrained(index))
            result.add_index(index);
        result.compress();
        return result;
      };

    const auto displacement =
      builder.field("displacement",
                    problem.locally_owned_dofs(),
                    problem.locally_relevant_dofs(),
                    free_components(problem.locally_owned_dofs(),
                                    problem.constraints()));
    const auto velocity =
      builder.field("velocity",
                    problem.locally_owned_dofs(),
                    problem.locally_relevant_dofs(),
                    free_components(problem.locally_owned_dofs(),
                                    problem.velocity_constraints()));

    const auto mass =
      ImmersX::matrix_operator<VectorType>(problem.mass_matrix());
    const auto stiffness =
      ImmersX::matrix_operator<VectorType>(problem.stiffness_matrix());
    const auto damping =
      ImmersX::matrix_operator<VectorType>(problem.damping_matrix());
    builder.preconditioner(
      displacement, [](const auto &linearized_matrix, const auto &prototype) {
        return make_amg_preconditioner(linearized_matrix, prototype);
      });
    builder.preconditioner(
      velocity, [](const auto &linearized_matrix, const auto &prototype) {
        return make_amg_preconditioner(linearized_matrix, prototype);
      });

    auto kinematic = builder.term(displacement, "kinematic");
    kinematic
      .residual([displacement, velocity, &problem, mass](const auto &context) {
        problem.update_constraints(context.time());
        return semidiscrete_detail::constrained_residual(
          mass.view * context.derivative(displacement) -
            mass.view * context.state(velocity),
          context.state(displacement),
          problem.constraints());
      })
      .state(velocity,
             semidiscrete_detail::constrained_matrix_operator(
               -1. * mass, problem.constraints()))
      .state(displacement,
             semidiscrete_detail::constrained_matrix_identity_operator(
               mass, problem.constraints()))
      .derivative(displacement,
                  semidiscrete_detail::constrained_matrix_operator(
                    mass, problem.constraints()));

    auto dynamics = builder.term(velocity, "dynamics");
    dynamics
      .residual([velocity, displacement, &problem, mass, stiffness, damping](
                  const auto &context) {
        problem.update_constraints(context.time());
        const auto &v_dot  = context.derivative(velocity);
        auto        result = mass.view * v_dot +
                      stiffness.view * context.state(displacement) +
                      damping.view * context.state(velocity);
        typename SemiDiscreteModel<VectorType>::Operation forcing;
        forcing.reinit_vector = [v_dot](VectorType &vector, const bool omit) {
          vector.reinit(v_dot, omit);
        };
        forcing.apply = [&problem, time = context.time()](VectorType &vector) {
          problem.body_force_at_time(time, vector);
        };
        forcing.apply_add = [&problem,
                             time = context.time()](VectorType &vector) {
          VectorType force;
          problem.body_force_at_time(time, force);
          vector += force;
        };
        return semidiscrete_detail::constrained_residual(
          result - forcing,
          context.state(velocity),
          problem.velocity_constraints());
      })
      .state(displacement,
             semidiscrete_detail::constrained_matrix_operator(
               stiffness, problem.velocity_constraints()))
      .state(velocity,
             semidiscrete_detail::constrained_matrix_operator_with_identity(
               damping, problem.velocity_constraints()))
      .derivative(velocity,
                  semidiscrete_detail::constrained_matrix_operator(
                    mass, problem.velocity_constraints()));

    return {displacement, velocity};
  }

  template <typename Adapter,
            typename Fields,
            int dim,
            int spacedim,
            typename GlobalVector>
  void
  initialize_elastodynamics_adapter_state(
    Adapter                                   &adapter,
    const Fields                              &fields,
    const ElastodynamicsSolver<dim, spacedim> &problem,
    GlobalVector                              &state,
    GlobalVector                              &state_dot)
  {
    adapter.field(state, fields.fields().displacement) = problem.displacement();
    adapter.field(state, fields.fields().velocity)     = problem.velocity();
    adapter.field(state_dot, fields.fields().displacement) = problem.velocity();
    typename ElastodynamicsSolver<dim, spacedim>::VectorType acceleration;
    problem.initial_acceleration(acceleration);
    adapter.field(state_dot, fields.fields().velocity) = acceleration;
  }
} // namespace ImmersX

#endif // immersx_elastodynamics_semidiscrete_h