#pragma once #include #include #include #include "drake/multibody/fem/damping_model.h" #include "drake/multibody/fem/fem_element.h" #include "drake/multibody/fem/linear_constitutive_model.h" namespace drake { namespace multibody { namespace fem { namespace internal { /* Forward declaration needed for defining the Traits below. */ class DummyElement; struct DummyData { using T = double; static constexpr int num_dofs = 12; double value{0}; Vector element_q; Vector element_v; Vector element_a; }; /* The traits for the DummyElement. In this case, all of the traits are unique values so we can detect that each value is used in the expected context. */ template <> struct FemElementTraits { using T = double; static constexpr int num_quadrature_points = 1; static constexpr int num_natural_dimension = 2; static constexpr int num_nodes = 4; /* See `DoComputeData` below on how this dummy data is updated. */ using Data = DummyData; static constexpr int num_dofs = Data::num_dofs; using ConstitutiveModel = LinearConstitutiveModel; }; /* A simple FemElement implementation. The calculation methods are implemented as returning a fixed value (which can independently be accessed by calling the corresponding dummy method -- e.g., CalcInverseDynamics() should return the value in inverse_dynamics_force(). */ class DummyElement final : public FemElement { public: using Base = FemElement; using Traits = FemElementTraits; using ConstitutiveModel = typename Traits::ConstitutiveModel; using T = typename Base::T; static constexpr int kNumDofs = Traits::num_dofs; DummyElement(const std::array& node_indices, ConstitutiveModel constitutive_model, DampingModel damping_model) : Base(node_indices, std::move(constitutive_model), std::move(damping_model)) {} /* Provides a fixed return value for CalcInverseDynamics(). */ static Vector inverse_dynamics_force() { return Vector::Constant(1.23456); } /* Creates an arbitrary SPD matrix. */ static Eigen::Matrix MakeSpdMatrix() { Eigen::Matrix A; for (int i = 0; i < kNumDofs; ++i) { for (int j = 0; j < kNumDofs; ++j) { A(i, j) = 2.7 * i + 3.1 * j; } } // A + A^T is guaranteed PSD. Adding the identity matrix to it makes it SPD. return (A + A.transpose()) + Eigen::Matrix::Identity(); } /* Provides a fixed return value for CalcStiffnessMatrix(). */ static Eigen::Matrix stiffness_matrix() { return 1.23 * MakeSpdMatrix(); } /* Provides a fixed return value for CalcMassMatrix(). */ static Eigen::Matrix mass_matrix() { return 7.89 * MakeSpdMatrix(); } /* Dummy element provides zero gravity force so that we have complete control over what the residual is. */ void AddScaledGravityForce(const Data&, const T&, const Vector3&, EigenPtr>) const {} private: /* Friend the base class so that the interface in the CRTP base class can access the private implementations of this class. */ friend Base; /* Implements FemElement::ComputeData(). Returns the sum of the last entries in each state. */ typename Traits::Data DoComputeData(const FemState& state) const { const int state_dofs = state.num_dofs(); const auto& q = state.GetPositions(); const auto& v = state.GetVelocities(); const auto& a = state.GetAccelerations(); typename Traits::Data data; data.value = q(state_dofs - 1); data.value += v(state_dofs - 1); data.value += a(state_dofs - 1); data.element_q = this->ExtractElementDofs(q); data.element_v = this->ExtractElementDofs(v); data.element_a = this->ExtractElementDofs(a); return data; } /* Implements FemElement::CalcInverseDynamics(). The inverse dynamics force is equal to a dummy nonzero value if the element has zero velocity and zero acceleration. Otherwise the force is zero.*/ void DoCalcInverseDynamics( const Data& data, EigenPtr> external_force) const { if (data.element_v.norm() == 0.0 && data.element_a.norm() == 0.0) { *external_force = this->inverse_dynamics_force(); } else { external_force->setZero(); } } /* Implements FemElement::AddScaledStiffnessMatrix(). */ void DoAddScaledStiffnessMatrix( const Data&, const T& scale, EigenPtr> K) const { *K += scale * stiffness_matrix(); } /* Implements FemElement::AddScaledMassMatrix(). */ void DoAddScaledMassMatrix( const Data&, const T& scale, EigenPtr> M) const { *M += scale * mass_matrix(); } }; } // namespace internal } // namespace fem } // namespace multibody } // namespace drake