871 lines
42 KiB
Plaintext
871 lines
42 KiB
Plaintext
|
|
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 ¶meters,
|
||
|
|
const mfem::Vector ¶meterDirection,
|
||
|
|
const mfem::Vector &volumeDisplacementDual,
|
||
|
|
mfem::Vector &volumeDisplacement,
|
||
|
|
mfem::Vector ¶meterDual
|
||
|
|
) {
|
||
|
|
{ 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 ¶meters,
|
||
|
|
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 ¶meters,
|
||
|
|
const mfem::Vector ¶meterDirection,
|
||
|
|
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 ¶meters,
|
||
|
|
const mfem::Vector &volumeDisplacementDual,
|
||
|
|
mfem::Vector ¶meterDual
|
||
|
|
) 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 ¶meters,
|
||
|
|
const mfem::Vector ¶meterDirection,
|
||
|
|
const mfem::Vector &volumeDisplacementDual,
|
||
|
|
mfem::Vector ¶meterDualAction
|
||
|
|
) 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 ¶meters,
|
||
|
|
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 ¶meters) 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 ¶meters,
|
||
|
|
mfem::Vector &volumeDisplacement
|
||
|
|
) const {
|
||
|
|
m_implementation->buildVolumeDisplacement(parameters, volumeDisplacement);
|
||
|
|
}
|
||
|
|
|
||
|
|
void applyJacobian(
|
||
|
|
const mfem::Vector ¶meters,
|
||
|
|
const mfem::Vector ¶meterDirection,
|
||
|
|
mfem::Vector &volumeDisplacementDirection
|
||
|
|
) const {
|
||
|
|
m_implementation->applyJacobian(parameters, parameterDirection, volumeDisplacementDirection);
|
||
|
|
}
|
||
|
|
|
||
|
|
void applyJacobianTranspose(
|
||
|
|
const mfem::Vector ¶meters,
|
||
|
|
const mfem::Vector &volumeDisplacementDual,
|
||
|
|
mfem::Vector ¶meterDual
|
||
|
|
) const {
|
||
|
|
m_implementation->applyJacobianTranspose(parameters, volumeDisplacementDual, parameterDual);
|
||
|
|
}
|
||
|
|
|
||
|
|
void applyPullbackDerivative(
|
||
|
|
const mfem::Vector ¶meters,
|
||
|
|
const mfem::Vector ¶meterDirection,
|
||
|
|
const mfem::Vector &volumeDisplacementDual,
|
||
|
|
mfem::Vector ¶meterDualAction
|
||
|
|
) 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 ¶meters,
|
||
|
|
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 ¶meters,
|
||
|
|
mfem::Vector &volumeDisplacement
|
||
|
|
) const override {
|
||
|
|
m_preparedDeformation.buildVolumeDisplacement(parameters, volumeDisplacement);
|
||
|
|
}
|
||
|
|
void applyJacobian(
|
||
|
|
const mfem::Vector ¶meters,
|
||
|
|
const mfem::Vector ¶meterDirection,
|
||
|
|
mfem::Vector &volumeDisplacementDirection
|
||
|
|
) const override {
|
||
|
|
m_preparedDeformation.applyJacobian(parameters, parameterDirection, volumeDisplacementDirection);
|
||
|
|
}
|
||
|
|
void applyJacobianTranspose(
|
||
|
|
const mfem::Vector ¶meters,
|
||
|
|
const mfem::Vector &volumeDisplacementDual,
|
||
|
|
mfem::Vector ¶meterDual
|
||
|
|
) const override {
|
||
|
|
m_preparedDeformation.applyJacobianTranspose(parameters, volumeDisplacementDual, parameterDual);
|
||
|
|
}
|
||
|
|
void applyPullbackDerivative(
|
||
|
|
const mfem::Vector ¶meters,
|
||
|
|
const mfem::Vector ¶meterDirection,
|
||
|
|
const mfem::Vector &volumeDisplacementDual,
|
||
|
|
mfem::Vector ¶meterDualAction
|
||
|
|
) 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 ¶meters,
|
||
|
|
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
|