Add native spherical harmonic surface coordinates to coupled equilibrium

This commit is contained in:
2026-09-19 08:51:42 -04:00
parent 2171b8b76a
commit 7b0eb8d92d
23 changed files with 3131 additions and 57 deletions

View File

@@ -172,6 +172,51 @@ export namespace mean_field::deformation {
return m_surface.parameterCount();
}
[[nodiscard]] bool hasNodalRadialParameters() const noexcept {
if constexpr (requires { PreparedSurface::hasNodalRadialParameters; }) {
return PreparedSurface::hasNodalRadialParameters;
}
return false;
}
[[nodiscard]] bool supportsSurfaceParameterMetric() const noexcept {
if constexpr (requires { PreparedSurface::hasCanonicalNodalVectorTrace; }) {
return PreparedSurface::hasCanonicalNodalVectorTrace;
}
return false;
}
// Reference surface metrics need the prescription tangent before any
// physical state has been prepared. Exposing that tangent avoids
// making normalization depend on the arbitrary interior mesh lift.
void applySurfaceJacobian(
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
mfem::Vector &surfaceDirection
) const {
requireCurrentDiscretization();
requireParameterSize(parameters);
requireParameterSize(parameterDirection);
if (surfaceDirection.Size() != surfaceDisplacementSize()) {
throw std::invalid_argument("The surface tangent output has an incompatible trace size.");
}
m_surface.applyJacobian(parameters, parameterDirection, surfaceDirection);
}
void applySurfaceJacobianTranspose(
const mfem::Vector &parameters,
const mfem::Vector &surfaceDual,
mfem::Vector &parameterDual
) const {
requireCurrentDiscretization();
requireParameterSize(parameters);
requireParameterSize(parameterDual);
if (surfaceDual.Size() != surfaceDisplacementSize()) {
throw std::invalid_argument("The surface transpose input has an incompatible trace size.");
}
m_surface.applyJacobianTranspose(parameters, surfaceDual, parameterDual);
}
[[nodiscard]] int surfaceDisplacementSize() const noexcept {
return m_surface.surfaceDisplacementSize();
}
@@ -580,6 +625,30 @@ export namespace mean_field::deformation {
return m_implementation->parameterCount();
}
[[nodiscard]] bool hasNodalRadialParameters() const noexcept {
return m_implementation->hasNodalRadialParameters();
}
[[nodiscard]] bool supportsSurfaceParameterMetric() const noexcept {
return m_implementation->supportsSurfaceParameterMetric();
}
void applySurfaceJacobian(
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
mfem::Vector &surfaceDirection
) const {
m_implementation->applySurfaceJacobian(parameters, parameterDirection, surfaceDirection);
}
void applySurfaceJacobianTranspose(
const mfem::Vector &parameters,
const mfem::Vector &surfaceDual,
mfem::Vector &parameterDual
) const {
m_implementation->applySurfaceJacobianTranspose(parameters, surfaceDual, parameterDual);
}
[[nodiscard]] int surfaceDisplacementSize() const noexcept {
return m_implementation->surfaceDisplacementSize();
}
@@ -654,10 +723,22 @@ export namespace mean_field::deformation {
private:
class Interface {
public:
virtual ~Interface() = default;
virtual ~Interface() = default;
[[nodiscard]] virtual DomainDeformationDescriptor descriptor() const noexcept = 0;
[[nodiscard]] virtual int parameterCount() const noexcept = 0;
[[nodiscard]] virtual DomainDeformationDescriptor descriptor() const noexcept = 0;
[[nodiscard]] virtual int parameterCount() const noexcept = 0;
[[nodiscard]] virtual bool hasNodalRadialParameters() const noexcept = 0;
[[nodiscard]] virtual bool supportsSurfaceParameterMetric() const noexcept = 0;
virtual void applySurfaceJacobian(
const mfem::Vector &,
const mfem::Vector &,
mfem::Vector &
) const = 0;
virtual void applySurfaceJacobianTranspose(
const mfem::Vector &,
const mfem::Vector &,
mfem::Vector &
) const = 0;
[[nodiscard]] virtual int surfaceDisplacementSize() const noexcept = 0;
[[nodiscard]] virtual int volumeDisplacementSize() const noexcept = 0;
[[nodiscard]] virtual bool matchesCurrentDiscretization() const noexcept = 0;
@@ -705,6 +786,46 @@ export namespace mean_field::deformation {
[[nodiscard]] int parameterCount() const noexcept override {
return m_preparedDeformation.parameterCount();
}
[[nodiscard]] bool hasNodalRadialParameters() const noexcept override {
if constexpr (requires { m_preparedDeformation.hasNodalRadialParameters(); }) {
return m_preparedDeformation.hasNodalRadialParameters();
}
return false;
}
[[nodiscard]] bool supportsSurfaceParameterMetric() const noexcept override {
if constexpr (requires(const PreparedDeformation &prepared, const mfem::Vector &x, mfem::Vector &y) {
{ prepared.supportsSurfaceParameterMetric() } noexcept -> std::same_as<bool>;
{ prepared.applySurfaceJacobian(x, x, y) } -> std::same_as<void>;
{ prepared.applySurfaceJacobianTranspose(x, x, y) } -> std::same_as<void>;
}) {
return m_preparedDeformation.supportsSurfaceParameterMetric();
}
return false;
}
void applySurfaceJacobian(
const mfem::Vector &parameters,
const mfem::Vector &direction,
mfem::Vector &trace
) const override {
if constexpr (requires { m_preparedDeformation.applySurfaceJacobian(parameters, direction, trace); }) {
m_preparedDeformation.applySurfaceJacobian(parameters, direction, trace);
} else {
throw std::logic_error("This domain deformation does not expose its surface tangent.");
}
}
void applySurfaceJacobianTranspose(
const mfem::Vector &parameters,
const mfem::Vector &trace,
mfem::Vector &dual
) const override {
if constexpr (requires {
m_preparedDeformation.applySurfaceJacobianTranspose(parameters, trace, dual);
}) {
m_preparedDeformation.applySurfaceJacobianTranspose(parameters, trace, dual);
} else {
throw std::logic_error("This domain deformation does not expose its surface transpose.");
}
}
[[nodiscard]] int surfaceDisplacementSize() const noexcept override {
return m_preparedDeformation.surfaceDisplacementSize();
}