feat(surface): surface deformation prescriptions
restricted the unknown state vector to surface deformation and implemented one prescription, NodalRadialSurface, while the full volumetric displacment field is reconstructed analytically from that. This reduced the number of degrees of freedom in the system by a factor of 80 while also removing many null vectors from the system.
This commit is contained in:
345
libmeanfield/impl/deformation/nodal_radial_surface.cpp
Normal file
345
libmeanfield/impl/deformation/nodal_radial_surface.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
|
||||
import :deformation.nodal_radial_surface;
|
||||
|
||||
namespace mean_field::deformation {
|
||||
namespace {
|
||||
[[nodiscard]] SurfaceDeformationDescriptor nodalRadialDescriptor(const int spatialDimension) noexcept {
|
||||
return {
|
||||
.name = "NodalRadialSurface",
|
||||
.spatialDimension = spatialDimension,
|
||||
.motionKind = SurfaceMotionKind::Radial,
|
||||
.linearOnReferenceGeometry = true,
|
||||
.requiresStarShapedReferenceSurface = true,
|
||||
.hasExactDerivativeTranspose = true,
|
||||
.hasExactPullbackDerivative = true,
|
||||
.translationTreatment = GeometricGaugeTreatment::Retained,
|
||||
.orientationTreatment = GeometricGaugeTreatment::Retained
|
||||
};
|
||||
}
|
||||
|
||||
void requireFiniteVector(
|
||||
const mfem::Vector &vector,
|
||||
const char *message
|
||||
) {
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
if (!std::isfinite(vector(index))) {
|
||||
throw std::invalid_argument(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SurfaceDeformationCompilationContext::SurfaceDeformationCompilationContext(
|
||||
mfem::ParFiniteElementSpace &scalarFiniteElementSpace,
|
||||
field::ScalarBoundaryDofMap surfaceDofMap
|
||||
)
|
||||
: m_scalarFiniteElementSpace(&scalarFiniteElementSpace),
|
||||
m_surfaceDofMap(std::move(surfaceDofMap)) {
|
||||
if (scalarFiniteElementSpace.Nonconforming()) {
|
||||
throw std::invalid_argument(
|
||||
"Surface deformation compilation currently requires a conforming scalar finite-element space."
|
||||
);
|
||||
}
|
||||
if (scalarFiniteElementSpace.GetVDim() != 1) {
|
||||
throw std::invalid_argument("Surface deformation compilation requires a scalar finite-element space.");
|
||||
}
|
||||
if (scalarFiniteElementSpace.GetMesh() == nullptr) {
|
||||
throw std::invalid_argument("Surface deformation compilation requires a finite-element mesh.");
|
||||
}
|
||||
if (m_surfaceDofMap.volume_true_dof_size() != scalarFiniteElementSpace.GetTrueVSize()) {
|
||||
throw std::invalid_argument(
|
||||
"The surface DOF map and scalar finite-element space have incompatible true-DOF sizes."
|
||||
);
|
||||
}
|
||||
if (m_surfaceDofMap.global_size() <= 0) {
|
||||
throw std::invalid_argument("Surface deformation compilation requires at least one surface coordinate.");
|
||||
}
|
||||
}
|
||||
|
||||
mfem::ParFiniteElementSpace &SurfaceDeformationCompilationContext::scalarFiniteElementSpace() const noexcept {
|
||||
return *m_scalarFiniteElementSpace;
|
||||
}
|
||||
|
||||
const field::ScalarBoundaryDofMap &SurfaceDeformationCompilationContext::surfaceDofMap() const noexcept {
|
||||
return m_surfaceDofMap;
|
||||
}
|
||||
|
||||
NodalRadialSurface::NodalRadialSurface(mfem::Vector referenceCenter)
|
||||
: m_referenceCenter(std::move(referenceCenter)) {
|
||||
validate();
|
||||
}
|
||||
|
||||
const mfem::Vector &NodalRadialSurface::referenceCenter() const noexcept {
|
||||
return m_referenceCenter;
|
||||
}
|
||||
|
||||
SurfaceDeformationDescriptor NodalRadialSurface::descriptor() const noexcept {
|
||||
return nodalRadialDescriptor(m_referenceCenter.Size());
|
||||
}
|
||||
|
||||
void NodalRadialSurface::validate() const {
|
||||
if (m_referenceCenter.Size() <= 0) {
|
||||
throw std::invalid_argument("NodalRadialSurface requires a non-empty reference center.");
|
||||
}
|
||||
requireFiniteVector(m_referenceCenter, "NodalRadialSurface reference-center coordinates must be finite.");
|
||||
}
|
||||
|
||||
PreparedNodalRadialSurface::PreparedNodalRadialSurface(
|
||||
const SurfaceDeformationDescriptor descriptor,
|
||||
mfem::Vector referenceCenter,
|
||||
field::ScalarBoundaryDofMap surfaceDofMap,
|
||||
mfem::Vector radialDirections,
|
||||
mfem::Vector referenceRadii
|
||||
)
|
||||
: m_descriptor(descriptor),
|
||||
m_referenceCenter(std::move(referenceCenter)),
|
||||
m_surfaceDofMap(std::move(surfaceDofMap)),
|
||||
m_radialDirections(std::move(radialDirections)),
|
||||
m_referenceRadii(std::move(referenceRadii)) {
|
||||
}
|
||||
|
||||
SurfaceDeformationDescriptor PreparedNodalRadialSurface::descriptor() const noexcept {
|
||||
return m_descriptor;
|
||||
}
|
||||
|
||||
int PreparedNodalRadialSurface::parameterCount() const noexcept {
|
||||
return m_surfaceDofMap.local_size();
|
||||
}
|
||||
|
||||
long long PreparedNodalRadialSurface::globalParameterCount() const noexcept {
|
||||
return m_surfaceDofMap.global_size();
|
||||
}
|
||||
|
||||
long long PreparedNodalRadialSurface::globalParameterOffset() const noexcept {
|
||||
return m_surfaceDofMap.global_offset();
|
||||
}
|
||||
|
||||
int PreparedNodalRadialSurface::spatialDimension() const noexcept {
|
||||
return m_descriptor.spatialDimension;
|
||||
}
|
||||
|
||||
int PreparedNodalRadialSurface::surfaceDisplacementSize() const noexcept {
|
||||
return spatialDimension() * parameterCount();
|
||||
}
|
||||
|
||||
long long PreparedNodalRadialSurface::globalSurfaceDisplacementSize() const noexcept {
|
||||
return static_cast<long long>(spatialDimension()) * globalParameterCount();
|
||||
}
|
||||
|
||||
long long PreparedNodalRadialSurface::globalSurfaceDisplacementOffset() const noexcept {
|
||||
return static_cast<long long>(spatialDimension()) * globalParameterOffset();
|
||||
}
|
||||
|
||||
int PreparedNodalRadialSurface::surfaceDisplacementDof(
|
||||
const int parameterDof,
|
||||
const int component
|
||||
) const {
|
||||
if (parameterDof < 0 || parameterDof >= parameterCount()) {
|
||||
throw std::out_of_range("Parameter DOF is outside PreparedNodalRadialSurface.");
|
||||
}
|
||||
if (component < 0 || component >= spatialDimension()) {
|
||||
throw std::out_of_range("Surface-displacement component is outside PreparedNodalRadialSurface.");
|
||||
}
|
||||
return spatialDimension() * parameterDof + component;
|
||||
}
|
||||
|
||||
double PreparedNodalRadialSurface::radialDirection(
|
||||
const int parameterDof,
|
||||
const int component
|
||||
) const {
|
||||
return m_radialDirections(surfaceDisplacementDof(parameterDof, component));
|
||||
}
|
||||
|
||||
double PreparedNodalRadialSurface::referenceRadius(const int parameterDof) const {
|
||||
if (parameterDof < 0 || parameterDof >= parameterCount()) {
|
||||
throw std::out_of_range("Parameter DOF is outside PreparedNodalRadialSurface.");
|
||||
}
|
||||
return m_referenceRadii(parameterDof);
|
||||
}
|
||||
|
||||
const mfem::Vector &PreparedNodalRadialSurface::referenceCenter() const noexcept {
|
||||
return m_referenceCenter;
|
||||
}
|
||||
|
||||
const field::ScalarBoundaryDofMap &PreparedNodalRadialSurface::surfaceDofMap() const noexcept {
|
||||
return m_surfaceDofMap;
|
||||
}
|
||||
|
||||
void PreparedNodalRadialSurface::buildSurfaceDisplacement(
|
||||
const mfem::Vector ¶meters,
|
||||
mfem::Vector &surfaceDisplacement
|
||||
) const {
|
||||
requireParameterSize(parameters);
|
||||
requireSurfaceDisplacementSize(surfaceDisplacement);
|
||||
|
||||
for (int parameterDof = 0; parameterDof < parameterCount(); ++parameterDof) {
|
||||
for (int component = 0; component < spatialDimension(); ++component) {
|
||||
const int surfaceDof = spatialDimension() * parameterDof + component;
|
||||
surfaceDisplacement(surfaceDof) = parameters(parameterDof) * m_radialDirections(surfaceDof);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedNodalRadialSurface::applyJacobian(
|
||||
const mfem::Vector ¶meters,
|
||||
const mfem::Vector ¶meterDirection,
|
||||
mfem::Vector &surfaceDisplacementDirection
|
||||
) const {
|
||||
requireParameterSize(parameters);
|
||||
requireParameterSize(parameterDirection);
|
||||
requireSurfaceDisplacementSize(surfaceDisplacementDirection);
|
||||
|
||||
for (int parameterDof = 0; parameterDof < parameterCount(); ++parameterDof) {
|
||||
for (int component = 0; component < spatialDimension(); ++component) {
|
||||
const int surfaceDof = spatialDimension() * parameterDof + component;
|
||||
surfaceDisplacementDirection(surfaceDof) =
|
||||
parameterDirection(parameterDof) * m_radialDirections(surfaceDof);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedNodalRadialSurface::applyJacobianTranspose(
|
||||
const mfem::Vector ¶meters,
|
||||
const mfem::Vector &surfaceDisplacementDual,
|
||||
mfem::Vector ¶meterDual
|
||||
) const {
|
||||
requireParameterSize(parameters);
|
||||
requireSurfaceDisplacementSize(surfaceDisplacementDual);
|
||||
requireParameterSize(parameterDual);
|
||||
|
||||
for (int parameterDof = 0; parameterDof < parameterCount(); ++parameterDof) {
|
||||
double radialWork = 0.0;
|
||||
for (int component = 0; component < spatialDimension(); ++component) {
|
||||
const int surfaceDof = spatialDimension() * parameterDof + component;
|
||||
radialWork += m_radialDirections(surfaceDof) * surfaceDisplacementDual(surfaceDof);
|
||||
}
|
||||
parameterDual(parameterDof) = radialWork;
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedNodalRadialSurface::applyPullbackDerivative(
|
||||
const mfem::Vector ¶meters,
|
||||
const mfem::Vector ¶meterDirection,
|
||||
const mfem::Vector &surfaceDisplacementDual,
|
||||
mfem::Vector ¶meterDualAction
|
||||
) const {
|
||||
requireParameterSize(parameters);
|
||||
requireParameterSize(parameterDirection);
|
||||
requireSurfaceDisplacementSize(surfaceDisplacementDual);
|
||||
requireParameterSize(parameterDualAction);
|
||||
|
||||
parameterDualAction = 0.0;
|
||||
}
|
||||
|
||||
void PreparedNodalRadialSurface::requireParameterSize(const mfem::Vector ¶meters) const {
|
||||
if (parameters.Size() != parameterCount()) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"Nodal radial parameter vector has size {}, but the prepared surface requires {}.",
|
||||
parameters.Size(), parameterCount()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedNodalRadialSurface::requireSurfaceDisplacementSize(const mfem::Vector &surfaceDisplacement) const {
|
||||
if (surfaceDisplacement.Size() != surfaceDisplacementSize()) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"Surface displacement vector has size {}, but the prepared nodal radial surface requires {}.",
|
||||
surfaceDisplacement.Size(), surfaceDisplacementSize()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PreparedNodalRadialSurface compileSurfaceDeformationPrescription(
|
||||
const NodalRadialSurface &prescription,
|
||||
const SurfaceDeformationCompilationContext &context
|
||||
) {
|
||||
prescription.validate();
|
||||
|
||||
mfem::ParFiniteElementSpace &scalarSpace = context.scalarFiniteElementSpace();
|
||||
const mfem::Mesh *mesh = scalarSpace.GetMesh();
|
||||
|
||||
if (mesh == nullptr) {
|
||||
throw std::invalid_argument("Nodal radial surface compilation requires a reference mesh.");
|
||||
}
|
||||
if (prescription.referenceCenter().Size() != mesh->SpaceDimension()) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"NodalRadialSurface reference center has dimension {}, but the reference mesh has spatial "
|
||||
"dimension {}.",
|
||||
prescription.referenceCenter().Size(), mesh->SpaceDimension()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const field::ScalarBoundaryDofMap &surfaceDofMap = context.surfaceDofMap();
|
||||
const int parameterCount = surfaceDofMap.local_size();
|
||||
const int spatialDimension = mesh->SpaceDimension();
|
||||
|
||||
mfem::Vector referencePositions(spatialDimension * parameterCount);
|
||||
mfem::ParGridFunction coordinateField(&scalarSpace);
|
||||
|
||||
for (int component = 0; component < spatialDimension; ++component) {
|
||||
mfem::FunctionCoefficient coordinateCoefficient([component](const mfem::Vector &position) {
|
||||
return position(component);
|
||||
});
|
||||
|
||||
coordinateField.ProjectCoefficient(coordinateCoefficient);
|
||||
|
||||
mfem::Vector coordinateTrueDofs;
|
||||
coordinateField.GetTrueDofs(coordinateTrueDofs);
|
||||
const mfem::Vector surfaceCoordinates = surfaceDofMap.gather(coordinateTrueDofs);
|
||||
|
||||
for (int parameterDof = 0; parameterDof < parameterCount; ++parameterDof) {
|
||||
referencePositions(spatialDimension * parameterDof + component) = surfaceCoordinates(parameterDof);
|
||||
}
|
||||
}
|
||||
|
||||
mfem::Vector radialDirections(referencePositions.Size());
|
||||
mfem::Vector referenceRadii(parameterCount);
|
||||
|
||||
for (int parameterDof = 0; parameterDof < parameterCount; ++parameterDof) {
|
||||
double radiusSquared = 0.0;
|
||||
|
||||
for (int component = 0; component < spatialDimension; ++component) {
|
||||
const int surfaceDof = spatialDimension * parameterDof + component;
|
||||
const double radialCoordinate =
|
||||
referencePositions(surfaceDof) - prescription.referenceCenter()(component);
|
||||
|
||||
radialDirections(surfaceDof) = radialCoordinate;
|
||||
radiusSquared += radialCoordinate * radialCoordinate;
|
||||
}
|
||||
|
||||
const double radius = std::sqrt(radiusSquared);
|
||||
if (!std::isfinite(radius) || radius <= 0.0) {
|
||||
throw std::invalid_argument(
|
||||
"Every nodal radial surface coordinate must have a finite positive distance from the reference "
|
||||
"center."
|
||||
);
|
||||
}
|
||||
|
||||
referenceRadii(parameterDof) = radius;
|
||||
for (int component = 0; component < spatialDimension; ++component) {
|
||||
radialDirections(spatialDimension * parameterDof + component) /= radius;
|
||||
}
|
||||
}
|
||||
|
||||
return PreparedNodalRadialSurface(
|
||||
nodalRadialDescriptor(spatialDimension), prescription.referenceCenter(), surfaceDofMap,
|
||||
std::move(radialDirections), std::move(referenceRadii)
|
||||
);
|
||||
}
|
||||
} // namespace mean_field::deformation
|
||||
1061
libmeanfield/impl/deformation/radial_extensions.cpp
Normal file
1061
libmeanfield/impl/deformation/radial_extensions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -45,15 +45,33 @@ namespace mean_field::fem {
|
||||
stroid::refinement::UniformRefinement(fem.smesh, extraRefine);
|
||||
}
|
||||
|
||||
if (fem.smesh.mesh == nullptr || fem.smesh.reference_mesh == nullptr) {
|
||||
throw std::runtime_error("A STROID mesh requires paired physical and logical reference meshes.");
|
||||
}
|
||||
|
||||
int mpiSize = 1;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
|
||||
|
||||
const std::unique_ptr<int[]> meshPartitioning(fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
|
||||
|
||||
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1);
|
||||
fem.logicalReferenceMesh =
|
||||
std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.reference_mesh, meshPartitioning.get(), 1);
|
||||
|
||||
fem.mesh->EnsureNodes();
|
||||
|
||||
if (fem.logicalReferenceMesh->GetNE() != fem.mesh->GetNE()) {
|
||||
throw std::runtime_error("The physical and logical reference meshes have incompatible local elements.");
|
||||
}
|
||||
for (int element = 0; element < fem.mesh->GetNE(); ++element) {
|
||||
if (fem.logicalReferenceMesh->GetElementGeometry(element) != fem.mesh->GetElementGeometry(element) ||
|
||||
fem.logicalReferenceMesh->GetAttribute(element) != fem.mesh->GetAttribute(element)) {
|
||||
throw std::runtime_error(
|
||||
"The physical and logical reference meshes do not preserve element correspondence."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Section 2: Exterior compactification coordinate
|
||||
// =====================================================================
|
||||
@@ -185,21 +203,32 @@ namespace mean_field::fem {
|
||||
|
||||
*fem.displacement = 0.0;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Surface deformation: scalar H1 coordinates on StellarSurface.
|
||||
//
|
||||
// This ambient scalar space exists only to define the surface basis
|
||||
// and owned true-DOF topology. Interior scalar DOFs are not nonlinear
|
||||
// unknowns.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.surfaceDeformationFes =
|
||||
std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.displacementFec.get());
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Density: scalar discontinuous L2
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
|
||||
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
|
||||
|
||||
fem.densityFes = DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
|
||||
fem.densityFes = DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Specific enthalpy: scalar continuous H1
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
|
||||
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
|
||||
|
||||
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
|
||||
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
|
||||
|
||||
// =====================================================================
|
||||
// Section 4: Multipole data
|
||||
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
@@ -21,29 +22,23 @@ namespace {
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector make_computational_origin(const mfem::ParMesh &mesh) {
|
||||
mfem::Vector origin(mesh.SpaceDimension());
|
||||
origin = 0.0;
|
||||
return origin;
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::StellarEquilibriumLayout make_layout(
|
||||
const mean_field::field::FieldDofMap &densityMap,
|
||||
const mean_field::field::FieldDofMap &displacementMap,
|
||||
const int surfaceDeformationParameterCount,
|
||||
const mean_field::field::FieldDofMap &gravityFluxMap,
|
||||
const mean_field::field::FieldDofMap &gravityPotentialMap,
|
||||
const mean_field::field::FieldDofMap &enthalpyMap
|
||||
) {
|
||||
using Form = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||
using Form = mean_field::utils::blocks::surface_deformed_stellar_equilibrium_form;
|
||||
|
||||
const std::array<int, Form::value_block_count> valueSizes{
|
||||
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
|
||||
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||
densityMap.reduced_size(), surfaceDeformationParameterCount, gravityFluxMap.reduced_size(),
|
||||
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||
};
|
||||
|
||||
const std::array<int, Form::residual_block_count> residualSizes{
|
||||
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
|
||||
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
|
||||
surfaceDeformationParameterCount, enthalpyMap.reduced_size(), 1
|
||||
};
|
||||
|
||||
return {valueSizes, residualSizes};
|
||||
@@ -182,11 +177,13 @@ namespace {
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions
|
||||
make_gravity_revisions(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions make_gravity_revisions(
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies,
|
||||
const mean_field::operators::StellarEquilibriumDependencyStamp &generatedDisplacement
|
||||
) {
|
||||
return {
|
||||
.discretization = {.value = dependencies.discretization.revision},
|
||||
.displacement = {.value = dependencies.displacement.revision},
|
||||
.displacement = {.value = generatedDisplacement.revision},
|
||||
.density = {.value = dependencies.density.revision},
|
||||
.gravity_gradient = {.value = dependencies.gravityGradient.revision},
|
||||
.gravity_potential = {.value = dependencies.gravityPotential.revision}
|
||||
@@ -194,26 +191,28 @@ namespace {
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::barotropic::BarotropicClosureDependencies
|
||||
make_barotropic_closure_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
make_barotropic_closure_dependencies(
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies,
|
||||
const mean_field::operators::StellarEquilibriumDependencyStamp &generatedDisplacement
|
||||
) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
|
||||
.displacement = {
|
||||
.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision
|
||||
}
|
||||
.displacement = {.identity = generatedDisplacement.identity, .revision = generatedDisplacement.revision}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::DisplacementResidualDependencies
|
||||
make_displacement_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
[[nodiscard]] mean_field::operators::DisplacementResidualDependencies make_displacement_dependencies(
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies,
|
||||
const mean_field::operators::StellarEquilibriumDependencyStamp &generatedDisplacement
|
||||
) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.displacement =
|
||||
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.displacement = {.identity = generatedDisplacement.identity, .revision = generatedDisplacement.revision},
|
||||
.gravityGradient =
|
||||
{.identity = dependencies.gravityGradient.identity, .revision = dependencies.gravityGradient.revision},
|
||||
.enthalpy = {.identity = dependencies.enthalpy.identity, .revision = dependencies.enthalpy.revision},
|
||||
@@ -222,7 +221,10 @@ namespace {
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies
|
||||
make_hydrostatic_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
make_hydrostatic_dependencies(
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies,
|
||||
const mean_field::operators::StellarEquilibriumDependencyStamp &generatedDisplacement
|
||||
) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
@@ -230,44 +232,48 @@ namespace {
|
||||
.gravityPotential =
|
||||
{.identity = dependencies.gravityPotential.identity,
|
||||
.revision = dependencies.gravityPotential.revision},
|
||||
.displacement =
|
||||
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
|
||||
.rotation = {.identity = dependencies.rotation.identity, .revision = dependencies.rotation.revision},
|
||||
.displacement = {.identity = generatedDisplacement.identity, .revision = generatedDisplacement.revision},
|
||||
.rotation = {.identity = dependencies.rotation.identity, .revision = dependencies.rotation.revision},
|
||||
.bernoulliConstant = {
|
||||
.identity = dependencies.bernoulliConstant.identity, .revision = dependencies.bernoulliConstant.revision
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::MassNormalizationDependencies
|
||||
make_mass_dependencies(const mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
[[nodiscard]] mean_field::operators::MassNormalizationDependencies make_mass_dependencies(
|
||||
const mean_field::operators::StellarEquilibriumDependencies &dependencies,
|
||||
const mean_field::operators::StellarEquilibriumDependencyStamp &generatedDisplacement
|
||||
) {
|
||||
return {
|
||||
.discretization =
|
||||
{.identity = dependencies.discretization.identity, .revision = dependencies.discretization.revision},
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.displacement =
|
||||
{.identity = dependencies.displacement.identity, .revision = dependencies.displacement.revision},
|
||||
.targetMass = {.identity = dependencies.targetMass.identity, .revision = dependencies.targetMass.revision}
|
||||
.density = {.identity = dependencies.density.identity, .revision = dependencies.density.revision},
|
||||
.displacement = {.identity = generatedDisplacement.identity, .revision = generatedDisplacement.revision},
|
||||
.targetMass = {.identity = dependencies.targetMass.identity, .revision = dependencies.targetMass.revision}
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mean_field::operators {
|
||||
struct PreparedStellarEquilibriumOperator::ConstructionData {
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation;
|
||||
field::FieldDofMap densityMap;
|
||||
field::FieldDofMap displacementMap;
|
||||
field::FieldDofMap gravityFluxMap;
|
||||
field::FieldDofMap gravityPotentialMap;
|
||||
field::FieldDofMap enthalpyMap;
|
||||
field::FieldBoundaryDofMap pressureSurfaceRows;
|
||||
field::FieldPointDofMap centerDisplacementRows;
|
||||
|
||||
StellarEquilibriumLayout layout;
|
||||
mfem::Array<int> gravityStateOffsets;
|
||||
mfem::Array<int> gravityResidualOffsets;
|
||||
|
||||
explicit ConstructionData(fem::FEM &f)
|
||||
: densityMap(
|
||||
ConstructionData(
|
||||
fem::FEM &f,
|
||||
deformation::PreparedDomainDeformationRuntime preparedDomainDeformation
|
||||
)
|
||||
: domainDeformation(std::move(preparedDomainDeformation)),
|
||||
densityMap(
|
||||
field::make_field_dof_map<
|
||||
field::Density,
|
||||
DomainSchema>(*f.densityFes)
|
||||
@@ -301,17 +307,9 @@ namespace mean_field::operators {
|
||||
enthalpyMap
|
||||
)
|
||||
),
|
||||
centerDisplacementRows(
|
||||
field::make_field_point_dof_map<field::Displacement>(
|
||||
*f.displacementFes,
|
||||
displacementMap,
|
||||
make_computational_origin(*f.mesh),
|
||||
1.0e-12
|
||||
)
|
||||
),
|
||||
layout(make_layout(
|
||||
densityMap,
|
||||
displacementMap,
|
||||
domainDeformation.parameterCount(),
|
||||
gravityFluxMap,
|
||||
gravityPotentialMap,
|
||||
enthalpyMap
|
||||
@@ -329,10 +327,12 @@ namespace mean_field::operators {
|
||||
}
|
||||
};
|
||||
|
||||
PreparedStellarEquilibriumOperator::ConstructionData
|
||||
PreparedStellarEquilibriumOperator::MakeConstructionData(fem::FEM &f) {
|
||||
PreparedStellarEquilibriumOperator::ConstructionData PreparedStellarEquilibriumOperator::MakeConstructionData(
|
||||
fem::FEM &f,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation
|
||||
) {
|
||||
verify_coupled_discretization(f);
|
||||
return ConstructionData(f);
|
||||
return ConstructionData(f, std::move(domainDeformation));
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
|
||||
@@ -340,7 +340,8 @@ namespace mean_field::operators {
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
const double targetMass,
|
||||
const PressureSurfaceConstraintView surfaceConstraint
|
||||
const PressureSurfaceConstraintView surfaceConstraint,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation
|
||||
)
|
||||
: PreparedStellarEquilibriumOperator(
|
||||
f,
|
||||
@@ -348,7 +349,10 @@ namespace mean_field::operators {
|
||||
equationOfState,
|
||||
targetMass,
|
||||
surfaceConstraint,
|
||||
MakeConstructionData(f)
|
||||
MakeConstructionData(
|
||||
f,
|
||||
std::move(domainDeformation)
|
||||
)
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -408,7 +412,7 @@ namespace mean_field::operators {
|
||||
constructionData.pressureSurfaceRows,
|
||||
surfaceConstraint
|
||||
),
|
||||
m_centeringConstraintOperator(constructionData.centerDisplacementRows),
|
||||
m_domainDeformation(std::move(constructionData.domainDeformation)),
|
||||
m_targetMass(targetMass) {
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(m_targetMass) && m_targetMass > 0.0,
|
||||
@@ -420,12 +424,35 @@ namespace mean_field::operators {
|
||||
"PreparedStellarEquilibriumOperator has inconsistent block dimensions."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
m_domainDeformation.volumeDisplacementSize() == constructionData.displacementMap.reduced_size(),
|
||||
"The domain-deformation output does not match the coupled displacement discretization."
|
||||
);
|
||||
|
||||
m_generatedDisplacementDependency.identity =
|
||||
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(&m_domainDeformation));
|
||||
|
||||
m_gravityState.SetSize(m_gravityStateOffsets.Last());
|
||||
|
||||
m_gravityDirection.SetSize(m_gravityStateOffsets.Last());
|
||||
|
||||
m_gravityState = 0.0;
|
||||
m_gravityDirection = 0.0;
|
||||
m_surfaceDeformationParameters.SetSize(m_domainDeformation.parameterCount());
|
||||
m_generatedVolumeDisplacement.SetSize(m_domainDeformation.volumeDisplacementSize());
|
||||
m_fullMechanicalResidual.SetSize(m_domainDeformation.volumeDisplacementSize());
|
||||
m_volumeDisplacementDirection.SetSize(m_domainDeformation.volumeDisplacementSize());
|
||||
m_fullMechanicalAction.SetSize(m_domainDeformation.volumeDisplacementSize());
|
||||
m_surfaceShapeAction.SetSize(m_domainDeformation.parameterCount());
|
||||
m_pullbackDerivativeAction.SetSize(m_domainDeformation.parameterCount());
|
||||
|
||||
m_gravityState = 0.0;
|
||||
m_gravityDirection = 0.0;
|
||||
m_surfaceDeformationParameters = 0.0;
|
||||
m_generatedVolumeDisplacement = 0.0;
|
||||
m_fullMechanicalResidual = 0.0;
|
||||
m_volumeDisplacementDirection = 0.0;
|
||||
m_fullMechanicalAction = 0.0;
|
||||
m_surfaceShapeAction = 0.0;
|
||||
m_pullbackDerivativeAction = 0.0;
|
||||
}
|
||||
|
||||
PreparedStellarEquilibriumReport PreparedStellarEquilibriumOperator::Prepare(
|
||||
@@ -448,8 +475,8 @@ namespace mean_field::operators {
|
||||
m_preparedDependencies.density, dependencies.density, "The density revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.displacement, dependencies.displacement,
|
||||
"The displacement revision cannot move backwards."
|
||||
m_preparedDependencies.surfaceDeformation, dependencies.surfaceDeformation,
|
||||
"The surface-deformation revision cannot move backwards."
|
||||
);
|
||||
validate_dependency_transition(
|
||||
m_preparedDependencies.gravityGradient, dependencies.gravityGradient,
|
||||
@@ -477,10 +504,10 @@ namespace mean_field::operators {
|
||||
|
||||
m_isPrepared = false;
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
using Form = utils::blocks::surface_deformed_stellar_equilibrium_form;
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto surfaceDeformationValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::surface_deformation_field.parameters_term);
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravityPotentialValue =
|
||||
@@ -490,49 +517,65 @@ namespace mean_field::operators {
|
||||
constexpr auto bernoulliValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
const mfem::Vector reducedDensity = make_value_view(state, m_layout, densityValue);
|
||||
const mfem::Vector displacement = make_value_view(state, m_layout, displacementValue);
|
||||
const mfem::Vector gravityGradient = make_value_view(state, m_layout, gravityGradientValue);
|
||||
const mfem::Vector gravityPotential = make_value_view(state, m_layout, gravityPotentialValue);
|
||||
const mfem::Vector reducedEnthalpy = make_value_view(state, m_layout, enthalpyValue);
|
||||
const mfem::Vector bernoulli = make_value_view(state, m_layout, bernoulliValue);
|
||||
const mfem::Vector reducedDensity = make_value_view(state, m_layout, densityValue);
|
||||
const mfem::Vector surfaceDeformationParameters = make_value_view(state, m_layout, surfaceDeformationValue);
|
||||
const mfem::Vector gravityGradient = make_value_view(state, m_layout, gravityGradientValue);
|
||||
const mfem::Vector gravityPotential = make_value_view(state, m_layout, gravityPotentialValue);
|
||||
const mfem::Vector reducedEnthalpy = make_value_view(state, m_layout, enthalpyValue);
|
||||
const mfem::Vector bernoulli = make_value_view(state, m_layout, bernoulliValue);
|
||||
|
||||
pack_gravity_vector(
|
||||
m_gravityState, m_gravityStateOffsets, reducedDensity, displacement, gravityGradient, gravityPotential
|
||||
);
|
||||
const bool generatedGeometryChanged =
|
||||
!wasPrepared || dependencies.discretization != m_preparedDependencies.discretization ||
|
||||
dependencies.surfaceDeformation != m_preparedDependencies.surfaceDeformation;
|
||||
|
||||
PreparedStellarEquilibriumReport report;
|
||||
if (generatedGeometryChanged) {
|
||||
m_surfaceDeformationParameters = surfaceDeformationParameters;
|
||||
m_generatedGeometryReport = m_domainDeformation.buildValidatedVolumeDisplacement(
|
||||
m_surfaceDeformationParameters, m_generatedVolumeDisplacement
|
||||
);
|
||||
++m_generatedDisplacementDependency.revision;
|
||||
++m_statistics.generatedGeometryBuilds;
|
||||
report.generatedVolumeDisplacement = true;
|
||||
}
|
||||
report.generatedGeometry = m_generatedGeometryReport;
|
||||
report.generatedDisplacement = m_generatedDisplacementDependency;
|
||||
|
||||
report.gravity = m_gravityOperator.Prepare(m_gravityState, make_gravity_revisions(dependencies));
|
||||
pack_gravity_vector(
|
||||
m_gravityState, m_gravityStateOffsets, reducedDensity, m_generatedVolumeDisplacement, gravityGradient,
|
||||
gravityPotential
|
||||
);
|
||||
|
||||
report.gravity = m_gravityOperator.Prepare(
|
||||
m_gravityState, make_gravity_revisions(dependencies, m_generatedDisplacementDependency)
|
||||
);
|
||||
|
||||
report.barotropicClosure = m_barotropicClosureOperator.Prepare(
|
||||
{.density = reducedDensity, .enthalpy = reducedEnthalpy, .displacement = displacement},
|
||||
make_barotropic_closure_dependencies(dependencies)
|
||||
{.density = reducedDensity, .enthalpy = reducedEnthalpy, .displacement = m_generatedVolumeDisplacement},
|
||||
make_barotropic_closure_dependencies(dependencies, m_generatedDisplacementDependency)
|
||||
);
|
||||
|
||||
report.hydrostatic = m_hydrostaticOperator.Prepare(
|
||||
{.enthalpy = reducedEnthalpy,
|
||||
.gravityPotential = gravityPotential,
|
||||
.displacement = displacement,
|
||||
.displacement = m_generatedVolumeDisplacement,
|
||||
.bernoulliConstant = bernoulli(0)},
|
||||
make_hydrostatic_dependencies(dependencies), rotation
|
||||
make_hydrostatic_dependencies(dependencies, m_generatedDisplacementDependency), rotation
|
||||
);
|
||||
|
||||
report.displacement = m_displacementOperator.Prepare(
|
||||
{.enthalpy = reducedEnthalpy}, make_displacement_dependencies(dependencies), rotation
|
||||
{.enthalpy = reducedEnthalpy},
|
||||
make_displacement_dependencies(dependencies, m_generatedDisplacementDependency), rotation
|
||||
);
|
||||
|
||||
report.massNormalization =
|
||||
m_massNormalizationOperator.Prepare({.targetMass = m_targetMass}, make_mass_dependencies(dependencies));
|
||||
report.massNormalization = m_massNormalizationOperator.Prepare(
|
||||
{.targetMass = m_targetMass}, make_mass_dependencies(dependencies, m_generatedDisplacementDependency)
|
||||
);
|
||||
|
||||
report.surfaceConstraint = m_surfaceConstraintOperator.Prepare(
|
||||
reducedEnthalpy, !wasPrepared || dependencies.enthalpy != m_preparedDependencies.enthalpy
|
||||
);
|
||||
|
||||
report.centeringConstraint = m_centeringConstraintOperator.Prepare(
|
||||
displacement, !wasPrepared || dependencies.displacement != m_preparedDependencies.displacement
|
||||
);
|
||||
|
||||
const bool dependenciesChanged = !wasPrepared || dependencies != m_preparedDependencies;
|
||||
if (dependenciesChanged || report.DidAnyChildWork()) {
|
||||
AssembleResidual();
|
||||
@@ -545,7 +588,7 @@ namespace mean_field::operators {
|
||||
}
|
||||
|
||||
void PreparedStellarEquilibriumOperator::AssembleResidual() {
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
using Form = utils::blocks::surface_deformed_stellar_equilibrium_form;
|
||||
|
||||
constexpr auto gravityGradientResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
@@ -553,8 +596,8 @@ namespace mean_field::operators {
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto densityResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto surfaceShapeResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::surface_deformation_field.shape_equilibrium_term);
|
||||
constexpr auto enthalpyResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
constexpr auto massResidual =
|
||||
@@ -562,14 +605,17 @@ namespace mean_field::operators {
|
||||
|
||||
mfem::Vector gravity;
|
||||
mfem::Vector closure;
|
||||
mfem::Vector displacement;
|
||||
mfem::Vector surfaceShape;
|
||||
mfem::Vector hydrostatic;
|
||||
mfem::Vector mass;
|
||||
|
||||
m_gravityOperator.Mult(m_gravityState, gravity);
|
||||
m_barotropicClosureOperator.BuildResidual(closure);
|
||||
m_displacementOperator.BuildResidual(displacement);
|
||||
m_centeringConstraintOperator.ApplyResidualRows(displacement);
|
||||
m_displacementOperator.BuildResidual(m_fullMechanicalResidual);
|
||||
surfaceShape.SetSize(m_domainDeformation.parameterCount());
|
||||
m_domainDeformation.applyJacobianTranspose(
|
||||
m_surfaceDeformationParameters, m_fullMechanicalResidual, surfaceShape
|
||||
);
|
||||
m_hydrostaticOperator.BuildResidual(hydrostatic);
|
||||
m_surfaceConstraintOperator.ApplyResidualRows(hydrostatic);
|
||||
m_massNormalizationOperator.BuildResidual(mass);
|
||||
@@ -599,8 +645,8 @@ namespace mean_field::operators {
|
||||
m_cachedResidual, m_layout, densityResidual, closure, "The closure residual has the wrong size."
|
||||
);
|
||||
assign_residual_block(
|
||||
m_cachedResidual, m_layout, displacementResidual, displacement,
|
||||
"The displacement residual has the wrong size."
|
||||
m_cachedResidual, m_layout, surfaceShapeResidual, surfaceShape,
|
||||
"The surface-shape residual has the wrong size."
|
||||
);
|
||||
|
||||
assign_residual_block(
|
||||
@@ -633,11 +679,11 @@ namespace mean_field::operators {
|
||||
direction, "PreparedStellarEquilibriumOperator received a non-finite Jacobian direction."
|
||||
);
|
||||
|
||||
using Form = utils::blocks::barotropic_equilibrium_form;
|
||||
using Form = utils::blocks::surface_deformed_stellar_equilibrium_form;
|
||||
|
||||
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto surfaceDeformationValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::surface_deformation_field.parameters_term);
|
||||
constexpr auto gravityGradientValue =
|
||||
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
||||
constexpr auto gravityPotentialValue =
|
||||
@@ -653,51 +699,61 @@ namespace mean_field::operators {
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::gravity_field.poisson_term);
|
||||
constexpr auto densityResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::density_field.mass_term);
|
||||
constexpr auto displacementResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
||||
constexpr auto surfaceShapeResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::surface_deformation_field.shape_equilibrium_term);
|
||||
constexpr auto enthalpyResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::enthalpy_field.specific_term);
|
||||
constexpr auto massResidual =
|
||||
utils::blocks::get_residual_block<Form>(utils::blocks::barotropic_constant_field.mass_normalization_term);
|
||||
|
||||
const mfem::Vector reducedDensityDirection = make_value_view(direction, m_layout, densityValue);
|
||||
const mfem::Vector displacementDirection = make_value_view(direction, m_layout, displacementValue);
|
||||
const mfem::Vector gravityGradientDirection = make_value_view(direction, m_layout, gravityGradientValue);
|
||||
const mfem::Vector gravityPotentialDirection = make_value_view(direction, m_layout, gravityPotentialValue);
|
||||
const mfem::Vector reducedEnthalpyDirection = make_value_view(direction, m_layout, enthalpyValue);
|
||||
const mfem::Vector bernoulliDirection = make_value_view(direction, m_layout, bernoulliValue);
|
||||
const mfem::Vector reducedDensityDirection = make_value_view(direction, m_layout, densityValue);
|
||||
const mfem::Vector surfaceDeformationDirection = make_value_view(direction, m_layout, surfaceDeformationValue);
|
||||
const mfem::Vector gravityGradientDirection = make_value_view(direction, m_layout, gravityGradientValue);
|
||||
const mfem::Vector gravityPotentialDirection = make_value_view(direction, m_layout, gravityPotentialValue);
|
||||
const mfem::Vector reducedEnthalpyDirection = make_value_view(direction, m_layout, enthalpyValue);
|
||||
const mfem::Vector bernoulliDirection = make_value_view(direction, m_layout, bernoulliValue);
|
||||
|
||||
m_domainDeformation.applyJacobian(
|
||||
m_surfaceDeformationParameters, surfaceDeformationDirection, m_volumeDisplacementDirection
|
||||
);
|
||||
|
||||
pack_gravity_vector(
|
||||
m_gravityDirection, m_gravityStateOffsets, reducedDensityDirection, displacementDirection,
|
||||
m_gravityDirection, m_gravityStateOffsets, reducedDensityDirection, m_volumeDisplacementDirection,
|
||||
gravityGradientDirection, gravityPotentialDirection
|
||||
);
|
||||
|
||||
mfem::Vector gravityAction;
|
||||
mfem::Vector closureAction;
|
||||
mfem::Vector displacementAction;
|
||||
mfem::Vector hydrostaticAction;
|
||||
mfem::Vector massAction;
|
||||
|
||||
m_gravityJacobianOperator.Mult(m_gravityDirection, gravityAction);
|
||||
|
||||
m_barotropicClosureOperator.Mult(
|
||||
reducedDensityDirection, reducedEnthalpyDirection, displacementDirection, closureAction
|
||||
reducedDensityDirection, reducedEnthalpyDirection, m_volumeDisplacementDirection, closureAction
|
||||
);
|
||||
|
||||
m_displacementOperator.ApplyCompleteJacobianAction(
|
||||
reducedDensityDirection, displacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
|
||||
displacementAction
|
||||
reducedDensityDirection, m_volumeDisplacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
|
||||
m_fullMechanicalAction
|
||||
);
|
||||
m_centeringConstraintOperator.ApplyJacobianRows(displacementDirection, displacementAction);
|
||||
m_domainDeformation.applyJacobianTranspose(
|
||||
m_surfaceDeformationParameters, m_fullMechanicalAction, m_surfaceShapeAction
|
||||
);
|
||||
m_domainDeformation.applyPullbackDerivative(
|
||||
m_surfaceDeformationParameters, surfaceDeformationDirection, m_fullMechanicalResidual,
|
||||
m_pullbackDerivativeAction
|
||||
);
|
||||
m_surfaceShapeAction += m_pullbackDerivativeAction;
|
||||
|
||||
m_hydrostaticOperator.ApplyCompleteJacobianAction(
|
||||
reducedEnthalpyDirection, gravityPotentialDirection, bernoulliDirection(0), displacementDirection,
|
||||
reducedEnthalpyDirection, gravityPotentialDirection, bernoulliDirection(0), m_volumeDisplacementDirection,
|
||||
hydrostaticAction
|
||||
);
|
||||
m_surfaceConstraintOperator.ApplyJacobianRows(reducedEnthalpyDirection, hydrostaticAction);
|
||||
|
||||
m_massNormalizationOperator.ApplyCompleteJacobianAction(
|
||||
reducedDensityDirection, displacementDirection, massAction
|
||||
reducedDensityDirection, m_volumeDisplacementDirection, massAction
|
||||
);
|
||||
|
||||
action.SetSize(Height());
|
||||
@@ -725,8 +781,8 @@ namespace mean_field::operators {
|
||||
action, m_layout, densityResidual, closureAction, "The closure Jacobian action has the wrong size."
|
||||
);
|
||||
assign_residual_block(
|
||||
action, m_layout, displacementResidual, displacementAction,
|
||||
"The displacement Jacobian action has the wrong size."
|
||||
action, m_layout, surfaceShapeResidual, m_surfaceShapeAction,
|
||||
"The surface-shape Jacobian action has the wrong size."
|
||||
);
|
||||
|
||||
assign_residual_block(
|
||||
@@ -743,8 +799,7 @@ namespace mean_field::operators {
|
||||
bool PreparedStellarEquilibriumOperator::IsPrepared() const noexcept {
|
||||
return m_isPrepared && m_gravityContext.IsPrepared() && m_barotropicClosureOperator.IsPrepared() &&
|
||||
m_hydrostaticOperator.IsPrepared() && m_displacementOperator.IsPrepared() &&
|
||||
m_massNormalizationOperator.IsPrepared() && m_surfaceConstraintOperator.IsPrepared() &&
|
||||
m_centeringConstraintOperator.IsPrepared();
|
||||
m_massNormalizationOperator.IsPrepared() && m_surfaceConstraintOperator.IsPrepared();
|
||||
}
|
||||
|
||||
double PreparedStellarEquilibriumOperator::GetTargetMass() const noexcept {
|
||||
@@ -808,9 +863,30 @@ namespace mean_field::operators {
|
||||
return m_surfaceConstraintOperator;
|
||||
}
|
||||
|
||||
const PreparedCenteringConstraint &
|
||||
PreparedStellarEquilibriumOperator::GetCenteringConstraintOperator() const noexcept {
|
||||
return m_centeringConstraintOperator;
|
||||
const deformation::PreparedDomainDeformationRuntime &
|
||||
PreparedStellarEquilibriumOperator::GetDomainDeformation() const noexcept {
|
||||
return m_domainDeformation;
|
||||
}
|
||||
|
||||
const mfem::Vector &PreparedStellarEquilibriumOperator::GetSurfaceDeformationParameters() const {
|
||||
VerifyPrepared();
|
||||
return m_surfaceDeformationParameters;
|
||||
}
|
||||
|
||||
const mfem::Vector &PreparedStellarEquilibriumOperator::GetGeneratedVolumeDisplacement() const {
|
||||
VerifyPrepared();
|
||||
return m_generatedVolumeDisplacement;
|
||||
}
|
||||
|
||||
const mfem::Vector &PreparedStellarEquilibriumOperator::GetFullMechanicalResidual() const {
|
||||
VerifyPrepared();
|
||||
return m_fullMechanicalResidual;
|
||||
}
|
||||
|
||||
const StellarEquilibriumDependencyStamp &
|
||||
PreparedStellarEquilibriumOperator::GetGeneratedDisplacementDependency() const {
|
||||
VerifyPrepared();
|
||||
return m_generatedDisplacementDependency;
|
||||
}
|
||||
|
||||
void PreparedStellarEquilibriumOperator::VerifyPrepared() const {
|
||||
|
||||
111
libmeanfield/interface/deformation/descriptors.cppm
Normal file
111
libmeanfield/interface/deformation/descriptors.cppm
Normal file
@@ -0,0 +1,111 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
export module mean_field:deformation.descriptors;
|
||||
|
||||
export namespace mean_field::deformation {
|
||||
enum class SurfaceMotionKind : std::uint8_t { Radial, Normal, GeneralVector };
|
||||
|
||||
enum class GeometricGaugeTreatment : std::uint8_t {
|
||||
Retained,
|
||||
ExcludedByParameterization,
|
||||
ConstrainedByPrescription
|
||||
};
|
||||
|
||||
enum class InteriorCenterBehavior : std::uint8_t { Unspecified, FixedAtReferenceCenter, DeterminedBySurfaceMotion };
|
||||
|
||||
enum class VacuumOuterBoundaryBehavior : std::uint8_t {
|
||||
Unspecified,
|
||||
FixedAtReferenceInfinity,
|
||||
DeterminedBySurfaceMotion
|
||||
};
|
||||
|
||||
struct SurfaceDeformationDescriptor final {
|
||||
std::string_view name;
|
||||
int spatialDimension;
|
||||
SurfaceMotionKind motionKind;
|
||||
bool linearOnReferenceGeometry;
|
||||
bool requiresStarShapedReferenceSurface;
|
||||
bool hasExactDerivativeTranspose;
|
||||
bool hasExactPullbackDerivative;
|
||||
GeometricGaugeTreatment translationTreatment;
|
||||
GeometricGaugeTreatment orientationTreatment;
|
||||
|
||||
[[nodiscard]] constexpr bool isValid() const noexcept {
|
||||
return !name.empty() && spatialDimension > 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool supportsExactNewtonLinearization() const noexcept {
|
||||
return hasExactDerivativeTranspose && hasExactPullbackDerivative;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const SurfaceDeformationDescriptor &) const = default;
|
||||
};
|
||||
|
||||
struct InteriorDeformationExtensionDescriptor final {
|
||||
std::string_view name;
|
||||
int spatialDimension;
|
||||
bool linearOnReferenceGeometry;
|
||||
bool requiresRadialFoliation;
|
||||
bool requiresAuxiliarySolve;
|
||||
bool hasExactDerivativeTranspose;
|
||||
bool hasExactPullbackDerivative;
|
||||
InteriorCenterBehavior centerBehavior;
|
||||
|
||||
[[nodiscard]] constexpr bool isValid() const noexcept {
|
||||
return !name.empty() && spatialDimension > 0 && centerBehavior != InteriorCenterBehavior::Unspecified;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool supportsExactNewtonLinearization() const noexcept {
|
||||
return hasExactDerivativeTranspose && hasExactPullbackDerivative;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const InteriorDeformationExtensionDescriptor &) const = default;
|
||||
};
|
||||
|
||||
struct VacuumDeformationExtensionDescriptor final {
|
||||
std::string_view name;
|
||||
int spatialDimension;
|
||||
bool linearOnReferenceGeometry;
|
||||
bool requiresRadialFoliation;
|
||||
bool requiresAuxiliarySolve;
|
||||
bool hasExactDerivativeTranspose;
|
||||
bool hasExactPullbackDerivative;
|
||||
VacuumOuterBoundaryBehavior outerBoundaryBehavior;
|
||||
|
||||
[[nodiscard]] constexpr bool isValid() const noexcept {
|
||||
return !name.empty() && spatialDimension > 0 &&
|
||||
outerBoundaryBehavior != VacuumOuterBoundaryBehavior::Unspecified;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool supportsExactNewtonLinearization() const noexcept {
|
||||
return hasExactDerivativeTranspose && hasExactPullbackDerivative;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const VacuumDeformationExtensionDescriptor &) const = default;
|
||||
};
|
||||
|
||||
struct DomainDeformationDescriptor final {
|
||||
SurfaceDeformationDescriptor surfaceDeformation;
|
||||
InteriorDeformationExtensionDescriptor stellarInteriorExtension;
|
||||
VacuumDeformationExtensionDescriptor vacuumExtension;
|
||||
bool linearOnReferenceGeometry;
|
||||
bool requiresAuxiliarySolve;
|
||||
bool hasExactDerivativeTranspose;
|
||||
bool hasExactPullbackDerivative;
|
||||
|
||||
[[nodiscard]] constexpr bool isValid() const noexcept {
|
||||
return surfaceDeformation.isValid() && stellarInteriorExtension.isValid() && vacuumExtension.isValid() &&
|
||||
surfaceDeformation.spatialDimension == stellarInteriorExtension.spatialDimension &&
|
||||
surfaceDeformation.spatialDimension == vacuumExtension.spatialDimension;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool supportsExactNewtonLinearization() const noexcept {
|
||||
return hasExactDerivativeTranspose && hasExactPullbackDerivative;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const DomainDeformationDescriptor &) const = default;
|
||||
};
|
||||
} // namespace mean_field::deformation
|
||||
870
libmeanfield/interface/deformation/domain_deformation.cppm
Normal file
870
libmeanfield/interface/deformation/domain_deformation.cppm
Normal file
@@ -0,0 +1,870 @@
|
||||
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
|
||||
63
libmeanfield/interface/deformation/interior_extension.cppm
Normal file
63
libmeanfield/interface/deformation/interior_extension.cppm
Normal file
@@ -0,0 +1,63 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:deformation.interior_extension;
|
||||
|
||||
export import :deformation.descriptors;
|
||||
|
||||
export namespace mean_field::deformation {
|
||||
template <typename Candidate>
|
||||
concept PreparedInteriorDeformationExtension = requires(
|
||||
const std::remove_cvref_t<Candidate> &preparedExtension,
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
const mfem::Vector &surfaceDisplacementDirection,
|
||||
const mfem::Vector &interiorDisplacementDual,
|
||||
mfem::Vector &interiorDisplacement,
|
||||
mfem::Vector &surfaceDisplacementDual
|
||||
) {
|
||||
{ preparedExtension.descriptor() } noexcept -> std::same_as<InteriorDeformationExtensionDescriptor>;
|
||||
{ preparedExtension.surfaceDisplacementSize() } noexcept -> std::same_as<int>;
|
||||
{ preparedExtension.interiorDisplacementSize() } noexcept -> std::same_as<int>;
|
||||
{ preparedExtension.scalarTrueDofCount() } noexcept -> std::same_as<int>;
|
||||
{ preparedExtension.hasStellarSupport(0) } -> std::same_as<bool>;
|
||||
{
|
||||
preparedExtension.buildInteriorDisplacement(surfaceDisplacement, interiorDisplacement)
|
||||
} -> std::same_as<void>;
|
||||
{
|
||||
preparedExtension.applyJacobian(surfaceDisplacement, surfaceDisplacementDirection, interiorDisplacement)
|
||||
} -> std::same_as<void>;
|
||||
{
|
||||
preparedExtension.applyJacobianTranspose(
|
||||
surfaceDisplacement, interiorDisplacementDual, surfaceDisplacementDual
|
||||
)
|
||||
} -> std::same_as<void>;
|
||||
{
|
||||
preparedExtension.applyPullbackDerivative(
|
||||
surfaceDisplacement, surfaceDisplacementDirection, interiorDisplacementDual, surfaceDisplacementDual
|
||||
)
|
||||
} -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept InteriorDeformationExtension = requires(const std::remove_cvref_t<Candidate> &extension) {
|
||||
typename std::remove_cvref_t<Candidate>::PreparedType;
|
||||
requires PreparedInteriorDeformationExtension<typename std::remove_cvref_t<Candidate>::PreparedType>;
|
||||
{ extension.descriptor() } noexcept -> std::same_as<InteriorDeformationExtensionDescriptor>;
|
||||
{ extension.validate() } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Extension, typename CompilationContext>
|
||||
concept InteriorDeformationExtensionCompilable =
|
||||
InteriorDeformationExtension<Extension> && requires(
|
||||
const std::remove_cvref_t<Extension> &extension,
|
||||
const std::remove_cvref_t<CompilationContext> &context
|
||||
) {
|
||||
{
|
||||
compileInteriorDeformationExtension(extension, context)
|
||||
} -> std::same_as<typename std::remove_cvref_t<Extension>::PreparedType>;
|
||||
};
|
||||
} // namespace mean_field::deformation
|
||||
138
libmeanfield/interface/deformation/nodal_radial_surface.cppm
Normal file
138
libmeanfield/interface/deformation/nodal_radial_surface.cppm
Normal file
@@ -0,0 +1,138 @@
|
||||
module;
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:deformation.nodal_radial_surface;
|
||||
|
||||
export import :deformation.surface_prescription;
|
||||
export import :field.mfem;
|
||||
|
||||
export namespace mean_field::deformation {
|
||||
class PreparedNodalRadialSurface;
|
||||
|
||||
class SurfaceDeformationCompilationContext final {
|
||||
public:
|
||||
SurfaceDeformationCompilationContext(
|
||||
mfem::ParFiniteElementSpace &scalarFiniteElementSpace,
|
||||
field::ScalarBoundaryDofMap surfaceDofMap
|
||||
);
|
||||
|
||||
[[nodiscard]] mfem::ParFiniteElementSpace &scalarFiniteElementSpace() const noexcept;
|
||||
|
||||
[[nodiscard]] const field::ScalarBoundaryDofMap &surfaceDofMap() const noexcept;
|
||||
|
||||
private:
|
||||
mfem::ParFiniteElementSpace *m_scalarFiniteElementSpace;
|
||||
field::ScalarBoundaryDofMap m_surfaceDofMap;
|
||||
};
|
||||
|
||||
class NodalRadialSurface final {
|
||||
public:
|
||||
using PreparedType = PreparedNodalRadialSurface;
|
||||
|
||||
explicit NodalRadialSurface(mfem::Vector referenceCenter);
|
||||
|
||||
[[nodiscard]] const mfem::Vector &referenceCenter() const noexcept;
|
||||
|
||||
[[nodiscard]] SurfaceDeformationDescriptor descriptor() const noexcept;
|
||||
|
||||
void validate() const;
|
||||
|
||||
private:
|
||||
mfem::Vector m_referenceCenter;
|
||||
};
|
||||
|
||||
class PreparedNodalRadialSurface final {
|
||||
public:
|
||||
[[nodiscard]] SurfaceDeformationDescriptor descriptor() const noexcept;
|
||||
|
||||
[[nodiscard]] int parameterCount() const noexcept;
|
||||
|
||||
[[nodiscard]] long long globalParameterCount() const noexcept;
|
||||
|
||||
[[nodiscard]] long long globalParameterOffset() const noexcept;
|
||||
|
||||
[[nodiscard]] int spatialDimension() const noexcept;
|
||||
|
||||
[[nodiscard]] int surfaceDisplacementSize() const noexcept;
|
||||
|
||||
[[nodiscard]] long long globalSurfaceDisplacementSize() const noexcept;
|
||||
|
||||
[[nodiscard]] long long globalSurfaceDisplacementOffset() const noexcept;
|
||||
|
||||
[[nodiscard]] int surfaceDisplacementDof(
|
||||
int parameterDof,
|
||||
int component
|
||||
) const;
|
||||
|
||||
[[nodiscard]] double radialDirection(
|
||||
int parameterDof,
|
||||
int component
|
||||
) const;
|
||||
|
||||
[[nodiscard]] double referenceRadius(int parameterDof) const;
|
||||
|
||||
[[nodiscard]] const mfem::Vector &referenceCenter() const noexcept;
|
||||
|
||||
[[nodiscard]] const field::ScalarBoundaryDofMap &surfaceDofMap() const noexcept;
|
||||
|
||||
void buildSurfaceDisplacement(
|
||||
const mfem::Vector ¶meters,
|
||||
mfem::Vector &surfaceDisplacement
|
||||
) const;
|
||||
|
||||
void applyJacobian(
|
||||
const mfem::Vector ¶meters,
|
||||
const mfem::Vector ¶meterDirection,
|
||||
mfem::Vector &surfaceDisplacementDirection
|
||||
) const;
|
||||
|
||||
void applyJacobianTranspose(
|
||||
const mfem::Vector ¶meters,
|
||||
const mfem::Vector &surfaceDisplacementDual,
|
||||
mfem::Vector ¶meterDual
|
||||
) const;
|
||||
|
||||
void applyPullbackDerivative(
|
||||
const mfem::Vector ¶meters,
|
||||
const mfem::Vector ¶meterDirection,
|
||||
const mfem::Vector &surfaceDisplacementDual,
|
||||
mfem::Vector ¶meterDualAction
|
||||
) const;
|
||||
|
||||
private:
|
||||
friend PreparedNodalRadialSurface compileSurfaceDeformationPrescription(
|
||||
const NodalRadialSurface &prescription,
|
||||
const SurfaceDeformationCompilationContext &context
|
||||
);
|
||||
|
||||
PreparedNodalRadialSurface(
|
||||
SurfaceDeformationDescriptor descriptor,
|
||||
mfem::Vector referenceCenter,
|
||||
field::ScalarBoundaryDofMap surfaceDofMap,
|
||||
mfem::Vector radialDirections,
|
||||
mfem::Vector referenceRadii
|
||||
);
|
||||
|
||||
void requireParameterSize(const mfem::Vector ¶meters) const;
|
||||
|
||||
void requireSurfaceDisplacementSize(const mfem::Vector &surfaceDisplacement) const;
|
||||
|
||||
SurfaceDeformationDescriptor m_descriptor;
|
||||
mfem::Vector m_referenceCenter;
|
||||
field::ScalarBoundaryDofMap m_surfaceDofMap;
|
||||
mfem::Vector m_radialDirections;
|
||||
mfem::Vector m_referenceRadii;
|
||||
};
|
||||
|
||||
[[nodiscard]] PreparedNodalRadialSurface compileSurfaceDeformationPrescription(
|
||||
const NodalRadialSurface &prescription,
|
||||
const SurfaceDeformationCompilationContext &context
|
||||
);
|
||||
|
||||
static_assert(SurfaceDeformationPrescription<NodalRadialSurface>);
|
||||
static_assert(PreparedSurfaceDeformationPrescription<PreparedNodalRadialSurface>);
|
||||
static_assert(SurfaceDeformationCompilable<
|
||||
NodalRadialSurface,
|
||||
SurfaceDeformationCompilationContext>);
|
||||
} // namespace mean_field::deformation
|
||||
302
libmeanfield/interface/deformation/radial_extensions.cppm
Normal file
302
libmeanfield/interface/deformation/radial_extensions.cppm
Normal file
@@ -0,0 +1,302 @@
|
||||
module;
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
|
||||
export module mean_field:deformation.radial_extensions;
|
||||
|
||||
export import :deformation.interior_extension;
|
||||
export import :deformation.vacuum_extension;
|
||||
export import :field.mfem;
|
||||
export import :utils.domain;
|
||||
|
||||
export namespace mean_field::deformation {
|
||||
class RadialDeformationExtensionCompilationContext final {
|
||||
public:
|
||||
RadialDeformationExtensionCompilationContext(
|
||||
mfem::ParFiniteElementSpace &scalarFiniteElementSpace,
|
||||
mfem::ParFiniteElementSpace &vectorFiniteElementSpace,
|
||||
mfem::ParMesh &logicalReferenceMesh,
|
||||
field::ScalarBoundaryDofMap stellarSurfaceDofMap,
|
||||
field::ScalarBoundaryDofMap infinitySurfaceDofMap,
|
||||
mfem::Array<int> stellarMaterialMarker,
|
||||
mfem::Array<int> vacuumMaterialMarker,
|
||||
int stellarSurfaceBoundaryAttribute,
|
||||
int infinitySurfaceBoundaryAttribute
|
||||
);
|
||||
|
||||
[[nodiscard]] int spatialDimension() const noexcept;
|
||||
[[nodiscard]] int surfaceDisplacementSize() const noexcept;
|
||||
[[nodiscard]] int volumeDisplacementSize() const noexcept;
|
||||
[[nodiscard]] int scalarTrueDofCount() const noexcept;
|
||||
[[nodiscard]] double logicalRadius(int scalarTrueDof) const;
|
||||
[[nodiscard]] double stellarSurfaceLogicalRadius() const noexcept;
|
||||
[[nodiscard]] double infinitySurfaceLogicalRadius() const noexcept;
|
||||
[[nodiscard]] int surfaceInterpolationEntryCount(int scalarTrueDof) const;
|
||||
[[nodiscard]] int surfaceGlobalCoordinate(
|
||||
int scalarTrueDof,
|
||||
int interpolationEntry
|
||||
) const;
|
||||
[[nodiscard]] double surfaceInterpolationWeight(
|
||||
int scalarTrueDof,
|
||||
int interpolationEntry
|
||||
) const;
|
||||
|
||||
private:
|
||||
friend class PreparedPowerLawRadialInteriorExtension;
|
||||
friend class PreparedFixedInfinityRadialVacuumExtension;
|
||||
|
||||
int m_spatialDimension{0};
|
||||
int m_scalarTrueDofCount{0};
|
||||
int m_volumeDisplacementSize{0};
|
||||
int m_surfaceDisplacementSize{0};
|
||||
int m_globalSurfaceDisplacementSize{0};
|
||||
int m_globalSurfaceDisplacementOffset{0};
|
||||
MPI_Comm m_communicator{MPI_COMM_NULL};
|
||||
mfem::Array<int> m_stellarSupport;
|
||||
mfem::Array<int> m_vacuumSupport;
|
||||
mfem::Vector m_logicalRadius;
|
||||
double m_stellarSurfaceLogicalRadius{0.0};
|
||||
double m_infinitySurfaceLogicalRadius{0.0};
|
||||
std::vector<int> m_surfaceInterpolationRowOffsets;
|
||||
std::vector<int> m_surfaceInterpolationGlobalCoordinates;
|
||||
std::vector<double> m_surfaceInterpolationWeights;
|
||||
std::vector<int> m_surfaceDisplacementCounts;
|
||||
std::vector<int> m_surfaceDisplacementOffsets;
|
||||
};
|
||||
|
||||
template <utils::domain::IsSchema SchemaT = utils::domain::CoreEnvelopeVacuumDomainSchema>
|
||||
requires(
|
||||
SchemaT::template contains_domain<utils::domain::Stellar>() &&
|
||||
SchemaT::template contains_domain<utils::domain::Vacuum>() &&
|
||||
SchemaT::template contains_boundary<utils::domain::StellarSurface>() &&
|
||||
SchemaT::template contains_boundary<utils::domain::InfinitySurface>()
|
||||
)
|
||||
[[nodiscard]] RadialDeformationExtensionCompilationContext makeRadialDeformationExtensionCompilationContext(
|
||||
mfem::ParFiniteElementSpace &scalarFiniteElementSpace,
|
||||
mfem::ParFiniteElementSpace &vectorFiniteElementSpace,
|
||||
mfem::ParMesh &logicalReferenceMesh
|
||||
) {
|
||||
const mfem::Mesh *mesh = scalarFiniteElementSpace.GetMesh();
|
||||
MFEM_VERIFY(mesh != nullptr, "Radial deformation extension compilation requires an MFEM mesh.");
|
||||
|
||||
return RadialDeformationExtensionCompilationContext(
|
||||
scalarFiniteElementSpace, vectorFiniteElementSpace, logicalReferenceMesh,
|
||||
field::make_scalar_boundary_dof_map<utils::domain::StellarSurface, SchemaT>(scalarFiniteElementSpace),
|
||||
field::make_scalar_boundary_dof_map<utils::domain::InfinitySurface, SchemaT>(scalarFiniteElementSpace),
|
||||
utils::domain::make_attribute_marker<utils::domain::Stellar, SchemaT>(*mesh),
|
||||
utils::domain::make_attribute_marker<utils::domain::Vacuum, SchemaT>(*mesh),
|
||||
SchemaT::template boundary_attribute<utils::domain::StellarSurface>(),
|
||||
SchemaT::template boundary_attribute<utils::domain::InfinitySurface>()
|
||||
);
|
||||
}
|
||||
|
||||
class PreparedPowerLawRadialInteriorExtension;
|
||||
|
||||
class PowerLawRadialInteriorExtension final {
|
||||
public:
|
||||
using PreparedType = PreparedPowerLawRadialInteriorExtension;
|
||||
|
||||
explicit PowerLawRadialInteriorExtension(double radialPower = 2.0);
|
||||
[[nodiscard]] double radialPower() const noexcept;
|
||||
[[nodiscard]] InteriorDeformationExtensionDescriptor descriptor() const noexcept;
|
||||
void validate() const;
|
||||
|
||||
private:
|
||||
double m_radialPower;
|
||||
};
|
||||
|
||||
class PreparedPowerLawRadialInteriorExtension final {
|
||||
public:
|
||||
[[nodiscard]] InteriorDeformationExtensionDescriptor descriptor() const noexcept;
|
||||
[[nodiscard]] int surfaceDisplacementSize() const noexcept;
|
||||
[[nodiscard]] int interiorDisplacementSize() const noexcept;
|
||||
[[nodiscard]] int scalarTrueDofCount() const noexcept;
|
||||
[[nodiscard]] double radialPower() const noexcept;
|
||||
[[nodiscard]] bool hasStellarSupport(int scalarTrueDof) const;
|
||||
[[nodiscard]] double radialWeight(int scalarTrueDof) const;
|
||||
[[nodiscard]] int surfaceInterpolationEntryCount(int scalarTrueDof) const;
|
||||
[[nodiscard]] int surfaceGlobalCoordinate(
|
||||
int scalarTrueDof,
|
||||
int interpolationEntry
|
||||
) const;
|
||||
[[nodiscard]] double surfaceInterpolationWeight(
|
||||
int scalarTrueDof,
|
||||
int interpolationEntry
|
||||
) const;
|
||||
|
||||
void buildInteriorDisplacement(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
mfem::Vector &interiorDisplacement
|
||||
) const;
|
||||
void applyJacobian(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
const mfem::Vector &surfaceDisplacementDirection,
|
||||
mfem::Vector &interiorDisplacementDirection
|
||||
) const;
|
||||
void applyJacobianTranspose(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
const mfem::Vector &interiorDisplacementDual,
|
||||
mfem::Vector &surfaceDisplacementDual
|
||||
) const;
|
||||
void applyPullbackDerivative(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
const mfem::Vector &surfaceDisplacementDirection,
|
||||
const mfem::Vector &interiorDisplacementDual,
|
||||
mfem::Vector &surfaceDisplacementDualAction
|
||||
) const;
|
||||
|
||||
private:
|
||||
friend PreparedPowerLawRadialInteriorExtension compileInteriorDeformationExtension(
|
||||
const PowerLawRadialInteriorExtension &extension,
|
||||
const RadialDeformationExtensionCompilationContext &context
|
||||
);
|
||||
|
||||
PreparedPowerLawRadialInteriorExtension(
|
||||
const PowerLawRadialInteriorExtension &extension,
|
||||
const RadialDeformationExtensionCompilationContext &context
|
||||
);
|
||||
void requireSurfaceSize(const mfem::Vector &surfaceDisplacement) const;
|
||||
void requireInteriorSize(const mfem::Vector &interiorDisplacement) const;
|
||||
void applyForward(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
mfem::Vector &interiorDisplacement
|
||||
) const;
|
||||
void applyTranspose(
|
||||
const mfem::Vector &interiorDisplacementDual,
|
||||
mfem::Vector &surfaceDisplacementDual
|
||||
) const;
|
||||
|
||||
InteriorDeformationExtensionDescriptor m_descriptor;
|
||||
double m_radialPower{2.0};
|
||||
int m_surfaceDisplacementSize{0};
|
||||
int m_interiorDisplacementSize{0};
|
||||
int m_spatialDimension{0};
|
||||
int m_globalSurfaceDisplacementSize{0};
|
||||
int m_globalSurfaceDisplacementOffset{0};
|
||||
MPI_Comm m_communicator{MPI_COMM_NULL};
|
||||
mfem::Array<int> m_stellarSupport;
|
||||
mfem::Vector m_radialWeights;
|
||||
std::vector<int> m_surfaceInterpolationRowOffsets;
|
||||
std::vector<int> m_surfaceInterpolationGlobalCoordinates;
|
||||
std::vector<double> m_surfaceInterpolationWeights;
|
||||
std::vector<int> m_surfaceDisplacementCounts;
|
||||
std::vector<int> m_surfaceDisplacementOffsets;
|
||||
mutable mfem::Vector m_globalSurfaceDisplacementWorkspace;
|
||||
mutable mfem::Vector m_localGlobalSurfaceDualWorkspace;
|
||||
mutable mfem::Vector m_globalSurfaceDualWorkspace;
|
||||
};
|
||||
|
||||
[[nodiscard]] PreparedPowerLawRadialInteriorExtension compileInteriorDeformationExtension(
|
||||
const PowerLawRadialInteriorExtension &extension,
|
||||
const RadialDeformationExtensionCompilationContext &context
|
||||
);
|
||||
|
||||
class PreparedFixedInfinityRadialVacuumExtension;
|
||||
|
||||
class FixedInfinityRadialVacuumExtension final {
|
||||
public:
|
||||
using PreparedType = PreparedFixedInfinityRadialVacuumExtension;
|
||||
|
||||
[[nodiscard]] VacuumDeformationExtensionDescriptor descriptor() const noexcept;
|
||||
void validate() const;
|
||||
};
|
||||
|
||||
class PreparedFixedInfinityRadialVacuumExtension final {
|
||||
public:
|
||||
[[nodiscard]] VacuumDeformationExtensionDescriptor descriptor() const noexcept;
|
||||
[[nodiscard]] int surfaceDisplacementSize() const noexcept;
|
||||
[[nodiscard]] int vacuumDisplacementSize() const noexcept;
|
||||
[[nodiscard]] int scalarTrueDofCount() const noexcept;
|
||||
[[nodiscard]] bool hasVacuumSupport(int scalarTrueDof) const;
|
||||
[[nodiscard]] double radialWeight(int scalarTrueDof) const;
|
||||
[[nodiscard]] int surfaceInterpolationEntryCount(int scalarTrueDof) const;
|
||||
[[nodiscard]] int surfaceGlobalCoordinate(
|
||||
int scalarTrueDof,
|
||||
int interpolationEntry
|
||||
) const;
|
||||
[[nodiscard]] double surfaceInterpolationWeight(
|
||||
int scalarTrueDof,
|
||||
int interpolationEntry
|
||||
) const;
|
||||
|
||||
void buildVacuumDisplacement(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
mfem::Vector &vacuumDisplacement
|
||||
) const;
|
||||
void applyJacobian(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
const mfem::Vector &surfaceDisplacementDirection,
|
||||
mfem::Vector &vacuumDisplacementDirection
|
||||
) const;
|
||||
void applyJacobianTranspose(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
const mfem::Vector &vacuumDisplacementDual,
|
||||
mfem::Vector &surfaceDisplacementDual
|
||||
) const;
|
||||
void applyPullbackDerivative(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
const mfem::Vector &surfaceDisplacementDirection,
|
||||
const mfem::Vector &vacuumDisplacementDual,
|
||||
mfem::Vector &surfaceDisplacementDualAction
|
||||
) const;
|
||||
|
||||
private:
|
||||
friend PreparedFixedInfinityRadialVacuumExtension compileVacuumDeformationExtension(
|
||||
const FixedInfinityRadialVacuumExtension &extension,
|
||||
const RadialDeformationExtensionCompilationContext &context
|
||||
);
|
||||
|
||||
PreparedFixedInfinityRadialVacuumExtension(
|
||||
const FixedInfinityRadialVacuumExtension &extension,
|
||||
const RadialDeformationExtensionCompilationContext &context
|
||||
);
|
||||
void requireSurfaceSize(const mfem::Vector &surfaceDisplacement) const;
|
||||
void requireVacuumSize(const mfem::Vector &vacuumDisplacement) const;
|
||||
void applyForward(
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
mfem::Vector &vacuumDisplacement
|
||||
) const;
|
||||
void applyTranspose(
|
||||
const mfem::Vector &vacuumDisplacementDual,
|
||||
mfem::Vector &surfaceDisplacementDual
|
||||
) const;
|
||||
|
||||
VacuumDeformationExtensionDescriptor m_descriptor;
|
||||
int m_surfaceDisplacementSize{0};
|
||||
int m_vacuumDisplacementSize{0};
|
||||
int m_spatialDimension{0};
|
||||
int m_globalSurfaceDisplacementSize{0};
|
||||
int m_globalSurfaceDisplacementOffset{0};
|
||||
MPI_Comm m_communicator{MPI_COMM_NULL};
|
||||
mfem::Array<int> m_vacuumSupport;
|
||||
mfem::Vector m_radialWeights;
|
||||
std::vector<int> m_surfaceInterpolationRowOffsets;
|
||||
std::vector<int> m_surfaceInterpolationGlobalCoordinates;
|
||||
std::vector<double> m_surfaceInterpolationWeights;
|
||||
std::vector<int> m_surfaceDisplacementCounts;
|
||||
std::vector<int> m_surfaceDisplacementOffsets;
|
||||
mutable mfem::Vector m_globalSurfaceDisplacementWorkspace;
|
||||
mutable mfem::Vector m_localGlobalSurfaceDualWorkspace;
|
||||
mutable mfem::Vector m_globalSurfaceDualWorkspace;
|
||||
};
|
||||
|
||||
[[nodiscard]] PreparedFixedInfinityRadialVacuumExtension compileVacuumDeformationExtension(
|
||||
const FixedInfinityRadialVacuumExtension &extension,
|
||||
const RadialDeformationExtensionCompilationContext &context
|
||||
);
|
||||
|
||||
static_assert(InteriorDeformationExtension<PowerLawRadialInteriorExtension>);
|
||||
static_assert(PreparedInteriorDeformationExtension<PreparedPowerLawRadialInteriorExtension>);
|
||||
static_assert(InteriorDeformationExtensionCompilable<
|
||||
PowerLawRadialInteriorExtension,
|
||||
RadialDeformationExtensionCompilationContext>);
|
||||
static_assert(VacuumDeformationExtension<FixedInfinityRadialVacuumExtension>);
|
||||
static_assert(PreparedVacuumDeformationExtension<PreparedFixedInfinityRadialVacuumExtension>);
|
||||
static_assert(VacuumDeformationExtensionCompilable<
|
||||
FixedInfinityRadialVacuumExtension,
|
||||
RadialDeformationExtensionCompilationContext>);
|
||||
} // namespace mean_field::deformation
|
||||
57
libmeanfield/interface/deformation/surface_prescription.cppm
Normal file
57
libmeanfield/interface/deformation/surface_prescription.cppm
Normal file
@@ -0,0 +1,57 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:deformation.surface_prescription;
|
||||
|
||||
export import :deformation.descriptors;
|
||||
|
||||
export namespace mean_field::deformation {
|
||||
template <typename Candidate>
|
||||
concept PreparedSurfaceDeformationPrescription = requires(
|
||||
const std::remove_cvref_t<Candidate> &preparedPrescription,
|
||||
const mfem::Vector ¶meters,
|
||||
const mfem::Vector ¶meterDirection,
|
||||
const mfem::Vector &surfaceDisplacementDual,
|
||||
mfem::Vector &surfaceDisplacement,
|
||||
mfem::Vector ¶meterDual
|
||||
) {
|
||||
{ preparedPrescription.descriptor() } noexcept -> std::same_as<SurfaceDeformationDescriptor>;
|
||||
{ preparedPrescription.parameterCount() } noexcept -> std::same_as<int>;
|
||||
{ preparedPrescription.surfaceDisplacementSize() } noexcept -> std::same_as<int>;
|
||||
{ preparedPrescription.buildSurfaceDisplacement(parameters, surfaceDisplacement) } -> std::same_as<void>;
|
||||
{
|
||||
preparedPrescription.applyJacobian(parameters, parameterDirection, surfaceDisplacement)
|
||||
} -> std::same_as<void>;
|
||||
{
|
||||
preparedPrescription.applyJacobianTranspose(parameters, surfaceDisplacementDual, parameterDual)
|
||||
} -> std::same_as<void>;
|
||||
{
|
||||
preparedPrescription.applyPullbackDerivative(
|
||||
parameters, parameterDirection, surfaceDisplacementDual, parameterDual
|
||||
)
|
||||
} -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept SurfaceDeformationPrescription = requires(const std::remove_cvref_t<Candidate> &prescription) {
|
||||
typename std::remove_cvref_t<Candidate>::PreparedType;
|
||||
requires PreparedSurfaceDeformationPrescription<typename std::remove_cvref_t<Candidate>::PreparedType>;
|
||||
{ prescription.descriptor() } noexcept -> std::same_as<SurfaceDeformationDescriptor>;
|
||||
{ prescription.validate() } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Prescription, typename CompilationContext>
|
||||
concept SurfaceDeformationCompilable =
|
||||
SurfaceDeformationPrescription<Prescription> && requires(
|
||||
const std::remove_cvref_t<Prescription> &prescription,
|
||||
const std::remove_cvref_t<CompilationContext> &context
|
||||
) {
|
||||
{
|
||||
compileSurfaceDeformationPrescription(prescription, context)
|
||||
} -> std::same_as<typename std::remove_cvref_t<Prescription>::PreparedType>;
|
||||
};
|
||||
} // namespace mean_field::deformation
|
||||
61
libmeanfield/interface/deformation/vacuum_extension.cppm
Normal file
61
libmeanfield/interface/deformation/vacuum_extension.cppm
Normal file
@@ -0,0 +1,61 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:deformation.vacuum_extension;
|
||||
|
||||
export import :deformation.descriptors;
|
||||
|
||||
export namespace mean_field::deformation {
|
||||
template <typename Candidate>
|
||||
concept PreparedVacuumDeformationExtension = requires(
|
||||
const std::remove_cvref_t<Candidate> &preparedExtension,
|
||||
const mfem::Vector &surfaceDisplacement,
|
||||
const mfem::Vector &surfaceDisplacementDirection,
|
||||
const mfem::Vector &vacuumDisplacementDual,
|
||||
mfem::Vector &vacuumDisplacement,
|
||||
mfem::Vector &surfaceDisplacementDual
|
||||
) {
|
||||
{ preparedExtension.descriptor() } noexcept -> std::same_as<VacuumDeformationExtensionDescriptor>;
|
||||
{ preparedExtension.surfaceDisplacementSize() } noexcept -> std::same_as<int>;
|
||||
{ preparedExtension.vacuumDisplacementSize() } noexcept -> std::same_as<int>;
|
||||
{ preparedExtension.scalarTrueDofCount() } noexcept -> std::same_as<int>;
|
||||
{ preparedExtension.hasVacuumSupport(0) } -> std::same_as<bool>;
|
||||
{ preparedExtension.buildVacuumDisplacement(surfaceDisplacement, vacuumDisplacement) } -> std::same_as<void>;
|
||||
{
|
||||
preparedExtension.applyJacobian(surfaceDisplacement, surfaceDisplacementDirection, vacuumDisplacement)
|
||||
} -> std::same_as<void>;
|
||||
{
|
||||
preparedExtension.applyJacobianTranspose(
|
||||
surfaceDisplacement, vacuumDisplacementDual, surfaceDisplacementDual
|
||||
)
|
||||
} -> std::same_as<void>;
|
||||
{
|
||||
preparedExtension.applyPullbackDerivative(
|
||||
surfaceDisplacement, surfaceDisplacementDirection, vacuumDisplacementDual, surfaceDisplacementDual
|
||||
)
|
||||
} -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Candidate>
|
||||
concept VacuumDeformationExtension = requires(const std::remove_cvref_t<Candidate> &extension) {
|
||||
typename std::remove_cvref_t<Candidate>::PreparedType;
|
||||
requires PreparedVacuumDeformationExtension<typename std::remove_cvref_t<Candidate>::PreparedType>;
|
||||
{ extension.descriptor() } noexcept -> std::same_as<VacuumDeformationExtensionDescriptor>;
|
||||
{ extension.validate() } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Extension, typename CompilationContext>
|
||||
concept VacuumDeformationExtensionCompilable =
|
||||
VacuumDeformationExtension<Extension> && requires(
|
||||
const std::remove_cvref_t<Extension> &extension,
|
||||
const std::remove_cvref_t<CompilationContext> &context
|
||||
) {
|
||||
{
|
||||
compileVacuumDeformationExtension(extension, context)
|
||||
} -> std::same_as<typename std::remove_cvref_t<Extension>::PreparedType>;
|
||||
};
|
||||
} // namespace mean_field::deformation
|
||||
@@ -28,6 +28,7 @@ export namespace mean_field::fem {
|
||||
|
||||
stroid::StroidMesh smesh;
|
||||
std::unique_ptr<mfem::ParMesh> mesh;
|
||||
std::unique_ptr<mfem::ParMesh> logicalReferenceMesh;
|
||||
|
||||
// =====================================================================
|
||||
// Compile-time field descriptors
|
||||
@@ -63,6 +64,14 @@ export namespace mean_field::fem {
|
||||
|
||||
std::unique_ptr<mfem::ParGridFunction> displacement;
|
||||
|
||||
/*
|
||||
* Scalar companion of the vector displacement space. Only its
|
||||
* StellarSurface true DOFs become surface-deformation coordinates.
|
||||
* Sharing displacementFec guarantees identical scalar basis
|
||||
* functions without duplicating the finite-element collection.
|
||||
*/
|
||||
std::unique_ptr<mfem::ParFiniteElementSpace> surfaceDeformationFes;
|
||||
|
||||
// =====================================================================
|
||||
// Density field
|
||||
// =====================================================================
|
||||
@@ -115,12 +124,13 @@ export namespace mean_field::fem {
|
||||
// =====================================================================
|
||||
|
||||
[[nodiscard]] bool okay() const {
|
||||
return mesh != nullptr &&
|
||||
return mesh != nullptr && logicalReferenceMesh != nullptr &&
|
||||
|
||||
gravityPotentialFec != nullptr && gravityPotentialFes != nullptr && gravityFluxFec != nullptr &&
|
||||
gravityFluxFes != nullptr &&
|
||||
|
||||
displacementFec != nullptr && displacementFes != nullptr && displacement != nullptr &&
|
||||
surfaceDeformationFes != nullptr &&
|
||||
|
||||
densityFec != nullptr && densityFes != nullptr &&
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ module;
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
@@ -889,6 +890,198 @@ export namespace mean_field::field {
|
||||
mfem::Array<int> m_trueToReduced;
|
||||
};
|
||||
|
||||
/*
|
||||
* Canonical coordinates on a scalar finite-element boundary.
|
||||
*
|
||||
* Unlike FieldBoundaryDofMap, this is not a row selection inside an
|
||||
* existing physical field. It defines an independent dense coordinate
|
||||
* vector whose entries are the locally owned scalar true DOFs on a
|
||||
* semantic boundary.
|
||||
*
|
||||
* Local coordinates follow increasing MFEM true-DOF order. Global
|
||||
* coordinates use the distributed-vector convention: ranks are ordered by
|
||||
* communicator rank and each rank contributes its locally sorted block.
|
||||
*/
|
||||
class ScalarBoundaryDofMap final {
|
||||
public:
|
||||
ScalarBoundaryDofMap() = default;
|
||||
|
||||
ScalarBoundaryDofMap(
|
||||
const int volumeTrueDofSize,
|
||||
const mfem::Array<int> &boundaryTrueDofs,
|
||||
const long long globalOffset,
|
||||
const long long globalSize
|
||||
)
|
||||
: m_boundaryDofs(
|
||||
volumeTrueDofSize,
|
||||
boundaryTrueDofs
|
||||
),
|
||||
m_globalOffset(globalOffset),
|
||||
m_globalSize(globalSize) {
|
||||
if (m_globalOffset < 0) {
|
||||
throw std::invalid_argument("ScalarBoundaryDofMap requires a non-negative global offset.");
|
||||
}
|
||||
if (m_globalSize < 0) {
|
||||
throw std::invalid_argument("ScalarBoundaryDofMap requires a non-negative global size.");
|
||||
}
|
||||
if (m_globalOffset + local_size() > m_globalSize) {
|
||||
throw std::invalid_argument(
|
||||
"ScalarBoundaryDofMap local coordinates lie outside the global boundary coordinate vector."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int volume_true_dof_size() const noexcept {
|
||||
return m_boundaryDofs.full_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] int local_size() const noexcept {
|
||||
return m_boundaryDofs.reduced_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] long long global_size() const noexcept {
|
||||
return m_globalSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] long long global_offset() const noexcept {
|
||||
return m_globalOffset;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return local_size() == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &boundary_true_dofs() const noexcept {
|
||||
return m_boundaryDofs.reduced_to_true();
|
||||
}
|
||||
|
||||
[[nodiscard]] int volume_true_dof(const int localBoundaryDof) const {
|
||||
return m_boundaryDofs.true_dof(localBoundaryDof);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<int> local_boundary_dof(const int volumeTrueDof) const {
|
||||
return m_boundaryDofs.reduced_dof(volumeTrueDof);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains_volume_true_dof(const int volumeTrueDof) const {
|
||||
return m_boundaryDofs.contains_true_dof(volumeTrueDof);
|
||||
}
|
||||
|
||||
[[nodiscard]] long long global_boundary_dof(const int localBoundaryDof) const {
|
||||
if (localBoundaryDof < 0 || localBoundaryDof >= local_size()) {
|
||||
throw std::out_of_range("Local boundary DOF is outside ScalarBoundaryDofMap.");
|
||||
}
|
||||
return m_globalOffset + localBoundaryDof;
|
||||
}
|
||||
|
||||
void gather(
|
||||
const mfem::Vector &volumeTrueValues,
|
||||
mfem::Vector &boundaryValues
|
||||
) const {
|
||||
m_boundaryDofs.gather(volumeTrueValues, boundaryValues);
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector gather(const mfem::Vector &volumeTrueValues) const {
|
||||
return m_boundaryDofs.gather(volumeTrueValues);
|
||||
}
|
||||
|
||||
void scatter(
|
||||
const mfem::Vector &boundaryValues,
|
||||
mfem::Vector &volumeTrueValues
|
||||
) const {
|
||||
m_boundaryDofs.scatter(boundaryValues, volumeTrueValues);
|
||||
}
|
||||
|
||||
[[nodiscard]] mfem::Vector scatter(const mfem::Vector &boundaryValues) const {
|
||||
return m_boundaryDofs.scatter(boundaryValues);
|
||||
}
|
||||
|
||||
private:
|
||||
FieldDofMap m_boundaryDofs;
|
||||
long long m_globalOffset{0};
|
||||
long long m_globalSize{0};
|
||||
};
|
||||
|
||||
template <
|
||||
utils::domain::IsBoundary BoundaryT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
requires(SchemaT::template contains_boundary<BoundaryT>())
|
||||
[[nodiscard]] ScalarBoundaryDofMap
|
||||
make_scalar_boundary_dof_map(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||
MFEM_VERIFY(
|
||||
!finiteElementSpace.Nonconforming(),
|
||||
"Scalar boundary true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||
);
|
||||
MFEM_VERIFY(finiteElementSpace.GetVDim() == 1, "ScalarBoundaryDofMap requires a scalar finite-element space.");
|
||||
|
||||
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||
MFEM_VERIFY(mesh != nullptr, "Scalar boundary DOF resolution requires an MFEM mesh.");
|
||||
|
||||
mfem::Array<int> boundaryVDofMarker(finiteElementSpace.GetVSize());
|
||||
boundaryVDofMarker = 0;
|
||||
|
||||
mfem::Array<int> boundaryElementVDofs;
|
||||
for (int boundaryElement = 0; boundaryElement < mesh->GetNBE(); ++boundaryElement) {
|
||||
if (!SchemaT::template boundary_attribute_matches<BoundaryT>(mesh->GetBdrAttribute(boundaryElement))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
finiteElementSpace.GetBdrElementVDofs(boundaryElement, boundaryElementVDofs);
|
||||
for (const int encodedVDof : boundaryElementVDofs) {
|
||||
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||
MFEM_VERIFY(
|
||||
vdof >= 0 && vdof < finiteElementSpace.GetVSize(),
|
||||
"MFEM returned an invalid scalar boundary vector DOF."
|
||||
);
|
||||
boundaryVDofMarker[vdof] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
finiteElementSpace.Synchronize(boundaryVDofMarker);
|
||||
|
||||
mfem::Array<int> boundaryTrueDofMarker(finiteElementSpace.GetTrueVSize());
|
||||
boundaryTrueDofMarker = 0;
|
||||
|
||||
for (int vdof = 0; vdof < boundaryVDofMarker.Size(); ++vdof) {
|
||||
if (boundaryVDofMarker[vdof] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||
if (trueDof < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(trueDof < boundaryTrueDofMarker.Size(), "MFEM returned an invalid scalar boundary true DOF.");
|
||||
boundaryTrueDofMarker[trueDof] = 1;
|
||||
}
|
||||
|
||||
mfem::Array<int> boundaryTrueDofs;
|
||||
mfem::FiniteElementSpace::MarkerToList(boundaryTrueDofMarker, boundaryTrueDofs);
|
||||
|
||||
const long long localSize = boundaryTrueDofs.Size();
|
||||
long long globalSize = 0;
|
||||
long long globalOffset = 0;
|
||||
|
||||
MPI_Allreduce(&localSize, &globalSize, 1, MPI_LONG_LONG, MPI_SUM, finiteElementSpace.GetComm());
|
||||
MPI_Exscan(&localSize, &globalOffset, 1, MPI_LONG_LONG, MPI_SUM, finiteElementSpace.GetComm());
|
||||
|
||||
if (finiteElementSpace.GetMyRank() == 0) {
|
||||
globalOffset = 0;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(globalSize > 0, "The requested semantic boundary has no scalar true DOFs.");
|
||||
|
||||
return ScalarBoundaryDofMap(finiteElementSpace.GetTrueVSize(), boundaryTrueDofs, globalOffset, globalSize);
|
||||
}
|
||||
|
||||
template <utils::domain::IsSchema SchemaT = utils::domain::CoreEnvelopeVacuumDomainSchema>
|
||||
requires(SchemaT::template contains_boundary<utils::domain::StellarSurface>())
|
||||
[[nodiscard]] ScalarBoundaryDofMap
|
||||
make_stellar_surface_scalar_dof_map(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||
return make_scalar_boundary_dof_map<utils::domain::StellarSurface, SchemaT>(finiteElementSpace);
|
||||
}
|
||||
|
||||
/*
|
||||
* Boundary rows expressed in a field's reduced solver ordering.
|
||||
*
|
||||
|
||||
@@ -3,6 +3,10 @@ module;
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
|
||||
#ifndef MEAN_FIELD_UNIFORM_POLYNOMIAL_ORDER_INCREMENT
|
||||
#define MEAN_FIELD_UNIFORM_POLYNOMIAL_ORDER_INCREMENT 0
|
||||
#endif
|
||||
|
||||
export module mean_field:field.registry;
|
||||
|
||||
export import :field.base;
|
||||
@@ -10,13 +14,16 @@ export import :quadrature.policy;
|
||||
export import :utils.domain;
|
||||
|
||||
export namespace mean_field::field {
|
||||
inline constexpr int uniformPolynomialOrderIncrement = MEAN_FIELD_UNIFORM_POLYNOMIAL_ORDER_INCREMENT;
|
||||
static_assert(uniformPolynomialOrderIncrement >= 0);
|
||||
|
||||
// =========================================================================
|
||||
// Density
|
||||
// =========================================================================
|
||||
|
||||
struct Density {
|
||||
static constexpr std::string_view name = "density";
|
||||
static constexpr int scalarOrder = 2;
|
||||
static constexpr int scalarOrder = 2 + uniformPolynomialOrderIncrement;
|
||||
|
||||
using Support = DomainSupport<utils::domain::Stellar>;
|
||||
|
||||
@@ -79,8 +86,8 @@ export namespace mean_field::field {
|
||||
struct Gravity {
|
||||
static constexpr std::string_view name = "gravity";
|
||||
|
||||
static constexpr int potentialOrder = 2;
|
||||
static constexpr int fluxOrder = 2;
|
||||
static constexpr int potentialOrder = 2 + uniformPolynomialOrderIncrement;
|
||||
static constexpr int fluxOrder = 2 + uniformPolynomialOrderIncrement;
|
||||
|
||||
using Support = DomainSupport<utils::domain::All>;
|
||||
|
||||
@@ -147,7 +154,7 @@ export namespace mean_field::field {
|
||||
|
||||
struct Displacement {
|
||||
static constexpr std::string_view name = "displacement";
|
||||
static constexpr int vectorOrder = 3;
|
||||
static constexpr int vectorOrder = 3 + uniformPolynomialOrderIncrement;
|
||||
|
||||
using Support = DomainSupport<utils::domain::All>;
|
||||
|
||||
@@ -229,7 +236,7 @@ export namespace mean_field::field {
|
||||
|
||||
struct Enthalpy {
|
||||
static constexpr std::string_view name = "specific_enthalpy";
|
||||
static constexpr int scalarOrder = 3;
|
||||
static constexpr int scalarOrder = 3 + uniformPolynomialOrderIncrement;
|
||||
|
||||
using Support = DomainSupport<utils::domain::Stellar>;
|
||||
|
||||
|
||||
@@ -65,6 +65,13 @@ export import :surface.constant;
|
||||
export import :surface.dependencies;
|
||||
export import :surface.compiled;
|
||||
export import :surface.compiler;
|
||||
export import :deformation.descriptors;
|
||||
export import :deformation.surface_prescription;
|
||||
export import :deformation.nodal_radial_surface;
|
||||
export import :deformation.interior_extension;
|
||||
export import :deformation.vacuum_extension;
|
||||
export import :deformation.radial_extensions;
|
||||
export import :deformation.domain_deformation;
|
||||
export import :model.stellar;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
export import :operators.prepared_centering_constraint;
|
||||
|
||||
@@ -5,8 +5,11 @@ module;
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:model.stellar;
|
||||
|
||||
export import :deformation.domain_deformation;
|
||||
export import :eos.runtime;
|
||||
export import :model.structure.base;
|
||||
export import :surface.compiler;
|
||||
@@ -35,38 +38,85 @@ export namespace mean_field::models {
|
||||
std::remove_cvref_t<decltype(std::declval<const std::remove_cvref_t<Candidate> &>().equationOfState())>;
|
||||
|
||||
template <typename Candidate, typename EquationOfState>
|
||||
concept SurfacePrescription =
|
||||
concept SurfaceCondition =
|
||||
surface::ConstantPressureSurfaceType<Candidate> &&
|
||||
surface::PressureSurfaceCompilable<surface::BarotropicSurfaceFormulation, std::remove_cvref_t<EquationOfState>>;
|
||||
|
||||
template <StructurePrescription Structure>
|
||||
requires SurfacePrescription<surface::ConstantPressureSurface, StructureEquationOfStateT<Structure>>
|
||||
template <
|
||||
StructurePrescription Structure,
|
||||
deformation::SurfaceDeformationPrescription SurfaceDeformation = deformation::NodalRadialSurface,
|
||||
deformation::InteriorDeformationExtension StellarInteriorDeformation =
|
||||
deformation::PowerLawRadialInteriorExtension,
|
||||
deformation::VacuumDeformationExtension VacuumDeformation = deformation::FixedInfinityRadialVacuumExtension>
|
||||
requires SurfaceCondition<surface::ConstantPressureSurface, StructureEquationOfStateT<Structure>>
|
||||
class StellarModel final {
|
||||
public:
|
||||
using StructurePrescriptionType = Structure;
|
||||
using SurfacePrescriptionType = surface::ConstantPressureSurface;
|
||||
using EquationOfStateType = StructureEquationOfStateT<Structure>;
|
||||
using StructurePrescriptionType = Structure;
|
||||
using SurfaceConditionType = surface::ConstantPressureSurface;
|
||||
using SurfaceDeformationPrescriptionType = SurfaceDeformation;
|
||||
using StellarInteriorDeformationExtensionType = StellarInteriorDeformation;
|
||||
using VacuumDeformationExtensionType = VacuumDeformation;
|
||||
using EquationOfStateType = StructureEquationOfStateT<Structure>;
|
||||
using SurfaceConstraintType =
|
||||
surface::CompiledPressureSurfaceConstraintT<surface::BarotropicSurfaceFormulation, EquationOfStateType>;
|
||||
|
||||
template <typename StructureArgument>
|
||||
requires std::same_as<
|
||||
std::remove_cvref_t<StructureArgument>,
|
||||
Structure>
|
||||
std::remove_cvref_t<StructureArgument>,
|
||||
Structure>
|
||||
explicit StellarModel(
|
||||
StructureArgument &&structurePrescription,
|
||||
const surface::ConstantPressureSurface surfacePrescription
|
||||
const surface::ConstantPressureSurface surfaceCondition
|
||||
)
|
||||
: StellarModel(
|
||||
std::forward<StructureArgument>(structurePrescription),
|
||||
surfaceCondition,
|
||||
deformation::NodalRadialSurface{defaultReferenceCenter()},
|
||||
deformation::PowerLawRadialInteriorExtension{},
|
||||
deformation::FixedInfinityRadialVacuumExtension{}
|
||||
) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename StructureArgument,
|
||||
typename SurfaceDeformationArgument,
|
||||
typename StellarInteriorDeformationArgument,
|
||||
typename VacuumDeformationArgument>
|
||||
requires std::same_as<
|
||||
std::remove_cvref_t<StructureArgument>,
|
||||
Structure> &&
|
||||
std::same_as<
|
||||
std::remove_cvref_t<SurfaceDeformationArgument>,
|
||||
SurfaceDeformation> &&
|
||||
std::same_as<
|
||||
std::remove_cvref_t<StellarInteriorDeformationArgument>,
|
||||
StellarInteriorDeformation> &&
|
||||
std::same_as<
|
||||
std::remove_cvref_t<VacuumDeformationArgument>,
|
||||
VacuumDeformation>
|
||||
explicit StellarModel(
|
||||
StructureArgument &&structurePrescription,
|
||||
const surface::ConstantPressureSurface surfaceCondition,
|
||||
SurfaceDeformationArgument &&surfaceDeformation,
|
||||
StellarInteriorDeformationArgument &&stellarInteriorDeformation,
|
||||
VacuumDeformationArgument &&vacuumDeformation
|
||||
)
|
||||
: m_structurePrescription(
|
||||
std::make_unique<Structure>(std::forward<StructureArgument>(structurePrescription))
|
||||
),
|
||||
m_surfacePrescription(std::make_unique<surface::ConstantPressureSurface>(surfacePrescription)),
|
||||
m_compiledSurfaceConstraint(
|
||||
std::make_unique<SurfaceConstraintType>(validateAndCompileSurface(
|
||||
*m_structurePrescription,
|
||||
*m_surfacePrescription
|
||||
))
|
||||
) {
|
||||
m_surfaceCondition(std::make_unique<surface::ConstantPressureSurface>(surfaceCondition)),
|
||||
m_surfaceDeformation(
|
||||
std::make_unique<SurfaceDeformation>(std::forward<SurfaceDeformationArgument>(surfaceDeformation))
|
||||
),
|
||||
m_stellarInteriorDeformation(
|
||||
std::make_unique<StellarInteriorDeformation>(
|
||||
std::forward<StellarInteriorDeformationArgument>(stellarInteriorDeformation)
|
||||
)
|
||||
),
|
||||
m_vacuumDeformation(
|
||||
std::make_unique<VacuumDeformation>(std::forward<VacuumDeformationArgument>(vacuumDeformation))
|
||||
),
|
||||
m_compiledSurfaceConstraint(std::make_unique<SurfaceConstraintType>(validateAndCompileConfiguration())) {
|
||||
}
|
||||
|
||||
~StellarModel() = default;
|
||||
@@ -80,8 +130,20 @@ export namespace mean_field::models {
|
||||
return *m_structurePrescription;
|
||||
}
|
||||
|
||||
[[nodiscard]] const surface::ConstantPressureSurface &surfacePrescription() const noexcept {
|
||||
return *m_surfacePrescription;
|
||||
[[nodiscard]] const surface::ConstantPressureSurface &surfaceCondition() const noexcept {
|
||||
return *m_surfaceCondition;
|
||||
}
|
||||
|
||||
[[nodiscard]] const SurfaceDeformation &surfaceDeformationPrescription() const noexcept {
|
||||
return *m_surfaceDeformation;
|
||||
}
|
||||
|
||||
[[nodiscard]] const StellarInteriorDeformation &stellarInteriorDeformationExtension() const noexcept {
|
||||
return *m_stellarInteriorDeformation;
|
||||
}
|
||||
|
||||
[[nodiscard]] const VacuumDeformation &vacuumDeformationExtension() const noexcept {
|
||||
return *m_vacuumDeformation;
|
||||
}
|
||||
|
||||
[[nodiscard]] const EquationOfStateType &equationOfState() const noexcept {
|
||||
@@ -100,20 +162,36 @@ export namespace mean_field::models {
|
||||
return *m_compiledSurfaceConstraint;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto compileDomainDeformation(fem::FEM &finiteElementModel) const {
|
||||
return deformation::compileDomainDeformation(
|
||||
surfaceDeformationPrescription(), stellarInteriorDeformationExtension(), vacuumDeformationExtension(),
|
||||
finiteElementModel
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] static SurfaceConstraintType validateAndCompileSurface(
|
||||
const Structure &structurePrescription,
|
||||
const surface::ConstantPressureSurface &surfacePrescription
|
||||
) {
|
||||
structurePrescription.validate();
|
||||
[[nodiscard]] static mfem::Vector defaultReferenceCenter() {
|
||||
mfem::Vector center(3);
|
||||
center = 0.0;
|
||||
return center;
|
||||
}
|
||||
|
||||
[[nodiscard]] SurfaceConstraintType validateAndCompileConfiguration() const {
|
||||
m_structurePrescription->validate();
|
||||
m_surfaceDeformation->validate();
|
||||
m_stellarInteriorDeformation->validate();
|
||||
m_vacuumDeformation->validate();
|
||||
|
||||
return surface::compilePressureSurfaceConstraint<surface::BarotropicSurfaceFormulation>(
|
||||
surfacePrescription, structurePrescription.equationOfState()
|
||||
*m_surfaceCondition, m_structurePrescription->equationOfState()
|
||||
);
|
||||
}
|
||||
|
||||
std::unique_ptr<Structure> m_structurePrescription;
|
||||
std::unique_ptr<surface::ConstantPressureSurface> m_surfacePrescription;
|
||||
std::unique_ptr<surface::ConstantPressureSurface> m_surfaceCondition;
|
||||
std::unique_ptr<SurfaceDeformation> m_surfaceDeformation;
|
||||
std::unique_ptr<StellarInteriorDeformation> m_stellarInteriorDeformation;
|
||||
std::unique_ptr<VacuumDeformation> m_vacuumDeformation;
|
||||
std::unique_ptr<SurfaceConstraintType> m_compiledSurfaceConstraint;
|
||||
};
|
||||
|
||||
@@ -121,12 +199,42 @@ export namespace mean_field::models {
|
||||
StellarModel(
|
||||
Structure &&,
|
||||
surface::ConstantPressureSurface
|
||||
) -> StellarModel<std::remove_cvref_t<Structure>>;
|
||||
)
|
||||
-> StellarModel<
|
||||
std::remove_cvref_t<Structure>,
|
||||
deformation::NodalRadialSurface,
|
||||
deformation::PowerLawRadialInteriorExtension,
|
||||
deformation::FixedInfinityRadialVacuumExtension>;
|
||||
|
||||
template <
|
||||
typename Structure,
|
||||
typename SurfaceDeformation,
|
||||
typename StellarInteriorDeformation,
|
||||
typename VacuumDeformation>
|
||||
StellarModel(
|
||||
Structure &&,
|
||||
surface::ConstantPressureSurface,
|
||||
SurfaceDeformation &&,
|
||||
StellarInteriorDeformation &&,
|
||||
VacuumDeformation &&
|
||||
)
|
||||
-> StellarModel<
|
||||
std::remove_cvref_t<Structure>,
|
||||
std::remove_cvref_t<SurfaceDeformation>,
|
||||
std::remove_cvref_t<StellarInteriorDeformation>,
|
||||
std::remove_cvref_t<VacuumDeformation>>;
|
||||
|
||||
namespace detail {
|
||||
template <typename Candidate> struct IsStellarModel : std::false_type { };
|
||||
|
||||
template <typename Structure> struct IsStellarModel<StellarModel<Structure>> : std::true_type { };
|
||||
template <
|
||||
typename Structure,
|
||||
typename SurfaceDeformation,
|
||||
typename StellarInteriorDeformation,
|
||||
typename VacuumDeformation>
|
||||
struct IsStellarModel<
|
||||
StellarModel<Structure, SurfaceDeformation, StellarInteriorDeformation, VacuumDeformation>>
|
||||
: std::true_type { };
|
||||
} // namespace detail
|
||||
|
||||
template <typename Candidate>
|
||||
@@ -143,7 +251,10 @@ export namespace mean_field::models {
|
||||
m_makeInitialSeed(&makeInitialSeedFor<typename std::remove_cvref_t<Model>::StructurePrescriptionType>),
|
||||
m_targetMass(model.targetMass()),
|
||||
m_surfaceCondition(model.compiledSurfaceConstraint().descriptor()),
|
||||
m_surfaceDependencies(model.compiledSurfaceConstraint().runtimeDependencies()) {
|
||||
m_surfaceDependencies(model.compiledSurfaceConstraint().runtimeDependencies()),
|
||||
m_surfaceDeformation(model.surfaceDeformationPrescription().descriptor()),
|
||||
m_stellarInteriorDeformation(model.stellarInteriorDeformationExtension().descriptor()),
|
||||
m_vacuumDeformation(model.vacuumDeformationExtension().descriptor()) {
|
||||
}
|
||||
|
||||
[[nodiscard]] eos::EquationOfStateView equationOfState() const noexcept {
|
||||
@@ -166,6 +277,18 @@ export namespace mean_field::models {
|
||||
return m_surfaceDependencies;
|
||||
}
|
||||
|
||||
[[nodiscard]] deformation::SurfaceDeformationDescriptor surfaceDeformation() const noexcept {
|
||||
return m_surfaceDeformation;
|
||||
}
|
||||
|
||||
[[nodiscard]] deformation::InteriorDeformationExtensionDescriptor stellarInteriorDeformation() const noexcept {
|
||||
return m_stellarInteriorDeformation;
|
||||
}
|
||||
|
||||
[[nodiscard]] deformation::VacuumDeformationExtensionDescriptor vacuumDeformation() const noexcept {
|
||||
return m_vacuumDeformation;
|
||||
}
|
||||
|
||||
private:
|
||||
using MakeInitialSeedFunction = structure::StructureSeed (*)(
|
||||
const void *,
|
||||
@@ -186,5 +309,8 @@ export namespace mean_field::models {
|
||||
double m_targetMass;
|
||||
surface::PressureSurfaceDescriptor m_surfaceCondition;
|
||||
surface::RuntimeSurfaceConstraintDependencies m_surfaceDependencies;
|
||||
deformation::SurfaceDeformationDescriptor m_surfaceDeformation;
|
||||
deformation::InteriorDeformationExtensionDescriptor m_stellarInteriorDeformation;
|
||||
deformation::VacuumDeformationExtensionDescriptor m_vacuumDeformation;
|
||||
};
|
||||
} // namespace mean_field::models
|
||||
|
||||
@@ -4,6 +4,7 @@ module;
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
@@ -18,7 +19,6 @@ export import :operators.context.gravity_field;
|
||||
export import :operators.gravity_field;
|
||||
export import :operators.gravity_field_jacobian;
|
||||
export import :operators.prepared_barotropic_closure;
|
||||
export import :operators.prepared_centering_constraint;
|
||||
export import :operators.prepared_displacement_residual;
|
||||
export import :operators.prepared_hydrostatic_equilibrium;
|
||||
export import :operators.prepared_mass_normalization;
|
||||
@@ -37,7 +37,7 @@ export namespace mean_field::operators {
|
||||
struct StellarEquilibriumDependencies final {
|
||||
StellarEquilibriumDependencyStamp discretization;
|
||||
StellarEquilibriumDependencyStamp density;
|
||||
StellarEquilibriumDependencyStamp displacement;
|
||||
StellarEquilibriumDependencyStamp surfaceDeformation;
|
||||
StellarEquilibriumDependencyStamp gravityGradient;
|
||||
StellarEquilibriumDependencyStamp gravityPotential;
|
||||
StellarEquilibriumDependencyStamp enthalpy;
|
||||
@@ -55,17 +55,18 @@ export namespace mean_field::operators {
|
||||
PreparedDisplacementResidualReport displacement;
|
||||
PreparedMassNormalizationReport massNormalization;
|
||||
PreparedSurfaceConstraintReport surfaceConstraint;
|
||||
PreparedCenteringConstraintReport centeringConstraint;
|
||||
deformation::DomainDeformationGeometryReport generatedGeometry;
|
||||
StellarEquilibriumDependencyStamp generatedDisplacement;
|
||||
bool generatedVolumeDisplacement{false};
|
||||
bool assembledResidual{false};
|
||||
|
||||
[[nodiscard]] bool DidAnyChildWork() const noexcept {
|
||||
return gravity.DidAnyWork() || barotropicClosure.DidAnyWork() || hydrostatic.DidAnyWork() ||
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork() || surfaceConstraint.DidAnyWork() ||
|
||||
centeringConstraint.DidAnyWork();
|
||||
displacement.DidAnyWork() || massNormalization.DidAnyWork() || surfaceConstraint.DidAnyWork();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||
return DidAnyChildWork() || assembledResidual;
|
||||
return DidAnyChildWork() || generatedVolumeDisplacement || assembledResidual;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,11 +74,13 @@ export namespace mean_field::operators {
|
||||
std::uint64_t residualAssemblies{0};
|
||||
std::uint64_t residualApplications{0};
|
||||
std::uint64_t jacobianApplications{0};
|
||||
std::uint64_t generatedGeometryBuilds{0};
|
||||
|
||||
constexpr auto operator<=>(const PreparedStellarEquilibriumStatistics &) const = default;
|
||||
};
|
||||
|
||||
using StellarEquilibriumLayout = utils::blocks::form_layout<utils::blocks::barotropic_equilibrium_form>;
|
||||
using StellarEquilibriumLayout =
|
||||
utils::blocks::form_layout<utils::blocks::surface_deformed_stellar_equilibrium_form>;
|
||||
|
||||
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
||||
public:
|
||||
@@ -99,7 +102,8 @@ export namespace mean_field::operators {
|
||||
domainMapper,
|
||||
stellarModel.equationOfState(),
|
||||
stellarModel.targetMass(),
|
||||
PressureSurfaceConstraintView{stellarModel.compiledSurfaceConstraint()}
|
||||
PressureSurfaceConstraintView{stellarModel.compiledSurfaceConstraint()},
|
||||
deformation::PreparedDomainDeformationRuntime{stellarModel.compileDomainDeformation(f)}
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -138,19 +142,27 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedPressureSurfaceConstraint &GetSurfaceConstraintOperator() const noexcept;
|
||||
[[nodiscard]] const PreparedCenteringConstraint &GetCenteringConstraintOperator() const noexcept;
|
||||
[[nodiscard]] const deformation::PreparedDomainDeformationRuntime &GetDomainDeformation() const noexcept;
|
||||
[[nodiscard]] const mfem::Vector &GetSurfaceDeformationParameters() const;
|
||||
[[nodiscard]] const mfem::Vector &GetGeneratedVolumeDisplacement() const;
|
||||
[[nodiscard]] const mfem::Vector &GetFullMechanicalResidual() const;
|
||||
[[nodiscard]] const StellarEquilibriumDependencyStamp &GetGeneratedDisplacementDependency() const;
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
|
||||
static ConstructionData MakeConstructionData(fem::FEM &f);
|
||||
static ConstructionData MakeConstructionData(
|
||||
fem::FEM &f,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
fem::FEM &f,
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const eos::Polytrope &equationOfState,
|
||||
double targetMass,
|
||||
PressureSurfaceConstraintView surfaceConstraint
|
||||
PressureSurfaceConstraintView surfaceConstraint,
|
||||
deformation::PreparedDomainDeformationRuntime domainDeformation
|
||||
);
|
||||
|
||||
PreparedStellarEquilibriumOperator(
|
||||
@@ -177,9 +189,14 @@ export namespace mean_field::operators {
|
||||
PreparedDisplacementResidualOperator m_displacementOperator;
|
||||
PreparedMassNormalizationOperator m_massNormalizationOperator;
|
||||
PreparedPressureSurfaceConstraint m_surfaceConstraintOperator;
|
||||
PreparedCenteringConstraint m_centeringConstraintOperator;
|
||||
deformation::PreparedDomainDeformationRuntime m_domainDeformation;
|
||||
|
||||
StellarEquilibriumDependencies m_preparedDependencies;
|
||||
StellarEquilibriumDependencyStamp m_generatedDisplacementDependency;
|
||||
deformation::DomainDeformationGeometryReport m_generatedGeometryReport;
|
||||
mfem::Vector m_surfaceDeformationParameters;
|
||||
mfem::Vector m_generatedVolumeDisplacement;
|
||||
mfem::Vector m_fullMechanicalResidual;
|
||||
mfem::Vector m_cachedResidual;
|
||||
double m_targetMass{0.0};
|
||||
|
||||
@@ -189,5 +206,9 @@ export namespace mean_field::operators {
|
||||
mfem::Vector m_gravityState;
|
||||
|
||||
mutable mfem::Vector m_gravityDirection;
|
||||
mutable mfem::Vector m_volumeDisplacementDirection;
|
||||
mutable mfem::Vector m_fullMechanicalAction;
|
||||
mutable mfem::Vector m_surfaceShapeAction;
|
||||
mutable mfem::Vector m_pullbackDerivativeAction;
|
||||
};
|
||||
} // namespace mean_field::operators
|
||||
|
||||
@@ -15,7 +15,7 @@ export namespace mean_field::surface {
|
||||
};
|
||||
|
||||
/*
|
||||
* The only physical surface prescription currently supported by
|
||||
* The only physical surface condition currently supported by
|
||||
* MeanField. It says nothing about which thermodynamic variable appears
|
||||
* in a nonlinear state vector; resolving pressure into that representation
|
||||
* is an EOS responsibility.
|
||||
|
||||
@@ -59,6 +59,19 @@ export namespace mean_field::utils::blocks {
|
||||
static inline constexpr geometry geometry_term{};
|
||||
};
|
||||
|
||||
struct surface_deformation final : field {
|
||||
struct parameters final : term {
|
||||
struct value final : value_block_base { };
|
||||
};
|
||||
|
||||
struct shape_equilibrium final : term {
|
||||
struct residual final : residual_block_base { };
|
||||
};
|
||||
|
||||
static inline constexpr parameters parameters_term{};
|
||||
static inline constexpr shape_equilibrium shape_equilibrium_term{};
|
||||
};
|
||||
|
||||
struct gravity final : field {
|
||||
struct gradient final : term {
|
||||
struct value final : value_block_base { };
|
||||
@@ -99,6 +112,7 @@ export namespace mean_field::utils::blocks {
|
||||
|
||||
inline constexpr density density_field{};
|
||||
inline constexpr displacement displacement_field{};
|
||||
inline constexpr surface_deformation surface_deformation_field{};
|
||||
inline constexpr gravity gravity_field{};
|
||||
inline constexpr enthalpy enthalpy_field{};
|
||||
inline constexpr barotropic_constant barotropic_constant_field{};
|
||||
@@ -422,6 +436,59 @@ export namespace mean_field::utils::blocks {
|
||||
density::mass::value,
|
||||
displacement::geometry::value>>;
|
||||
|
||||
// Root stellar-equilibrium coordinates:
|
||||
// [rho, q, g, Phi, h, C], where q parameterizes the stellar surface.
|
||||
// Full-volume displacement remains an internal coordinate of the child
|
||||
// operators above and is generated from q by the domain-deformation map.
|
||||
using surface_deformed_stellar_equilibrium_form = block_form<
|
||||
type_list<
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value,
|
||||
gravity::gradient::value,
|
||||
gravity::poisson::value,
|
||||
enthalpy::specific::value,
|
||||
barotropic_constant::mass_normalization::value>,
|
||||
type_list<
|
||||
gravity::gradient::residual,
|
||||
gravity::poisson::residual,
|
||||
density::mass::residual,
|
||||
surface_deformation::shape_equilibrium::residual,
|
||||
enthalpy::specific::residual,
|
||||
barotropic_constant::mass_normalization::residual>>;
|
||||
|
||||
using surface_deformed_stellar_equilibrium_jacobian_form = type_list<
|
||||
block_row<
|
||||
gravity::gradient::residual,
|
||||
gravity::gradient::value,
|
||||
gravity::poisson::value,
|
||||
surface_deformation::parameters::value>,
|
||||
block_row<
|
||||
gravity::poisson::residual,
|
||||
gravity::gradient::value,
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value>,
|
||||
block_row<
|
||||
density::mass::residual,
|
||||
density::mass::value,
|
||||
enthalpy::specific::value,
|
||||
surface_deformation::parameters::value>,
|
||||
block_row<
|
||||
surface_deformation::shape_equilibrium::residual,
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value,
|
||||
gravity::gradient::value,
|
||||
enthalpy::specific::value>,
|
||||
block_row<
|
||||
enthalpy::specific::residual,
|
||||
enthalpy::specific::value,
|
||||
gravity::poisson::value,
|
||||
surface_deformation::parameters::value,
|
||||
barotropic_constant::mass_normalization::value>,
|
||||
block_row<
|
||||
barotropic_constant::mass_normalization::residual,
|
||||
density::mass::value,
|
||||
surface_deformation::parameters::value>>;
|
||||
|
||||
// Columns: [d, h]
|
||||
// Rows: [R_d]
|
||||
using pressure_force_form = block_form<
|
||||
@@ -438,4 +505,8 @@ export namespace mean_field::utils::blocks {
|
||||
static_assert(valid_jacobian_form<
|
||||
barotropic_equilibrium_form,
|
||||
barotropic_equilibrium_jacobian_form>);
|
||||
|
||||
static_assert(valid_jacobian_form<
|
||||
surface_deformed_stellar_equilibrium_form,
|
||||
surface_deformed_stellar_equilibrium_jacobian_form>);
|
||||
} // namespace mean_field::utils::blocks
|
||||
|
||||
Reference in New Issue
Block a user