Files

871 lines
42 KiB
Plaintext
Raw Permalink Normal View History

module;
#include <algorithm>
#include <cmath>
#include <compare>
#include <concepts>
#include <cstdint>
#include <limits>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
#include <mfem.hpp>
#include <mpi.h>
export module mean_field:deformation.domain_deformation;
export import :deformation.interior_extension;
export import :deformation.nodal_radial_surface;
export import :deformation.radial_extensions;
export import :deformation.surface_prescription;
export import :deformation.vacuum_extension;
export import :fem;
export import :field.mfem;
export import :utils.domain;
export namespace mean_field::deformation {
enum class VolumeDeformationOwner : std::uint8_t { StellarInterior, Vacuum };
struct DomainDeformationDiscretizationDependencies final {
const mfem::Mesh *physicalMeshIdentity{nullptr};
const mfem::ParMesh *logicalReferenceMeshIdentity{nullptr};
const mfem::ParFiniteElementSpace *surfaceScalarSpaceIdentity{nullptr};
const mfem::ParFiniteElementSpace *volumeDisplacementSpaceIdentity{nullptr};
long physicalMeshSequence{-1};
long logicalReferenceMeshSequence{-1};
long surfaceScalarSpaceSequence{-1};
long volumeDisplacementSpaceSequence{-1};
[[nodiscard]] bool isCurrent() const noexcept {
return physicalMeshIdentity != nullptr && logicalReferenceMeshIdentity != nullptr &&
surfaceScalarSpaceIdentity != nullptr && volumeDisplacementSpaceIdentity != nullptr &&
physicalMeshIdentity->GetSequence() == physicalMeshSequence &&
logicalReferenceMeshIdentity->GetSequence() == logicalReferenceMeshSequence &&
surfaceScalarSpaceIdentity->GetSequence() == surfaceScalarSpaceSequence &&
volumeDisplacementSpaceIdentity->GetSequence() == volumeDisplacementSpaceSequence;
}
};
struct DomainDeformationCompositionReport final {
int scalarTrueDofCount{0};
int stellarInteriorOwnedScalarDofCount{0};
int vacuumOwnedScalarDofCount{0};
int sharedSurfaceScalarDofCount{0};
[[nodiscard]] constexpr int assignedScalarDofCount() const noexcept {
return stellarInteriorOwnedScalarDofCount + vacuumOwnedScalarDofCount;
}
constexpr auto operator<=>(const DomainDeformationCompositionReport &) const = default;
};
struct DomainDeformationGeometryReport final {
double minimumJacobianDeterminant{std::numeric_limits<double>::infinity()};
[[nodiscard]] bool isOrientationPreserving(const double determinantFloor = 0.0) const noexcept {
return std::isfinite(minimumJacobianDeterminant) && std::isfinite(determinantFloor) &&
determinantFloor >= 0.0 && minimumJacobianDeterminant > determinantFloor;
}
};
struct PreparedDomainDeformationActionStatistics final {
std::uint64_t volumeBuildApplications{0};
std::uint64_t jacobianApplications{0};
std::uint64_t jacobianTransposeApplications{0};
std::uint64_t pullbackDerivativeApplications{0};
std::uint64_t geometryInspections{0};
constexpr auto operator<=>(const PreparedDomainDeformationActionStatistics &) const = default;
};
template <typename Candidate>
concept PreparedDomainDeformationOperator = requires(
const std::remove_cvref_t<Candidate> &preparedDeformation,
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
const mfem::Vector &volumeDisplacementDual,
mfem::Vector &volumeDisplacement,
mfem::Vector &parameterDual
) {
{ preparedDeformation.descriptor() } noexcept -> std::same_as<DomainDeformationDescriptor>;
{ preparedDeformation.parameterCount() } noexcept -> std::same_as<int>;
{ preparedDeformation.surfaceDisplacementSize() } noexcept -> std::same_as<int>;
{ preparedDeformation.volumeDisplacementSize() } noexcept -> std::same_as<int>;
{ preparedDeformation.buildVolumeDisplacement(parameters, volumeDisplacement) } -> std::same_as<void>;
{ preparedDeformation.applyJacobian(parameters, parameterDirection, volumeDisplacement) } -> std::same_as<void>;
{
preparedDeformation.applyJacobianTranspose(parameters, volumeDisplacementDual, parameterDual)
} -> std::same_as<void>;
{
preparedDeformation.applyPullbackDerivative(
parameters, parameterDirection, volumeDisplacementDual, parameterDual
)
} -> std::same_as<void>;
};
template <
PreparedSurfaceDeformationPrescription PreparedSurface,
PreparedInteriorDeformationExtension PreparedInterior,
PreparedVacuumDeformationExtension PreparedVacuum>
class PreparedDomainDeformation final {
public:
PreparedDomainDeformation(
PreparedSurface preparedSurface,
PreparedInterior preparedInterior,
PreparedVacuum preparedVacuum,
mfem::ParFiniteElementSpace &surfaceScalarSpace,
mfem::ParFiniteElementSpace &volumeDisplacementSpace,
mfem::ParMesh &logicalReferenceMesh
)
: m_surface(std::move(preparedSurface)),
m_interior(std::move(preparedInterior)),
m_vacuum(std::move(preparedVacuum)),
m_volumeDisplacementSpace(&volumeDisplacementSpace),
m_descriptor(makeDescriptor(
m_surface,
m_interior,
m_vacuum
)),
m_surfaceDisplacementWorkspace(surfaceDisplacementSize()),
m_surfaceDirectionWorkspace(surfaceDisplacementSize()),
m_interiorVolumeWorkspace(volumeDisplacementSize()),
m_vacuumVolumeWorkspace(volumeDisplacementSize()),
m_interiorVolumeDualWorkspace(volumeDisplacementSize()),
m_vacuumVolumeDualWorkspace(volumeDisplacementSize()),
m_interiorSurfaceDualWorkspace(surfaceDisplacementSize()),
m_vacuumSurfaceDualWorkspace(surfaceDisplacementSize()),
m_surfaceDualWorkspace(surfaceDisplacementSize()),
m_interiorSurfacePullbackWorkspace(surfaceDisplacementSize()),
m_vacuumSurfacePullbackWorkspace(surfaceDisplacementSize()),
m_surfacePullbackWorkspace(surfaceDisplacementSize()),
m_parameterPullbackWorkspace(parameterCount()),
m_volumeGridFunctionWorkspace(std::make_unique<mfem::ParGridFunction>(&volumeDisplacementSpace)) {
validateCompatibility(surfaceScalarSpace, volumeDisplacementSpace, logicalReferenceMesh);
compileOwnership();
const mfem::Mesh *physicalMesh = volumeDisplacementSpace.GetMesh();
m_discretizationDependencies = {
.physicalMeshIdentity = physicalMesh,
.logicalReferenceMeshIdentity = &logicalReferenceMesh,
.surfaceScalarSpaceIdentity = &surfaceScalarSpace,
.volumeDisplacementSpaceIdentity = &volumeDisplacementSpace,
.physicalMeshSequence = physicalMesh->GetSequence(),
.logicalReferenceMeshSequence = logicalReferenceMesh.GetSequence(),
.surfaceScalarSpaceSequence = surfaceScalarSpace.GetSequence(),
.volumeDisplacementSpaceSequence = volumeDisplacementSpace.GetSequence()
};
}
PreparedDomainDeformation(const PreparedDomainDeformation &) = delete;
PreparedDomainDeformation &operator=(const PreparedDomainDeformation &) = delete;
PreparedDomainDeformation(PreparedDomainDeformation &&) noexcept = default;
PreparedDomainDeformation &operator=(PreparedDomainDeformation &&) noexcept = default;
[[nodiscard]] DomainDeformationDescriptor descriptor() const noexcept {
return m_descriptor;
}
[[nodiscard]] int parameterCount() const noexcept {
return m_surface.parameterCount();
}
[[nodiscard]] int surfaceDisplacementSize() const noexcept {
return m_surface.surfaceDisplacementSize();
}
[[nodiscard]] int volumeDisplacementSize() const noexcept {
return m_interior.interiorDisplacementSize();
}
[[nodiscard]] int scalarTrueDofCount() const noexcept {
return m_interior.scalarTrueDofCount();
}
[[nodiscard]] int spatialDimension() const noexcept {
return m_descriptor.surfaceDeformation.spatialDimension;
}
[[nodiscard]] VolumeDeformationOwner volumeOwner(const int scalarTrueDof) const {
requireScalarTrueDof(scalarTrueDof);
return m_volumeOwners[static_cast<std::size_t>(scalarTrueDof)];
}
[[nodiscard]] bool isSharedSurfaceDof(const int scalarTrueDof) const {
requireScalarTrueDof(scalarTrueDof);
return m_interior.hasStellarSupport(scalarTrueDof) && m_vacuum.hasVacuumSupport(scalarTrueDof);
}
[[nodiscard]] const DomainDeformationCompositionReport &compositionReport() const noexcept {
return m_compositionReport;
}
[[nodiscard]] const DomainDeformationDiscretizationDependencies &discretizationDependencies() const noexcept {
return m_discretizationDependencies;
}
[[nodiscard]] bool matchesCurrentDiscretization() const noexcept {
return m_discretizationDependencies.isCurrent();
}
[[nodiscard]] const PreparedDomainDeformationActionStatistics &actionStatistics() const noexcept {
return m_actionStatistics;
}
[[nodiscard]] const PreparedSurface &surfaceDeformationPrescription() const noexcept {
return m_surface;
}
[[nodiscard]] const PreparedInterior &stellarInteriorExtension() const noexcept {
return m_interior;
}
[[nodiscard]] const PreparedVacuum &vacuumExtension() const noexcept {
return m_vacuum;
}
void buildVolumeDisplacement(
const mfem::Vector &parameters,
mfem::Vector &volumeDisplacement
) const {
requireCurrentDiscretization();
requireParameterSize(parameters);
requireVolumeSize(volumeDisplacement);
m_surface.buildSurfaceDisplacement(parameters, m_surfaceDisplacementWorkspace);
m_interior.buildInteriorDisplacement(m_surfaceDisplacementWorkspace, m_interiorVolumeWorkspace);
m_vacuum.buildVacuumDisplacement(m_surfaceDisplacementWorkspace, m_vacuumVolumeWorkspace);
mergeVolumeFields(m_interiorVolumeWorkspace, m_vacuumVolumeWorkspace, volumeDisplacement);
++m_actionStatistics.volumeBuildApplications;
}
void applyJacobian(
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
mfem::Vector &volumeDisplacementDirection
) const {
requireCurrentDiscretization();
requireParameterSize(parameters);
requireParameterSize(parameterDirection);
requireVolumeSize(volumeDisplacementDirection);
m_surface.buildSurfaceDisplacement(parameters, m_surfaceDisplacementWorkspace);
m_surface.applyJacobian(parameters, parameterDirection, m_surfaceDirectionWorkspace);
m_interior.applyJacobian(
m_surfaceDisplacementWorkspace, m_surfaceDirectionWorkspace, m_interiorVolumeWorkspace
);
m_vacuum.applyJacobian(
m_surfaceDisplacementWorkspace, m_surfaceDirectionWorkspace, m_vacuumVolumeWorkspace
);
mergeVolumeFields(m_interiorVolumeWorkspace, m_vacuumVolumeWorkspace, volumeDisplacementDirection);
++m_actionStatistics.jacobianApplications;
}
void applyJacobianTranspose(
const mfem::Vector &parameters,
const mfem::Vector &volumeDisplacementDual,
mfem::Vector &parameterDual
) const {
requireCurrentDiscretization();
requireParameterSize(parameters);
requireVolumeSize(volumeDisplacementDual);
requireParameterSize(parameterDual);
m_surface.buildSurfaceDisplacement(parameters, m_surfaceDisplacementWorkspace);
splitVolumeDual(volumeDisplacementDual);
applyExtensionTransposes();
m_surface.applyJacobianTranspose(parameters, m_surfaceDualWorkspace, parameterDual);
++m_actionStatistics.jacobianTransposeApplications;
}
void applyPullbackDerivative(
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
const mfem::Vector &volumeDisplacementDual,
mfem::Vector &parameterDualAction
) const {
requireCurrentDiscretization();
requireParameterSize(parameters);
requireParameterSize(parameterDirection);
requireVolumeSize(volumeDisplacementDual);
requireParameterSize(parameterDualAction);
m_surface.buildSurfaceDisplacement(parameters, m_surfaceDisplacementWorkspace);
m_surface.applyJacobian(parameters, parameterDirection, m_surfaceDirectionWorkspace);
splitVolumeDual(volumeDisplacementDual);
applyExtensionTransposes();
m_interior.applyPullbackDerivative(
m_surfaceDisplacementWorkspace, m_surfaceDirectionWorkspace, m_interiorVolumeDualWorkspace,
m_interiorSurfacePullbackWorkspace
);
m_vacuum.applyPullbackDerivative(
m_surfaceDisplacementWorkspace, m_surfaceDirectionWorkspace, m_vacuumVolumeDualWorkspace,
m_vacuumSurfacePullbackWorkspace
);
addSurfaceFields(
m_interiorSurfacePullbackWorkspace, m_vacuumSurfacePullbackWorkspace, m_surfacePullbackWorkspace
);
m_surface.applyJacobianTranspose(parameters, m_surfacePullbackWorkspace, parameterDualAction);
m_surface.applyPullbackDerivative(
parameters, parameterDirection, m_surfaceDualWorkspace, m_parameterPullbackWorkspace
);
parameterDualAction += m_parameterPullbackWorkspace;
++m_actionStatistics.pullbackDerivativeApplications;
}
[[nodiscard]] DomainDeformationGeometryReport
inspectMappedGeometry(const mfem::Vector &volumeDisplacement) const {
requireCurrentDiscretization();
requireVolumeSize(volumeDisplacement);
m_volumeGridFunctionWorkspace->SetFromTrueDofs(volumeDisplacement);
mfem::Mesh *mesh = m_volumeDisplacementSpace->GetMesh();
double localMinimumDeterminant = std::numeric_limits<double>::infinity();
int localGeometryIsFinite = 1;
for (int element = 0; element < mesh->GetNE(); ++element) {
mfem::ElementTransformation *transformation = mesh->GetElementTransformation(element);
const mfem::FiniteElement *finiteElement = m_volumeDisplacementSpace->GetFE(element);
// Positivity is a pointwise geometry requirement, not an
// integration-accuracy requirement. A rule only slightly
// above the displacement order can miss a narrow negative
// region of the determinant even when a downstream physics
// rule samples it. The determinant of a d-dimensional
// degree-p deformation gradient can vary at substantially
// higher order, so inspect at a conservative d*p scale.
const int geometryInspectionOrder =
std::max(finiteElement->GetOrder() + 2, 2 * spatialDimension() * finiteElement->GetOrder());
const mfem::IntegrationRule &rule =
mfem::IntRules.Get(transformation->GetGeometryType(), geometryInspectionOrder);
for (int point = 0; point < rule.GetNPoints(); ++point) {
transformation->SetIntPoint(&rule.IntPoint(point));
mfem::DenseMatrix deformationGradient;
m_volumeGridFunctionWorkspace->GetVectorGradient(*transformation, deformationGradient);
for (int component = 0; component < spatialDimension(); ++component) {
deformationGradient(component, component) += 1.0;
}
const double determinant = deformationGradient.Det();
if (!std::isfinite(determinant)) {
localGeometryIsFinite = 0;
} else {
localMinimumDeterminant = std::min(localMinimumDeterminant, determinant);
}
}
}
double globalMinimumDeterminant = 0.0;
int globalGeometryIsFinite = 0;
MPI_Allreduce(
&localMinimumDeterminant, &globalMinimumDeterminant, 1, MPI_DOUBLE, MPI_MIN,
m_volumeDisplacementSpace->GetComm()
);
MPI_Allreduce(
&localGeometryIsFinite, &globalGeometryIsFinite, 1, MPI_INT, MPI_MIN,
m_volumeDisplacementSpace->GetComm()
);
if (globalGeometryIsFinite == 0) {
globalMinimumDeterminant = std::numeric_limits<double>::quiet_NaN();
}
++m_actionStatistics.geometryInspections;
return {.minimumJacobianDeterminant = globalMinimumDeterminant};
}
[[nodiscard]] DomainDeformationGeometryReport buildValidatedVolumeDisplacement(
const mfem::Vector &parameters,
mfem::Vector &volumeDisplacement,
const double determinantFloor = 0.0
) const {
if (!std::isfinite(determinantFloor) || determinantFloor < 0.0) {
throw std::invalid_argument("The mapped-geometry determinant floor must be finite and non-negative.");
}
buildVolumeDisplacement(parameters, volumeDisplacement);
const DomainDeformationGeometryReport report = inspectMappedGeometry(volumeDisplacement);
if (!report.isOrientationPreserving(determinantFloor)) {
throw std::domain_error("The prepared domain deformation inverts at least one volume element.");
}
return report;
}
private:
[[nodiscard]] static DomainDeformationDescriptor makeDescriptor(
const PreparedSurface &surface,
const PreparedInterior &interior,
const PreparedVacuum &vacuum
) noexcept {
const SurfaceDeformationDescriptor surfaceDescriptor = surface.descriptor();
const InteriorDeformationExtensionDescriptor interiorDescriptor = interior.descriptor();
const VacuumDeformationExtensionDescriptor vacuumDescriptor = vacuum.descriptor();
return {
.surfaceDeformation = surfaceDescriptor,
.stellarInteriorExtension = interiorDescriptor,
.vacuumExtension = vacuumDescriptor,
.linearOnReferenceGeometry = surfaceDescriptor.linearOnReferenceGeometry &&
interiorDescriptor.linearOnReferenceGeometry &&
vacuumDescriptor.linearOnReferenceGeometry,
.requiresAuxiliarySolve =
interiorDescriptor.requiresAuxiliarySolve || vacuumDescriptor.requiresAuxiliarySolve,
.hasExactDerivativeTranspose = surfaceDescriptor.hasExactDerivativeTranspose &&
interiorDescriptor.hasExactDerivativeTranspose &&
vacuumDescriptor.hasExactDerivativeTranspose,
.hasExactPullbackDerivative = surfaceDescriptor.hasExactPullbackDerivative &&
interiorDescriptor.hasExactPullbackDerivative &&
vacuumDescriptor.hasExactPullbackDerivative
};
}
void validateCompatibility(
mfem::ParFiniteElementSpace &surfaceScalarSpace,
mfem::ParFiniteElementSpace &volumeDisplacementSpace,
mfem::ParMesh &logicalReferenceMesh
) const {
const mfem::Mesh *physicalMesh = volumeDisplacementSpace.GetMesh();
if (!m_descriptor.isValid()) {
throw std::invalid_argument("Prepared domain deformation descriptors are incompatible.");
}
if (!m_descriptor.supportsExactNewtonLinearization()) {
throw std::invalid_argument("Prepared domain deformation requires exact transpose and pullback paths.");
}
if (physicalMesh == nullptr || surfaceScalarSpace.GetMesh() != physicalMesh) {
throw std::invalid_argument("Prepared domain deformation spaces must share one physical mesh.");
}
if (logicalReferenceMesh.GetNE() != physicalMesh->GetNE() ||
logicalReferenceMesh.GetNBE() != physicalMesh->GetNBE()) {
throw std::invalid_argument("Prepared domain deformation requires the paired logical reference mesh.");
}
if (m_surface.surfaceDisplacementSize() != m_interior.surfaceDisplacementSize() ||
m_surface.surfaceDisplacementSize() != m_vacuum.surfaceDisplacementSize()) {
throw std::invalid_argument("Prepared deformation factors have incompatible surface trace sizes.");
}
if (m_interior.interiorDisplacementSize() != m_vacuum.vacuumDisplacementSize() ||
m_interior.interiorDisplacementSize() != volumeDisplacementSpace.GetTrueVSize()) {
throw std::invalid_argument("Prepared deformation factors have incompatible volume vector sizes.");
}
if (m_interior.scalarTrueDofCount() != m_vacuum.scalarTrueDofCount() ||
volumeDisplacementSpace.GetTrueVSize() != spatialDimension() * m_interior.scalarTrueDofCount()) {
throw std::invalid_argument("Prepared deformation factors have incompatible scalar volume topology.");
}
if (volumeDisplacementSpace.GetOrdering() != mfem::Ordering::byNODES) {
throw std::invalid_argument("Prepared domain deformation requires MFEM byNODES volume ordering.");
}
}
void compileOwnership() {
m_volumeOwners.resize(static_cast<std::size_t>(scalarTrueDofCount()));
m_compositionReport.scalarTrueDofCount = scalarTrueDofCount();
for (int scalarTrueDof = 0; scalarTrueDof < scalarTrueDofCount(); ++scalarTrueDof) {
const bool hasStellarSupport = m_interior.hasStellarSupport(scalarTrueDof);
const bool hasVacuumSupport = m_vacuum.hasVacuumSupport(scalarTrueDof);
if (!hasStellarSupport && !hasVacuumSupport) {
throw std::invalid_argument("A volume displacement DOF has no deformation-extension owner.");
}
if (hasStellarSupport) {
m_volumeOwners[static_cast<std::size_t>(scalarTrueDof)] = VolumeDeformationOwner::StellarInterior;
++m_compositionReport.stellarInteriorOwnedScalarDofCount;
if (hasVacuumSupport) {
++m_compositionReport.sharedSurfaceScalarDofCount;
}
} else {
m_volumeOwners[static_cast<std::size_t>(scalarTrueDof)] = VolumeDeformationOwner::Vacuum;
++m_compositionReport.vacuumOwnedScalarDofCount;
}
}
}
[[nodiscard]] int volumeVectorDof(
const int scalarTrueDof,
const int component
) const noexcept {
return scalarTrueDof + component * scalarTrueDofCount();
}
void mergeVolumeFields(
const mfem::Vector &interiorVolume,
const mfem::Vector &vacuumVolume,
mfem::Vector &volume
) const noexcept {
for (int scalarTrueDof = 0; scalarTrueDof < scalarTrueDofCount(); ++scalarTrueDof) {
const mfem::Vector &source = volumeOwner(scalarTrueDof) == VolumeDeformationOwner::StellarInterior
? interiorVolume
: vacuumVolume;
for (int component = 0; component < spatialDimension(); ++component) {
const int vectorDof = volumeVectorDof(scalarTrueDof, component);
volume(vectorDof) = source(vectorDof);
}
}
}
void splitVolumeDual(const mfem::Vector &volumeDual) const noexcept {
m_interiorVolumeDualWorkspace = 0.0;
m_vacuumVolumeDualWorkspace = 0.0;
for (int scalarTrueDof = 0; scalarTrueDof < scalarTrueDofCount(); ++scalarTrueDof) {
mfem::Vector &destination = volumeOwner(scalarTrueDof) == VolumeDeformationOwner::StellarInterior
? m_interiorVolumeDualWorkspace
: m_vacuumVolumeDualWorkspace;
for (int component = 0; component < spatialDimension(); ++component) {
const int vectorDof = volumeVectorDof(scalarTrueDof, component);
destination(vectorDof) = volumeDual(vectorDof);
}
}
}
void applyExtensionTransposes() const {
m_interior.applyJacobianTranspose(
m_surfaceDisplacementWorkspace, m_interiorVolumeDualWorkspace, m_interiorSurfaceDualWorkspace
);
m_vacuum.applyJacobianTranspose(
m_surfaceDisplacementWorkspace, m_vacuumVolumeDualWorkspace, m_vacuumSurfaceDualWorkspace
);
addSurfaceFields(m_interiorSurfaceDualWorkspace, m_vacuumSurfaceDualWorkspace, m_surfaceDualWorkspace);
}
static void addSurfaceFields(
const mfem::Vector &interior,
const mfem::Vector &vacuum,
mfem::Vector &sum
) {
sum = interior;
sum += vacuum;
}
void requireCurrentDiscretization() const {
if (!matchesCurrentDiscretization()) {
throw std::logic_error("Prepared domain deformation discretization dependencies are stale.");
}
}
void requireParameterSize(const mfem::Vector &parameters) const {
if (parameters.Size() != parameterCount()) {
throw std::invalid_argument("Prepared domain deformation received an incompatible parameter vector.");
}
}
void requireVolumeSize(const mfem::Vector &volume) const {
if (volume.Size() != volumeDisplacementSize()) {
throw std::invalid_argument("Prepared domain deformation received an incompatible volume vector.");
}
}
void requireScalarTrueDof(const int scalarTrueDof) const {
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
throw std::out_of_range("Scalar true DOF is outside the prepared domain deformation.");
}
}
PreparedSurface m_surface;
PreparedInterior m_interior;
PreparedVacuum m_vacuum;
mfem::ParFiniteElementSpace *m_volumeDisplacementSpace;
DomainDeformationDescriptor m_descriptor;
DomainDeformationCompositionReport m_compositionReport;
DomainDeformationDiscretizationDependencies m_discretizationDependencies;
std::vector<VolumeDeformationOwner> m_volumeOwners;
mutable PreparedDomainDeformationActionStatistics m_actionStatistics;
mutable mfem::Vector m_surfaceDisplacementWorkspace;
mutable mfem::Vector m_surfaceDirectionWorkspace;
mutable mfem::Vector m_interiorVolumeWorkspace;
mutable mfem::Vector m_vacuumVolumeWorkspace;
mutable mfem::Vector m_interiorVolumeDualWorkspace;
mutable mfem::Vector m_vacuumVolumeDualWorkspace;
mutable mfem::Vector m_interiorSurfaceDualWorkspace;
mutable mfem::Vector m_vacuumSurfaceDualWorkspace;
mutable mfem::Vector m_surfaceDualWorkspace;
mutable mfem::Vector m_interiorSurfacePullbackWorkspace;
mutable mfem::Vector m_vacuumSurfacePullbackWorkspace;
mutable mfem::Vector m_surfacePullbackWorkspace;
mutable mfem::Vector m_parameterPullbackWorkspace;
mutable std::unique_ptr<mfem::ParGridFunction> m_volumeGridFunctionWorkspace;
};
template <
PreparedSurfaceDeformationPrescription PreparedSurface,
PreparedInteriorDeformationExtension PreparedInterior,
PreparedVacuumDeformationExtension PreparedVacuum>
[[nodiscard]] auto composePreparedDomainDeformation(
PreparedSurface preparedSurface,
PreparedInterior preparedInterior,
PreparedVacuum preparedVacuum,
mfem::ParFiniteElementSpace &surfaceScalarSpace,
mfem::ParFiniteElementSpace &volumeDisplacementSpace,
mfem::ParMesh &logicalReferenceMesh
) {
return PreparedDomainDeformation<PreparedSurface, PreparedInterior, PreparedVacuum>{
std::move(preparedSurface), std::move(preparedInterior), std::move(preparedVacuum),
surfaceScalarSpace, volumeDisplacementSpace, logicalReferenceMesh
};
}
class PreparedDomainDeformationRuntime final {
public:
template <PreparedDomainDeformationOperator PreparedDeformation>
requires(!std::same_as<
std::remove_cvref_t<PreparedDeformation>,
PreparedDomainDeformationRuntime>)
explicit PreparedDomainDeformationRuntime(PreparedDeformation &&preparedDeformation)
: m_implementation(
std::make_unique<Implementation<std::remove_cvref_t<PreparedDeformation>>>(
std::forward<PreparedDeformation>(preparedDeformation)
)
) {
}
PreparedDomainDeformationRuntime(const PreparedDomainDeformationRuntime &) = delete;
PreparedDomainDeformationRuntime &operator=(const PreparedDomainDeformationRuntime &) = delete;
PreparedDomainDeformationRuntime(PreparedDomainDeformationRuntime &&) noexcept = default;
PreparedDomainDeformationRuntime &operator=(PreparedDomainDeformationRuntime &&) noexcept = default;
[[nodiscard]] DomainDeformationDescriptor descriptor() const noexcept {
return m_implementation->descriptor();
}
[[nodiscard]] int parameterCount() const noexcept {
return m_implementation->parameterCount();
}
[[nodiscard]] int surfaceDisplacementSize() const noexcept {
return m_implementation->surfaceDisplacementSize();
}
[[nodiscard]] int volumeDisplacementSize() const noexcept {
return m_implementation->volumeDisplacementSize();
}
[[nodiscard]] bool matchesCurrentDiscretization() const noexcept {
return m_implementation->matchesCurrentDiscretization();
}
[[nodiscard]] DomainDeformationCompositionReport compositionReport() const noexcept {
return m_implementation->compositionReport();
}
[[nodiscard]] DomainDeformationDiscretizationDependencies discretizationDependencies() const noexcept {
return m_implementation->discretizationDependencies();
}
[[nodiscard]] PreparedDomainDeformationActionStatistics actionStatistics() const noexcept {
return m_implementation->actionStatistics();
}
void buildVolumeDisplacement(
const mfem::Vector &parameters,
mfem::Vector &volumeDisplacement
) const {
m_implementation->buildVolumeDisplacement(parameters, volumeDisplacement);
}
void applyJacobian(
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
mfem::Vector &volumeDisplacementDirection
) const {
m_implementation->applyJacobian(parameters, parameterDirection, volumeDisplacementDirection);
}
void applyJacobianTranspose(
const mfem::Vector &parameters,
const mfem::Vector &volumeDisplacementDual,
mfem::Vector &parameterDual
) const {
m_implementation->applyJacobianTranspose(parameters, volumeDisplacementDual, parameterDual);
}
void applyPullbackDerivative(
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
const mfem::Vector &volumeDisplacementDual,
mfem::Vector &parameterDualAction
) const {
m_implementation->applyPullbackDerivative(
parameters, parameterDirection, volumeDisplacementDual, parameterDualAction
);
}
[[nodiscard]] DomainDeformationGeometryReport
inspectMappedGeometry(const mfem::Vector &volumeDisplacement) const {
return m_implementation->inspectMappedGeometry(volumeDisplacement);
}
[[nodiscard]] DomainDeformationGeometryReport buildValidatedVolumeDisplacement(
const mfem::Vector &parameters,
mfem::Vector &volumeDisplacement,
const double determinantFloor = 0.0
) const {
return m_implementation->buildValidatedVolumeDisplacement(parameters, volumeDisplacement, determinantFloor);
}
private:
class Interface {
public:
virtual ~Interface() = default;
[[nodiscard]] virtual DomainDeformationDescriptor descriptor() const noexcept = 0;
[[nodiscard]] virtual int parameterCount() const noexcept = 0;
[[nodiscard]] virtual int surfaceDisplacementSize() const noexcept = 0;
[[nodiscard]] virtual int volumeDisplacementSize() const noexcept = 0;
[[nodiscard]] virtual bool matchesCurrentDiscretization() const noexcept = 0;
[[nodiscard]] virtual DomainDeformationCompositionReport compositionReport() const noexcept = 0;
[[nodiscard]] virtual DomainDeformationDiscretizationDependencies
discretizationDependencies() const noexcept = 0;
[[nodiscard]] virtual PreparedDomainDeformationActionStatistics actionStatistics() const noexcept = 0;
virtual void buildVolumeDisplacement(
const mfem::Vector &,
mfem::Vector &
) const = 0;
virtual void applyJacobian(
const mfem::Vector &,
const mfem::Vector &,
mfem::Vector &
) const = 0;
virtual void applyJacobianTranspose(
const mfem::Vector &,
const mfem::Vector &,
mfem::Vector &
) const = 0;
virtual void applyPullbackDerivative(
const mfem::Vector &,
const mfem::Vector &,
const mfem::Vector &,
mfem::Vector &
) const = 0;
[[nodiscard]] virtual DomainDeformationGeometryReport inspectMappedGeometry(const mfem::Vector &) const = 0;
[[nodiscard]] virtual DomainDeformationGeometryReport buildValidatedVolumeDisplacement(
const mfem::Vector &,
mfem::Vector &,
double
) const = 0;
};
template <PreparedDomainDeformationOperator PreparedDeformation> class Implementation final : public Interface {
public:
explicit Implementation(PreparedDeformation preparedDeformation)
: m_preparedDeformation(std::move(preparedDeformation)) {
}
[[nodiscard]] DomainDeformationDescriptor descriptor() const noexcept override {
return m_preparedDeformation.descriptor();
}
[[nodiscard]] int parameterCount() const noexcept override {
return m_preparedDeformation.parameterCount();
}
[[nodiscard]] int surfaceDisplacementSize() const noexcept override {
return m_preparedDeformation.surfaceDisplacementSize();
}
[[nodiscard]] int volumeDisplacementSize() const noexcept override {
return m_preparedDeformation.volumeDisplacementSize();
}
[[nodiscard]] bool matchesCurrentDiscretization() const noexcept override {
return m_preparedDeformation.matchesCurrentDiscretization();
}
[[nodiscard]] DomainDeformationCompositionReport compositionReport() const noexcept override {
return m_preparedDeformation.compositionReport();
}
[[nodiscard]] DomainDeformationDiscretizationDependencies
discretizationDependencies() const noexcept override {
return m_preparedDeformation.discretizationDependencies();
}
[[nodiscard]] PreparedDomainDeformationActionStatistics actionStatistics() const noexcept override {
return m_preparedDeformation.actionStatistics();
}
void buildVolumeDisplacement(
const mfem::Vector &parameters,
mfem::Vector &volumeDisplacement
) const override {
m_preparedDeformation.buildVolumeDisplacement(parameters, volumeDisplacement);
}
void applyJacobian(
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
mfem::Vector &volumeDisplacementDirection
) const override {
m_preparedDeformation.applyJacobian(parameters, parameterDirection, volumeDisplacementDirection);
}
void applyJacobianTranspose(
const mfem::Vector &parameters,
const mfem::Vector &volumeDisplacementDual,
mfem::Vector &parameterDual
) const override {
m_preparedDeformation.applyJacobianTranspose(parameters, volumeDisplacementDual, parameterDual);
}
void applyPullbackDerivative(
const mfem::Vector &parameters,
const mfem::Vector &parameterDirection,
const mfem::Vector &volumeDisplacementDual,
mfem::Vector &parameterDualAction
) const override {
m_preparedDeformation.applyPullbackDerivative(
parameters, parameterDirection, volumeDisplacementDual, parameterDualAction
);
}
[[nodiscard]] DomainDeformationGeometryReport
inspectMappedGeometry(const mfem::Vector &volumeDisplacement) const override {
return m_preparedDeformation.inspectMappedGeometry(volumeDisplacement);
}
[[nodiscard]] DomainDeformationGeometryReport buildValidatedVolumeDisplacement(
const mfem::Vector &parameters,
mfem::Vector &volumeDisplacement,
const double determinantFloor
) const override {
return m_preparedDeformation.buildValidatedVolumeDisplacement(
parameters, volumeDisplacement, determinantFloor
);
}
private:
PreparedDeformation m_preparedDeformation;
};
std::unique_ptr<Interface> m_implementation;
};
template <
utils::domain::IsSchema SchemaT = utils::domain::CoreEnvelopeVacuumDomainSchema,
SurfaceDeformationPrescription SurfacePrescription,
InteriorDeformationExtension InteriorExtension,
VacuumDeformationExtension VacuumExtension>
requires SurfaceDeformationCompilable<
SurfacePrescription,
SurfaceDeformationCompilationContext> &&
InteriorDeformationExtensionCompilable<
InteriorExtension,
RadialDeformationExtensionCompilationContext> &&
VacuumDeformationExtensionCompilable<
VacuumExtension,
RadialDeformationExtensionCompilationContext>
[[nodiscard]] auto compileDomainDeformation(
const SurfacePrescription &surfacePrescription,
const InteriorExtension &interiorExtension,
const VacuumExtension &vacuumExtension,
fem::FEM &finiteElementModel
) {
if (!finiteElementModel.okay()) {
throw std::invalid_argument("Domain deformation compilation requires a complete finite-element model.");
}
const field::ScalarBoundaryDofMap surfaceDofMap =
field::make_stellar_surface_scalar_dof_map<SchemaT>(*finiteElementModel.surfaceDeformationFes);
const SurfaceDeformationCompilationContext surfaceContext{
*finiteElementModel.surfaceDeformationFes, surfaceDofMap
};
auto preparedSurface = compileSurfaceDeformationPrescription(surfacePrescription, surfaceContext);
const RadialDeformationExtensionCompilationContext extensionContext =
makeRadialDeformationExtensionCompilationContext<SchemaT>(
*finiteElementModel.surfaceDeformationFes, *finiteElementModel.displacementFes,
*finiteElementModel.logicalReferenceMesh
);
auto preparedInterior = compileInteriorDeformationExtension(interiorExtension, extensionContext);
auto preparedVacuum = compileVacuumDeformationExtension(vacuumExtension, extensionContext);
return composePreparedDomainDeformation(
std::move(preparedSurface), std::move(preparedInterior), std::move(preparedVacuum),
*finiteElementModel.surfaceDeformationFes, *finiteElementModel.displacementFes,
*finiteElementModel.logicalReferenceMesh
);
}
} // namespace mean_field::deformation