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.
1062 lines
52 KiB
C++
1062 lines
52 KiB
C++
module;
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <format>
|
|
#include <limits>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <mfem.hpp>
|
|
#include <mpi.h>
|
|
|
|
module mean_field;
|
|
|
|
import :deformation.radial_extensions;
|
|
|
|
namespace mean_field::deformation {
|
|
namespace {
|
|
struct LocatedBoundaryPoint final {
|
|
int boundaryElement{-1};
|
|
mfem::IntegrationPoint integrationPoint;
|
|
double residualNorm{std::numeric_limits<double>::infinity()};
|
|
};
|
|
|
|
[[nodiscard]] InteriorDeformationExtensionDescriptor
|
|
powerLawInteriorDescriptor(const int spatialDimension) noexcept {
|
|
return {
|
|
.name = "PowerLawRadialInteriorExtension",
|
|
.spatialDimension = spatialDimension,
|
|
.linearOnReferenceGeometry = true,
|
|
.requiresRadialFoliation = true,
|
|
.requiresAuxiliarySolve = false,
|
|
.hasExactDerivativeTranspose = true,
|
|
.hasExactPullbackDerivative = true,
|
|
.centerBehavior = InteriorCenterBehavior::FixedAtReferenceCenter
|
|
};
|
|
}
|
|
|
|
[[nodiscard]] VacuumDeformationExtensionDescriptor
|
|
fixedInfinityVacuumDescriptor(const int spatialDimension) noexcept {
|
|
return {
|
|
.name = "FixedInfinityRadialVacuumExtension",
|
|
.spatialDimension = spatialDimension,
|
|
.linearOnReferenceGeometry = true,
|
|
.requiresRadialFoliation = true,
|
|
.requiresAuxiliarySolve = false,
|
|
.hasExactDerivativeTranspose = true,
|
|
.hasExactPullbackDerivative = true,
|
|
.outerBoundaryBehavior = VacuumOuterBoundaryBehavior::FixedAtReferenceInfinity
|
|
};
|
|
}
|
|
|
|
[[nodiscard]] double logicalInfinityRadius(const mfem::Vector &position) {
|
|
double radius = 0.0;
|
|
for (int component = 0; component < position.Size(); ++component) {
|
|
radius = std::max(radius, std::abs(position(component)));
|
|
}
|
|
return radius;
|
|
}
|
|
|
|
[[nodiscard]] double euclideanDistance(
|
|
const mfem::Vector &first,
|
|
const mfem::Vector &second
|
|
) {
|
|
double squaredDistance = 0.0;
|
|
for (int component = 0; component < first.Size(); ++component) {
|
|
const double difference = first(component) - second(component);
|
|
squaredDistance += difference * difference;
|
|
}
|
|
return std::sqrt(squaredDistance);
|
|
}
|
|
|
|
[[nodiscard]] mfem::Array<int> buildTrueDofSupport(
|
|
mfem::ParFiniteElementSpace &scalarFiniteElementSpace,
|
|
const mfem::Array<int> &materialMarker
|
|
) {
|
|
const mfem::Mesh *mesh = scalarFiniteElementSpace.GetMesh();
|
|
if (mesh == nullptr) {
|
|
throw std::invalid_argument("Logical radial extension support compilation requires a mesh.");
|
|
}
|
|
if (materialMarker.Size() != mesh->attributes.Max()) {
|
|
throw std::invalid_argument(
|
|
"A logical radial extension material marker does not cover every material attribute."
|
|
);
|
|
}
|
|
|
|
mfem::Array<int> localDofMarker(scalarFiniteElementSpace.GetVSize());
|
|
localDofMarker = 0;
|
|
mfem::Array<int> elementDofs;
|
|
|
|
for (int element = 0; element < mesh->GetNE(); ++element) {
|
|
const int attribute = mesh->GetAttribute(element);
|
|
if (attribute <= 0 || attribute > materialMarker.Size() || materialMarker[attribute - 1] == 0) {
|
|
continue;
|
|
}
|
|
|
|
scalarFiniteElementSpace.GetElementDofs(element, elementDofs);
|
|
for (const int encodedDof : elementDofs) {
|
|
localDofMarker[mfem::FiniteElementSpace::DecodeDof(encodedDof)] = 1;
|
|
}
|
|
}
|
|
|
|
scalarFiniteElementSpace.Synchronize(localDofMarker);
|
|
mfem::Array<int> trueDofMarker(scalarFiniteElementSpace.GetTrueVSize());
|
|
trueDofMarker = 0;
|
|
|
|
for (int localDof = 0; localDof < localDofMarker.Size(); ++localDof) {
|
|
if (localDofMarker[localDof] == 0) {
|
|
continue;
|
|
}
|
|
const int trueDof = scalarFiniteElementSpace.GetLocalTDofNumber(localDof);
|
|
if (trueDof >= 0) {
|
|
trueDofMarker[trueDof] = 1;
|
|
}
|
|
}
|
|
|
|
return trueDofMarker;
|
|
}
|
|
|
|
[[nodiscard]] mfem::Vector buildLogicalTrueDofPositions(
|
|
mfem::ParFiniteElementSpace &scalarFiniteElementSpace,
|
|
mfem::ParMesh &logicalReferenceMesh
|
|
) {
|
|
const mfem::Mesh *physicalMesh = scalarFiniteElementSpace.GetMesh();
|
|
if (physicalMesh == nullptr || physicalMesh->GetNE() != logicalReferenceMesh.GetNE()) {
|
|
throw std::invalid_argument(
|
|
"Logical radial extension compilation requires paired physical and logical elements."
|
|
);
|
|
}
|
|
|
|
const int spatialDimension = logicalReferenceMesh.SpaceDimension();
|
|
const int trueDofCount = scalarFiniteElementSpace.GetTrueVSize();
|
|
mfem::Vector logicalPositions(spatialDimension * trueDofCount);
|
|
mfem::Array<int> processed(trueDofCount);
|
|
processed = 0;
|
|
|
|
mfem::Array<int> elementDofs;
|
|
mfem::Vector logicalPosition(spatialDimension);
|
|
|
|
for (int element = 0; element < physicalMesh->GetNE(); ++element) {
|
|
if (physicalMesh->GetElementGeometry(element) != logicalReferenceMesh.GetElementGeometry(element) ||
|
|
physicalMesh->GetAttribute(element) != logicalReferenceMesh.GetAttribute(element)) {
|
|
throw std::invalid_argument("A physical element does not match its logical reference element.");
|
|
}
|
|
|
|
const mfem::FiniteElement &finiteElement = *scalarFiniteElementSpace.GetFE(element);
|
|
const mfem::IntegrationRule &nodes = finiteElement.GetNodes();
|
|
scalarFiniteElementSpace.GetElementDofs(element, elementDofs);
|
|
if (nodes.GetNPoints() != elementDofs.Size()) {
|
|
throw std::invalid_argument(
|
|
"The scalar companion basis is not nodal on a logical reference element."
|
|
);
|
|
}
|
|
|
|
mfem::ElementTransformation *logicalTransformation =
|
|
logicalReferenceMesh.GetElementTransformation(element);
|
|
if (logicalTransformation == nullptr) {
|
|
throw std::invalid_argument("A logical reference element has no transformation.");
|
|
}
|
|
|
|
for (int localElementDof = 0; localElementDof < elementDofs.Size(); ++localElementDof) {
|
|
const int localDof = mfem::FiniteElementSpace::DecodeDof(elementDofs[localElementDof]);
|
|
const int trueDof = scalarFiniteElementSpace.GetLocalTDofNumber(localDof);
|
|
if (trueDof < 0) {
|
|
continue;
|
|
}
|
|
|
|
logicalTransformation->Transform(nodes.IntPoint(localElementDof), logicalPosition);
|
|
if (processed[trueDof] != 0) {
|
|
mfem::Vector existingPosition(
|
|
logicalPositions.GetData() + spatialDimension * trueDof, spatialDimension
|
|
);
|
|
const double scale = std::max(1.0, logicalInfinityRadius(logicalPosition));
|
|
if (euclideanDistance(existingPosition, logicalPosition) > 1.0e-12 * scale) {
|
|
throw std::invalid_argument(
|
|
"A shared scalar DOF has inconsistent logical reference coordinates."
|
|
);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
for (int component = 0; component < spatialDimension; ++component) {
|
|
logicalPositions(spatialDimension * trueDof + component) = logicalPosition(component);
|
|
}
|
|
processed[trueDof] = 1;
|
|
}
|
|
}
|
|
|
|
for (int trueDof = 0; trueDof < trueDofCount; ++trueDof) {
|
|
if (processed[trueDof] == 0) {
|
|
throw std::invalid_argument("An owned scalar DOF has no logical reference coordinate.");
|
|
}
|
|
}
|
|
|
|
return logicalPositions;
|
|
}
|
|
|
|
[[nodiscard]] double boundaryLogicalRadius(
|
|
mfem::ParMesh &logicalReferenceMesh,
|
|
const int boundaryAttribute
|
|
) {
|
|
double minimumRadius = std::numeric_limits<double>::infinity();
|
|
double maximumRadius = 0.0;
|
|
int sampleCount = 0;
|
|
mfem::Vector position(logicalReferenceMesh.SpaceDimension());
|
|
|
|
for (int boundaryElement = 0; boundaryElement < logicalReferenceMesh.GetNBE(); ++boundaryElement) {
|
|
if (logicalReferenceMesh.GetBdrAttribute(boundaryElement) != boundaryAttribute) {
|
|
continue;
|
|
}
|
|
|
|
mfem::ElementTransformation *transformation =
|
|
logicalReferenceMesh.GetBdrElementTransformation(boundaryElement);
|
|
const int geometry = logicalReferenceMesh.GetBdrElementGeometry(boundaryElement);
|
|
const mfem::IntegrationRule *vertices = mfem::Geometries.GetVertices(geometry);
|
|
if (transformation == nullptr || vertices == nullptr) {
|
|
throw std::invalid_argument("A logical radial boundary element is incomplete.");
|
|
}
|
|
|
|
for (int vertex = 0; vertex < vertices->GetNPoints(); ++vertex) {
|
|
transformation->Transform(vertices->IntPoint(vertex), position);
|
|
const double radius = logicalInfinityRadius(position);
|
|
minimumRadius = std::min(minimumRadius, radius);
|
|
maximumRadius = std::max(maximumRadius, radius);
|
|
++sampleCount;
|
|
}
|
|
}
|
|
|
|
if (sampleCount == 0 || !(minimumRadius > 0.0)) {
|
|
throw std::invalid_argument("A required logical radial boundary is absent.");
|
|
}
|
|
if (maximumRadius - minimumRadius > 1.0e-12 * std::max(1.0, maximumRadius)) {
|
|
throw std::invalid_argument("A logical radial boundary is not a constant L-infinity-radius surface.");
|
|
}
|
|
return 0.5 * (minimumRadius + maximumRadius);
|
|
}
|
|
|
|
[[nodiscard]] LocatedBoundaryPoint locateLogicalBoundaryPoint(
|
|
mfem::ParMesh &logicalReferenceMesh,
|
|
const int boundaryAttribute,
|
|
const mfem::Vector &target
|
|
) {
|
|
LocatedBoundaryPoint best;
|
|
const double residualTolerance = 2.0e-11 * std::max(1.0, logicalInfinityRadius(target));
|
|
|
|
for (int boundaryElement = 0; boundaryElement < logicalReferenceMesh.GetNBE(); ++boundaryElement) {
|
|
if (logicalReferenceMesh.GetBdrAttribute(boundaryElement) != boundaryAttribute) {
|
|
continue;
|
|
}
|
|
|
|
const int geometry = logicalReferenceMesh.GetBdrElementGeometry(boundaryElement);
|
|
if (geometry != mfem::Geometry::SQUARE) {
|
|
throw std::invalid_argument(
|
|
"The STROID logical radial foliation currently requires quadrilateral boundary elements."
|
|
);
|
|
}
|
|
|
|
mfem::ElementTransformation *transformation =
|
|
logicalReferenceMesh.GetBdrElementTransformation(boundaryElement);
|
|
if (transformation == nullptr) {
|
|
throw std::invalid_argument("A logical boundary element has no transformation.");
|
|
}
|
|
|
|
mfem::IntegrationPoint point = mfem::Geometries.GetCenter(geometry);
|
|
mfem::Vector transformed(target.Size());
|
|
mfem::Vector residual(target.Size());
|
|
bool nonsingular = true;
|
|
|
|
for (int iteration = 0; iteration < 8; ++iteration) {
|
|
transformation->Transform(point, transformed);
|
|
residual = target;
|
|
residual -= transformed;
|
|
if (residual.Norml2() <= residualTolerance) {
|
|
break;
|
|
}
|
|
|
|
transformation->SetIntPoint(&point);
|
|
const mfem::DenseMatrix &jacobian = transformation->Jacobian();
|
|
double normal00 = 0.0;
|
|
double normal01 = 0.0;
|
|
double normal11 = 0.0;
|
|
double right0 = 0.0;
|
|
double right1 = 0.0;
|
|
|
|
for (int component = 0; component < target.Size(); ++component) {
|
|
normal00 += jacobian(component, 0) * jacobian(component, 0);
|
|
normal01 += jacobian(component, 0) * jacobian(component, 1);
|
|
normal11 += jacobian(component, 1) * jacobian(component, 1);
|
|
right0 += jacobian(component, 0) * residual(component);
|
|
right1 += jacobian(component, 1) * residual(component);
|
|
}
|
|
|
|
const double determinant = normal00 * normal11 - normal01 * normal01;
|
|
if (!(std::abs(determinant) > std::numeric_limits<double>::epsilon())) {
|
|
nonsingular = false;
|
|
break;
|
|
}
|
|
|
|
point.x += (normal11 * right0 - normal01 * right1) / determinant;
|
|
point.y += (normal00 * right1 - normal01 * right0) / determinant;
|
|
}
|
|
|
|
if (!nonsingular || !mfem::Geometry::CheckPoint(geometry, point, 1.0e-10)) {
|
|
continue;
|
|
}
|
|
|
|
transformation->Transform(point, transformed);
|
|
const double residualNorm = euclideanDistance(transformed, target);
|
|
if (residualNorm <= residualTolerance && residualNorm < best.residualNorm) {
|
|
best.boundaryElement = boundaryElement;
|
|
best.integrationPoint = point;
|
|
best.residualNorm = residualNorm;
|
|
}
|
|
}
|
|
|
|
if (best.boundaryElement < 0) {
|
|
throw std::invalid_argument(
|
|
std::format(
|
|
"No logical boundary element contains a projected foliation point on boundary attribute {}.",
|
|
boundaryAttribute
|
|
)
|
|
);
|
|
}
|
|
return best;
|
|
}
|
|
|
|
void appendSurfaceInterpolationRow(
|
|
mfem::ParFiniteElementSpace &scalarFiniteElementSpace,
|
|
const field::ScalarBoundaryDofMap &stellarSurfaceDofMap,
|
|
const LocatedBoundaryPoint &locatedPoint,
|
|
std::vector<int> &globalCoordinates,
|
|
std::vector<double> &weights
|
|
) {
|
|
mfem::Array<int> boundaryDofs;
|
|
scalarFiniteElementSpace.GetBdrElementDofs(locatedPoint.boundaryElement, boundaryDofs);
|
|
const mfem::FiniteElement *boundaryElement = scalarFiniteElementSpace.GetBE(locatedPoint.boundaryElement);
|
|
if (boundaryElement == nullptr || boundaryElement->GetDof() != boundaryDofs.Size()) {
|
|
throw std::invalid_argument("The physical surface trace basis is incompatible with its boundary DOFs.");
|
|
}
|
|
|
|
mfem::Vector shape(boundaryDofs.Size());
|
|
boundaryElement->CalcShape(locatedPoint.integrationPoint, shape);
|
|
double partitionOfUnity = 0.0;
|
|
|
|
for (int localShapeDof = 0; localShapeDof < boundaryDofs.Size(); ++localShapeDof) {
|
|
partitionOfUnity += shape(localShapeDof);
|
|
if (std::abs(shape(localShapeDof)) <= 64.0 * std::numeric_limits<double>::epsilon()) {
|
|
continue;
|
|
}
|
|
|
|
const int localDof = mfem::FiniteElementSpace::DecodeDof(boundaryDofs[localShapeDof]);
|
|
const int trueDof = scalarFiniteElementSpace.GetLocalTDofNumber(localDof);
|
|
if (trueDof < 0) {
|
|
throw std::invalid_argument(
|
|
"Logical surface interpolation across MPI ownership is not implemented yet."
|
|
);
|
|
}
|
|
const std::optional<int> surfaceDof = stellarSurfaceDofMap.local_boundary_dof(trueDof);
|
|
if (!surfaceDof.has_value()) {
|
|
throw std::invalid_argument(
|
|
"A logical surface interpolation basis DOF is absent from the compact surface map."
|
|
);
|
|
}
|
|
|
|
const long long globalCoordinate = stellarSurfaceDofMap.global_boundary_dof(*surfaceDof);
|
|
if (globalCoordinate > std::numeric_limits<int>::max()) {
|
|
throw std::overflow_error("A global surface coordinate exceeds supported integer indexing.");
|
|
}
|
|
globalCoordinates.push_back(static_cast<int>(globalCoordinate));
|
|
weights.push_back(shape(localShapeDof));
|
|
}
|
|
|
|
if (std::abs(partitionOfUnity - 1.0) > 2.0e-12) {
|
|
throw std::invalid_argument("A logical surface interpolation row does not preserve constants.");
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] std::vector<int> gatherCounts(
|
|
const int localCount,
|
|
MPI_Comm communicator
|
|
) {
|
|
int communicatorSize = 0;
|
|
MPI_Comm_size(communicator, &communicatorSize);
|
|
std::vector<int> counts(static_cast<std::size_t>(communicatorSize));
|
|
MPI_Allgather(&localCount, 1, MPI_INT, counts.data(), 1, MPI_INT, communicator);
|
|
return counts;
|
|
}
|
|
|
|
[[nodiscard]] std::vector<int> prefixOffsets(const std::vector<int> &counts) {
|
|
std::vector<int> offsets(counts.size());
|
|
int offset = 0;
|
|
for (std::size_t rank = 0; rank < counts.size(); ++rank) {
|
|
offsets[rank] = offset;
|
|
offset += counts[rank];
|
|
}
|
|
return offsets;
|
|
}
|
|
|
|
void requireVectorSize(
|
|
const mfem::Vector &vector,
|
|
const int requiredSize,
|
|
const char *name
|
|
) {
|
|
if (vector.Size() != requiredSize) {
|
|
throw std::invalid_argument(
|
|
std::format(
|
|
"{} has size {}, but the prepared logical radial extension requires {}.", name, vector.Size(),
|
|
requiredSize
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
void gatherSurfaceDisplacement(
|
|
const mfem::Vector &localSurfaceDisplacement,
|
|
mfem::Vector &globalSurfaceDisplacement,
|
|
const std::vector<int> &counts,
|
|
const std::vector<int> &offsets,
|
|
MPI_Comm communicator
|
|
) {
|
|
MPI_Allgatherv(
|
|
localSurfaceDisplacement.GetData(), localSurfaceDisplacement.Size(), MPI_DOUBLE,
|
|
globalSurfaceDisplacement.GetData(), counts.data(), offsets.data(), MPI_DOUBLE, communicator
|
|
);
|
|
}
|
|
|
|
void reduceSurfaceDual(
|
|
const mfem::Vector &localGlobalSurfaceDual,
|
|
mfem::Vector &globalSurfaceDual,
|
|
mfem::Vector &surfaceDisplacementDual,
|
|
const int globalSurfaceDisplacementOffset,
|
|
MPI_Comm communicator
|
|
) {
|
|
MPI_Allreduce(
|
|
localGlobalSurfaceDual.GetData(), globalSurfaceDual.GetData(), localGlobalSurfaceDual.Size(),
|
|
MPI_DOUBLE, MPI_SUM, communicator
|
|
);
|
|
for (int localDof = 0; localDof < surfaceDisplacementDual.Size(); ++localDof) {
|
|
surfaceDisplacementDual(localDof) = globalSurfaceDual(globalSurfaceDisplacementOffset + localDof);
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] int interpolationEntryIndex(
|
|
const std::vector<int> &rowOffsets,
|
|
const int scalarTrueDof,
|
|
const int interpolationEntry
|
|
) {
|
|
if (scalarTrueDof < 0 || scalarTrueDof + 1 >= static_cast<int>(rowOffsets.size())) {
|
|
throw std::out_of_range("Scalar true DOF is outside the logical radial extension.");
|
|
}
|
|
const int entryCount = rowOffsets[scalarTrueDof + 1] - rowOffsets[scalarTrueDof];
|
|
if (interpolationEntry < 0 || interpolationEntry >= entryCount) {
|
|
throw std::out_of_range("Surface interpolation entry is outside the logical radial extension row.");
|
|
}
|
|
return rowOffsets[scalarTrueDof] + interpolationEntry;
|
|
}
|
|
|
|
[[nodiscard]] int mfemByNodesVectorDof(
|
|
const int scalarTrueDof,
|
|
const int component,
|
|
const int scalarTrueDofCount
|
|
) noexcept {
|
|
return scalarTrueDof + component * scalarTrueDofCount;
|
|
}
|
|
|
|
void applySparseForward(
|
|
const int spatialDimension,
|
|
const mfem::Array<int> &support,
|
|
const mfem::Vector &radialWeights,
|
|
const std::vector<int> &rowOffsets,
|
|
const std::vector<int> &surfaceCoordinates,
|
|
const std::vector<double> &surfaceWeights,
|
|
const mfem::Vector &globalSurfaceDisplacement,
|
|
mfem::Vector &volumeDisplacement
|
|
) {
|
|
volumeDisplacement = 0.0;
|
|
for (int scalarTrueDof = 0; scalarTrueDof < support.Size(); ++scalarTrueDof) {
|
|
if (support[scalarTrueDof] == 0 || radialWeights(scalarTrueDof) == 0.0) {
|
|
continue;
|
|
}
|
|
for (int component = 0; component < spatialDimension; ++component) {
|
|
double interpolatedSurfaceDisplacement = 0.0;
|
|
for (int entry = rowOffsets[scalarTrueDof]; entry < rowOffsets[scalarTrueDof + 1]; ++entry) {
|
|
interpolatedSurfaceDisplacement +=
|
|
surfaceWeights[entry] *
|
|
globalSurfaceDisplacement(spatialDimension * surfaceCoordinates[entry] + component);
|
|
}
|
|
volumeDisplacement(mfemByNodesVectorDof(scalarTrueDof, component, support.Size())) =
|
|
radialWeights(scalarTrueDof) * interpolatedSurfaceDisplacement;
|
|
}
|
|
}
|
|
}
|
|
|
|
void applySparseTranspose(
|
|
const int spatialDimension,
|
|
const mfem::Array<int> &support,
|
|
const mfem::Vector &radialWeights,
|
|
const std::vector<int> &rowOffsets,
|
|
const std::vector<int> &surfaceCoordinates,
|
|
const std::vector<double> &surfaceWeights,
|
|
const mfem::Vector &volumeDual,
|
|
mfem::Vector &globalSurfaceDual
|
|
) {
|
|
globalSurfaceDual = 0.0;
|
|
for (int scalarTrueDof = 0; scalarTrueDof < support.Size(); ++scalarTrueDof) {
|
|
if (support[scalarTrueDof] == 0 || radialWeights(scalarTrueDof) == 0.0) {
|
|
continue;
|
|
}
|
|
for (int entry = rowOffsets[scalarTrueDof]; entry < rowOffsets[scalarTrueDof + 1]; ++entry) {
|
|
for (int component = 0; component < spatialDimension; ++component) {
|
|
globalSurfaceDual(spatialDimension * surfaceCoordinates[entry] + component) +=
|
|
radialWeights(scalarTrueDof) * surfaceWeights[entry] *
|
|
volumeDual(mfemByNodesVectorDof(scalarTrueDof, component, support.Size()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
RadialDeformationExtensionCompilationContext::RadialDeformationExtensionCompilationContext(
|
|
mfem::ParFiniteElementSpace &scalarFiniteElementSpace,
|
|
mfem::ParFiniteElementSpace &vectorFiniteElementSpace,
|
|
mfem::ParMesh &logicalReferenceMesh,
|
|
field::ScalarBoundaryDofMap stellarSurfaceDofMap,
|
|
field::ScalarBoundaryDofMap infinitySurfaceDofMap,
|
|
mfem::Array<int> stellarMaterialMarker,
|
|
mfem::Array<int> vacuumMaterialMarker,
|
|
const int stellarSurfaceBoundaryAttribute,
|
|
const int infinitySurfaceBoundaryAttribute
|
|
)
|
|
: m_communicator(scalarFiniteElementSpace.GetComm()) {
|
|
const mfem::Mesh *physicalMesh = scalarFiniteElementSpace.GetMesh();
|
|
if (physicalMesh == nullptr || vectorFiniteElementSpace.GetMesh() != physicalMesh) {
|
|
throw std::invalid_argument(
|
|
"Logical radial extension scalar and vector spaces must share one physical mesh."
|
|
);
|
|
}
|
|
int communicatorSize = 0;
|
|
MPI_Comm_size(m_communicator, &communicatorSize);
|
|
if (communicatorSize != 1) {
|
|
throw std::invalid_argument(
|
|
"Logical radial surface interpolation currently supports one MPI rank; shared interpolation-row "
|
|
"ownership remains deferred."
|
|
);
|
|
}
|
|
if (scalarFiniteElementSpace.Nonconforming() || vectorFiniteElementSpace.Nonconforming() ||
|
|
logicalReferenceMesh.Nonconforming()) {
|
|
throw std::invalid_argument("Logical radial extension compilation currently requires conforming meshes.");
|
|
}
|
|
|
|
m_spatialDimension = physicalMesh->SpaceDimension();
|
|
m_scalarTrueDofCount = scalarFiniteElementSpace.GetTrueVSize();
|
|
m_volumeDisplacementSize = vectorFiniteElementSpace.GetTrueVSize();
|
|
if (logicalReferenceMesh.SpaceDimension() != m_spatialDimension ||
|
|
logicalReferenceMesh.GetNE() != physicalMesh->GetNE() ||
|
|
logicalReferenceMesh.GetNBE() != physicalMesh->GetNBE()) {
|
|
throw std::invalid_argument("The STROID logical reference mesh does not match the physical mesh topology.");
|
|
}
|
|
for (int boundaryElement = 0; boundaryElement < physicalMesh->GetNBE(); ++boundaryElement) {
|
|
if (logicalReferenceMesh.GetBdrAttribute(boundaryElement) !=
|
|
physicalMesh->GetBdrAttribute(boundaryElement) ||
|
|
logicalReferenceMesh.GetBdrElementGeometry(boundaryElement) !=
|
|
physicalMesh->GetBdrElementGeometry(boundaryElement)) {
|
|
throw std::invalid_argument("The STROID logical and physical boundary elements do not correspond.");
|
|
}
|
|
}
|
|
if (scalarFiniteElementSpace.GetVDim() != 1 || vectorFiniteElementSpace.GetVDim() != m_spatialDimension ||
|
|
vectorFiniteElementSpace.GetOrdering() != mfem::Ordering::byNODES ||
|
|
m_volumeDisplacementSize != m_spatialDimension * m_scalarTrueDofCount ||
|
|
vectorFiniteElementSpace.FEColl() != scalarFiniteElementSpace.FEColl()) {
|
|
throw std::invalid_argument(
|
|
"Logical radial extension compilation requires an MFEM byNODES vector space made from its scalar "
|
|
"companion basis."
|
|
);
|
|
}
|
|
if (stellarSurfaceDofMap.volume_true_dof_size() != m_scalarTrueDofCount ||
|
|
infinitySurfaceDofMap.volume_true_dof_size() != m_scalarTrueDofCount) {
|
|
throw std::invalid_argument("Logical radial boundary maps do not match the scalar companion space.");
|
|
}
|
|
|
|
m_surfaceDisplacementSize = m_spatialDimension * stellarSurfaceDofMap.local_size();
|
|
const long long globalSurfaceSize = m_spatialDimension * stellarSurfaceDofMap.global_size();
|
|
const long long globalOffset = m_spatialDimension * stellarSurfaceDofMap.global_offset();
|
|
if (globalSurfaceSize > std::numeric_limits<int>::max() || globalOffset > std::numeric_limits<int>::max()) {
|
|
throw std::overflow_error("The logical radial extension surface vector exceeds MPI integer indexing.");
|
|
}
|
|
m_globalSurfaceDisplacementSize = static_cast<int>(globalSurfaceSize);
|
|
m_globalSurfaceDisplacementOffset = static_cast<int>(globalOffset);
|
|
m_surfaceDisplacementCounts = gatherCounts(m_surfaceDisplacementSize, m_communicator);
|
|
m_surfaceDisplacementOffsets = prefixOffsets(m_surfaceDisplacementCounts);
|
|
|
|
m_stellarSupport = buildTrueDofSupport(scalarFiniteElementSpace, stellarMaterialMarker);
|
|
m_vacuumSupport = buildTrueDofSupport(scalarFiniteElementSpace, vacuumMaterialMarker);
|
|
const mfem::Vector logicalPositions =
|
|
buildLogicalTrueDofPositions(scalarFiniteElementSpace, logicalReferenceMesh);
|
|
|
|
m_stellarSurfaceLogicalRadius = boundaryLogicalRadius(logicalReferenceMesh, stellarSurfaceBoundaryAttribute);
|
|
m_infinitySurfaceLogicalRadius = boundaryLogicalRadius(logicalReferenceMesh, infinitySurfaceBoundaryAttribute);
|
|
if (!(m_infinitySurfaceLogicalRadius > m_stellarSurfaceLogicalRadius)) {
|
|
throw std::invalid_argument("Logical reference infinity must lie outside the logical stellar surface.");
|
|
}
|
|
|
|
m_logicalRadius.SetSize(m_scalarTrueDofCount);
|
|
m_surfaceInterpolationRowOffsets.resize(static_cast<std::size_t>(m_scalarTrueDofCount + 1));
|
|
mfem::Vector logicalPosition(m_spatialDimension);
|
|
mfem::Vector stellarSurfaceTarget(m_spatialDimension);
|
|
mfem::Vector infinitySurfaceTarget(m_spatialDimension);
|
|
const double centerTolerance = 64.0 * std::numeric_limits<double>::epsilon() * m_infinitySurfaceLogicalRadius;
|
|
const double radialTolerance = 128.0 * std::numeric_limits<double>::epsilon() * m_infinitySurfaceLogicalRadius;
|
|
|
|
for (int scalarTrueDof = 0; scalarTrueDof < m_scalarTrueDofCount; ++scalarTrueDof) {
|
|
m_surfaceInterpolationRowOffsets[scalarTrueDof] = static_cast<int>(m_surfaceInterpolationWeights.size());
|
|
for (int component = 0; component < m_spatialDimension; ++component) {
|
|
logicalPosition(component) = logicalPositions(m_spatialDimension * scalarTrueDof + component);
|
|
}
|
|
|
|
const double radius = logicalInfinityRadius(logicalPosition);
|
|
m_logicalRadius(scalarTrueDof) = radius;
|
|
if (radius <= centerTolerance) {
|
|
continue;
|
|
}
|
|
if (m_stellarSupport[scalarTrueDof] != 0 && radius > m_stellarSurfaceLogicalRadius + radialTolerance) {
|
|
throw std::invalid_argument("A stellar DOF lies outside the logical stellar surface.");
|
|
}
|
|
if (m_vacuumSupport[scalarTrueDof] != 0 && (radius < m_stellarSurfaceLogicalRadius - radialTolerance ||
|
|
radius > m_infinitySurfaceLogicalRadius + radialTolerance)) {
|
|
throw std::invalid_argument("A vacuum DOF lies outside the logical exterior interval.");
|
|
}
|
|
|
|
for (int component = 0; component < m_spatialDimension; ++component) {
|
|
stellarSurfaceTarget(component) = logicalPosition(component) * m_stellarSurfaceLogicalRadius / radius;
|
|
}
|
|
const LocatedBoundaryPoint stellarSurfacePoint =
|
|
locateLogicalBoundaryPoint(logicalReferenceMesh, stellarSurfaceBoundaryAttribute, stellarSurfaceTarget);
|
|
appendSurfaceInterpolationRow(
|
|
scalarFiniteElementSpace, stellarSurfaceDofMap, stellarSurfacePoint,
|
|
m_surfaceInterpolationGlobalCoordinates, m_surfaceInterpolationWeights
|
|
);
|
|
|
|
if (m_vacuumSupport[scalarTrueDof] != 0) {
|
|
for (int component = 0; component < m_spatialDimension; ++component) {
|
|
infinitySurfaceTarget(component) =
|
|
logicalPosition(component) * m_infinitySurfaceLogicalRadius / radius;
|
|
}
|
|
static_cast<void>(locateLogicalBoundaryPoint(
|
|
logicalReferenceMesh, infinitySurfaceBoundaryAttribute, infinitySurfaceTarget
|
|
));
|
|
}
|
|
}
|
|
m_surfaceInterpolationRowOffsets[m_scalarTrueDofCount] = static_cast<int>(m_surfaceInterpolationWeights.size());
|
|
}
|
|
|
|
int RadialDeformationExtensionCompilationContext::spatialDimension() const noexcept {
|
|
return m_spatialDimension;
|
|
}
|
|
|
|
int RadialDeformationExtensionCompilationContext::surfaceDisplacementSize() const noexcept {
|
|
return m_surfaceDisplacementSize;
|
|
}
|
|
|
|
int RadialDeformationExtensionCompilationContext::volumeDisplacementSize() const noexcept {
|
|
return m_volumeDisplacementSize;
|
|
}
|
|
|
|
int RadialDeformationExtensionCompilationContext::scalarTrueDofCount() const noexcept {
|
|
return m_scalarTrueDofCount;
|
|
}
|
|
|
|
double RadialDeformationExtensionCompilationContext::logicalRadius(const int scalarTrueDof) const {
|
|
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
|
|
throw std::out_of_range("Scalar true DOF is outside the logical radial compilation context.");
|
|
}
|
|
return m_logicalRadius(scalarTrueDof);
|
|
}
|
|
|
|
double RadialDeformationExtensionCompilationContext::stellarSurfaceLogicalRadius() const noexcept {
|
|
return m_stellarSurfaceLogicalRadius;
|
|
}
|
|
|
|
double RadialDeformationExtensionCompilationContext::infinitySurfaceLogicalRadius() const noexcept {
|
|
return m_infinitySurfaceLogicalRadius;
|
|
}
|
|
|
|
int RadialDeformationExtensionCompilationContext::surfaceInterpolationEntryCount(const int scalarTrueDof) const {
|
|
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
|
|
throw std::out_of_range("Scalar true DOF is outside the logical radial compilation context.");
|
|
}
|
|
return m_surfaceInterpolationRowOffsets[scalarTrueDof + 1] - m_surfaceInterpolationRowOffsets[scalarTrueDof];
|
|
}
|
|
|
|
int RadialDeformationExtensionCompilationContext::surfaceGlobalCoordinate(
|
|
const int scalarTrueDof,
|
|
const int interpolationEntry
|
|
) const {
|
|
return m_surfaceInterpolationGlobalCoordinates[interpolationEntryIndex(
|
|
m_surfaceInterpolationRowOffsets, scalarTrueDof, interpolationEntry
|
|
)];
|
|
}
|
|
|
|
double RadialDeformationExtensionCompilationContext::surfaceInterpolationWeight(
|
|
const int scalarTrueDof,
|
|
const int interpolationEntry
|
|
) const {
|
|
return m_surfaceInterpolationWeights[interpolationEntryIndex(
|
|
m_surfaceInterpolationRowOffsets, scalarTrueDof, interpolationEntry
|
|
)];
|
|
}
|
|
|
|
PowerLawRadialInteriorExtension::PowerLawRadialInteriorExtension(const double radialPower)
|
|
: m_radialPower(radialPower) {
|
|
validate();
|
|
}
|
|
|
|
double PowerLawRadialInteriorExtension::radialPower() const noexcept {
|
|
return m_radialPower;
|
|
}
|
|
|
|
InteriorDeformationExtensionDescriptor PowerLawRadialInteriorExtension::descriptor() const noexcept {
|
|
return powerLawInteriorDescriptor(3);
|
|
}
|
|
|
|
void PowerLawRadialInteriorExtension::validate() const {
|
|
if (!std::isfinite(m_radialPower) || m_radialPower < 1.0) {
|
|
throw std::invalid_argument("PowerLawRadialInteriorExtension requires a finite radial power at least one.");
|
|
}
|
|
}
|
|
|
|
PreparedPowerLawRadialInteriorExtension::PreparedPowerLawRadialInteriorExtension(
|
|
const PowerLawRadialInteriorExtension &extension,
|
|
const RadialDeformationExtensionCompilationContext &context
|
|
)
|
|
: m_descriptor(powerLawInteriorDescriptor(context.m_spatialDimension)),
|
|
m_radialPower(extension.radialPower()),
|
|
m_surfaceDisplacementSize(context.m_surfaceDisplacementSize),
|
|
m_interiorDisplacementSize(context.m_volumeDisplacementSize),
|
|
m_spatialDimension(context.m_spatialDimension),
|
|
m_globalSurfaceDisplacementSize(context.m_globalSurfaceDisplacementSize),
|
|
m_globalSurfaceDisplacementOffset(context.m_globalSurfaceDisplacementOffset),
|
|
m_communicator(context.m_communicator),
|
|
m_stellarSupport(context.m_stellarSupport),
|
|
m_radialWeights(context.m_scalarTrueDofCount),
|
|
m_surfaceInterpolationRowOffsets(context.m_surfaceInterpolationRowOffsets),
|
|
m_surfaceInterpolationGlobalCoordinates(context.m_surfaceInterpolationGlobalCoordinates),
|
|
m_surfaceInterpolationWeights(context.m_surfaceInterpolationWeights),
|
|
m_surfaceDisplacementCounts(context.m_surfaceDisplacementCounts),
|
|
m_surfaceDisplacementOffsets(context.m_surfaceDisplacementOffsets),
|
|
m_globalSurfaceDisplacementWorkspace(context.m_globalSurfaceDisplacementSize),
|
|
m_localGlobalSurfaceDualWorkspace(context.m_globalSurfaceDisplacementSize),
|
|
m_globalSurfaceDualWorkspace(context.m_globalSurfaceDisplacementSize) {
|
|
for (int scalarTrueDof = 0; scalarTrueDof < scalarTrueDofCount(); ++scalarTrueDof) {
|
|
if (m_stellarSupport[scalarTrueDof] == 0 || context.m_logicalRadius(scalarTrueDof) == 0.0) {
|
|
m_radialWeights(scalarTrueDof) = 0.0;
|
|
continue;
|
|
}
|
|
m_radialWeights(scalarTrueDof) =
|
|
std::pow(context.m_logicalRadius(scalarTrueDof) / context.m_stellarSurfaceLogicalRadius, m_radialPower);
|
|
}
|
|
}
|
|
|
|
InteriorDeformationExtensionDescriptor PreparedPowerLawRadialInteriorExtension::descriptor() const noexcept {
|
|
return m_descriptor;
|
|
}
|
|
int PreparedPowerLawRadialInteriorExtension::surfaceDisplacementSize() const noexcept {
|
|
return m_surfaceDisplacementSize;
|
|
}
|
|
int PreparedPowerLawRadialInteriorExtension::interiorDisplacementSize() const noexcept {
|
|
return m_interiorDisplacementSize;
|
|
}
|
|
int PreparedPowerLawRadialInteriorExtension::scalarTrueDofCount() const noexcept {
|
|
return m_stellarSupport.Size();
|
|
}
|
|
double PreparedPowerLawRadialInteriorExtension::radialPower() const noexcept {
|
|
return m_radialPower;
|
|
}
|
|
bool PreparedPowerLawRadialInteriorExtension::hasStellarSupport(const int scalarTrueDof) const {
|
|
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
|
|
throw std::out_of_range("Scalar true DOF is outside the prepared interior extension.");
|
|
}
|
|
return m_stellarSupport[scalarTrueDof] != 0;
|
|
}
|
|
double PreparedPowerLawRadialInteriorExtension::radialWeight(const int scalarTrueDof) const {
|
|
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
|
|
throw std::out_of_range("Scalar true DOF is outside the prepared interior extension.");
|
|
}
|
|
return m_radialWeights(scalarTrueDof);
|
|
}
|
|
int PreparedPowerLawRadialInteriorExtension::surfaceInterpolationEntryCount(const int scalarTrueDof) const {
|
|
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
|
|
throw std::out_of_range("Scalar true DOF is outside the prepared interior extension.");
|
|
}
|
|
return m_surfaceInterpolationRowOffsets[scalarTrueDof + 1] - m_surfaceInterpolationRowOffsets[scalarTrueDof];
|
|
}
|
|
int PreparedPowerLawRadialInteriorExtension::surfaceGlobalCoordinate(
|
|
const int scalarTrueDof,
|
|
const int interpolationEntry
|
|
) const {
|
|
return m_surfaceInterpolationGlobalCoordinates[interpolationEntryIndex(
|
|
m_surfaceInterpolationRowOffsets, scalarTrueDof, interpolationEntry
|
|
)];
|
|
}
|
|
double PreparedPowerLawRadialInteriorExtension::surfaceInterpolationWeight(
|
|
const int scalarTrueDof,
|
|
const int interpolationEntry
|
|
) const {
|
|
return m_surfaceInterpolationWeights[interpolationEntryIndex(
|
|
m_surfaceInterpolationRowOffsets, scalarTrueDof, interpolationEntry
|
|
)];
|
|
}
|
|
|
|
void PreparedPowerLawRadialInteriorExtension::requireSurfaceSize(const mfem::Vector &vector) const {
|
|
requireVectorSize(vector, surfaceDisplacementSize(), "Surface displacement");
|
|
}
|
|
void PreparedPowerLawRadialInteriorExtension::requireInteriorSize(const mfem::Vector &vector) const {
|
|
requireVectorSize(vector, interiorDisplacementSize(), "Interior displacement");
|
|
}
|
|
void PreparedPowerLawRadialInteriorExtension::applyForward(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
mfem::Vector &interiorDisplacement
|
|
) const {
|
|
gatherSurfaceDisplacement(
|
|
surfaceDisplacement, m_globalSurfaceDisplacementWorkspace, m_surfaceDisplacementCounts,
|
|
m_surfaceDisplacementOffsets, m_communicator
|
|
);
|
|
applySparseForward(
|
|
m_spatialDimension, m_stellarSupport, m_radialWeights, m_surfaceInterpolationRowOffsets,
|
|
m_surfaceInterpolationGlobalCoordinates, m_surfaceInterpolationWeights,
|
|
m_globalSurfaceDisplacementWorkspace, interiorDisplacement
|
|
);
|
|
}
|
|
void PreparedPowerLawRadialInteriorExtension::applyTranspose(
|
|
const mfem::Vector &interiorDisplacementDual,
|
|
mfem::Vector &surfaceDisplacementDual
|
|
) const {
|
|
applySparseTranspose(
|
|
m_spatialDimension, m_stellarSupport, m_radialWeights, m_surfaceInterpolationRowOffsets,
|
|
m_surfaceInterpolationGlobalCoordinates, m_surfaceInterpolationWeights, interiorDisplacementDual,
|
|
m_localGlobalSurfaceDualWorkspace
|
|
);
|
|
reduceSurfaceDual(
|
|
m_localGlobalSurfaceDualWorkspace, m_globalSurfaceDualWorkspace, surfaceDisplacementDual,
|
|
m_globalSurfaceDisplacementOffset, m_communicator
|
|
);
|
|
}
|
|
void PreparedPowerLawRadialInteriorExtension::buildInteriorDisplacement(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
mfem::Vector &interiorDisplacement
|
|
) const {
|
|
requireSurfaceSize(surfaceDisplacement);
|
|
requireInteriorSize(interiorDisplacement);
|
|
applyForward(surfaceDisplacement, interiorDisplacement);
|
|
}
|
|
void PreparedPowerLawRadialInteriorExtension::applyJacobian(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
const mfem::Vector &surfaceDisplacementDirection,
|
|
mfem::Vector &interiorDisplacementDirection
|
|
) const {
|
|
requireSurfaceSize(surfaceDisplacement);
|
|
requireSurfaceSize(surfaceDisplacementDirection);
|
|
requireInteriorSize(interiorDisplacementDirection);
|
|
applyForward(surfaceDisplacementDirection, interiorDisplacementDirection);
|
|
}
|
|
void PreparedPowerLawRadialInteriorExtension::applyJacobianTranspose(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
const mfem::Vector &interiorDisplacementDual,
|
|
mfem::Vector &surfaceDisplacementDual
|
|
) const {
|
|
requireSurfaceSize(surfaceDisplacement);
|
|
requireInteriorSize(interiorDisplacementDual);
|
|
requireSurfaceSize(surfaceDisplacementDual);
|
|
applyTranspose(interiorDisplacementDual, surfaceDisplacementDual);
|
|
}
|
|
void PreparedPowerLawRadialInteriorExtension::applyPullbackDerivative(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
const mfem::Vector &surfaceDisplacementDirection,
|
|
const mfem::Vector &interiorDisplacementDual,
|
|
mfem::Vector &surfaceDisplacementDualAction
|
|
) const {
|
|
requireSurfaceSize(surfaceDisplacement);
|
|
requireSurfaceSize(surfaceDisplacementDirection);
|
|
requireInteriorSize(interiorDisplacementDual);
|
|
requireSurfaceSize(surfaceDisplacementDualAction);
|
|
surfaceDisplacementDualAction = 0.0;
|
|
}
|
|
PreparedPowerLawRadialInteriorExtension compileInteriorDeformationExtension(
|
|
const PowerLawRadialInteriorExtension &extension,
|
|
const RadialDeformationExtensionCompilationContext &context
|
|
) {
|
|
extension.validate();
|
|
return PreparedPowerLawRadialInteriorExtension(extension, context);
|
|
}
|
|
|
|
VacuumDeformationExtensionDescriptor FixedInfinityRadialVacuumExtension::descriptor() const noexcept {
|
|
return fixedInfinityVacuumDescriptor(3);
|
|
}
|
|
void FixedInfinityRadialVacuumExtension::validate() const {
|
|
}
|
|
|
|
PreparedFixedInfinityRadialVacuumExtension::PreparedFixedInfinityRadialVacuumExtension(
|
|
const FixedInfinityRadialVacuumExtension &extension,
|
|
const RadialDeformationExtensionCompilationContext &context
|
|
)
|
|
: m_descriptor(fixedInfinityVacuumDescriptor(context.m_spatialDimension)),
|
|
m_surfaceDisplacementSize(context.m_surfaceDisplacementSize),
|
|
m_vacuumDisplacementSize(context.m_volumeDisplacementSize),
|
|
m_spatialDimension(context.m_spatialDimension),
|
|
m_globalSurfaceDisplacementSize(context.m_globalSurfaceDisplacementSize),
|
|
m_globalSurfaceDisplacementOffset(context.m_globalSurfaceDisplacementOffset),
|
|
m_communicator(context.m_communicator),
|
|
m_vacuumSupport(context.m_vacuumSupport),
|
|
m_radialWeights(context.m_scalarTrueDofCount),
|
|
m_surfaceInterpolationRowOffsets(context.m_surfaceInterpolationRowOffsets),
|
|
m_surfaceInterpolationGlobalCoordinates(context.m_surfaceInterpolationGlobalCoordinates),
|
|
m_surfaceInterpolationWeights(context.m_surfaceInterpolationWeights),
|
|
m_surfaceDisplacementCounts(context.m_surfaceDisplacementCounts),
|
|
m_surfaceDisplacementOffsets(context.m_surfaceDisplacementOffsets),
|
|
m_globalSurfaceDisplacementWorkspace(context.m_globalSurfaceDisplacementSize),
|
|
m_localGlobalSurfaceDualWorkspace(context.m_globalSurfaceDisplacementSize),
|
|
m_globalSurfaceDualWorkspace(context.m_globalSurfaceDisplacementSize) {
|
|
static_cast<void>(extension);
|
|
for (int scalarTrueDof = 0; scalarTrueDof < scalarTrueDofCount(); ++scalarTrueDof) {
|
|
if (m_vacuumSupport[scalarTrueDof] == 0) {
|
|
m_radialWeights(scalarTrueDof) = 0.0;
|
|
continue;
|
|
}
|
|
m_radialWeights(scalarTrueDof) =
|
|
(context.m_infinitySurfaceLogicalRadius - context.m_logicalRadius(scalarTrueDof)) /
|
|
(context.m_infinitySurfaceLogicalRadius - context.m_stellarSurfaceLogicalRadius);
|
|
}
|
|
}
|
|
|
|
VacuumDeformationExtensionDescriptor PreparedFixedInfinityRadialVacuumExtension::descriptor() const noexcept {
|
|
return m_descriptor;
|
|
}
|
|
int PreparedFixedInfinityRadialVacuumExtension::surfaceDisplacementSize() const noexcept {
|
|
return m_surfaceDisplacementSize;
|
|
}
|
|
int PreparedFixedInfinityRadialVacuumExtension::vacuumDisplacementSize() const noexcept {
|
|
return m_vacuumDisplacementSize;
|
|
}
|
|
int PreparedFixedInfinityRadialVacuumExtension::scalarTrueDofCount() const noexcept {
|
|
return m_vacuumSupport.Size();
|
|
}
|
|
bool PreparedFixedInfinityRadialVacuumExtension::hasVacuumSupport(const int scalarTrueDof) const {
|
|
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
|
|
throw std::out_of_range("Scalar true DOF is outside the prepared vacuum extension.");
|
|
}
|
|
return m_vacuumSupport[scalarTrueDof] != 0;
|
|
}
|
|
double PreparedFixedInfinityRadialVacuumExtension::radialWeight(const int scalarTrueDof) const {
|
|
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
|
|
throw std::out_of_range("Scalar true DOF is outside the prepared vacuum extension.");
|
|
}
|
|
return m_radialWeights(scalarTrueDof);
|
|
}
|
|
int PreparedFixedInfinityRadialVacuumExtension::surfaceInterpolationEntryCount(const int scalarTrueDof) const {
|
|
if (scalarTrueDof < 0 || scalarTrueDof >= scalarTrueDofCount()) {
|
|
throw std::out_of_range("Scalar true DOF is outside the prepared vacuum extension.");
|
|
}
|
|
return m_surfaceInterpolationRowOffsets[scalarTrueDof + 1] - m_surfaceInterpolationRowOffsets[scalarTrueDof];
|
|
}
|
|
int PreparedFixedInfinityRadialVacuumExtension::surfaceGlobalCoordinate(
|
|
const int scalarTrueDof,
|
|
const int interpolationEntry
|
|
) const {
|
|
return m_surfaceInterpolationGlobalCoordinates[interpolationEntryIndex(
|
|
m_surfaceInterpolationRowOffsets, scalarTrueDof, interpolationEntry
|
|
)];
|
|
}
|
|
double PreparedFixedInfinityRadialVacuumExtension::surfaceInterpolationWeight(
|
|
const int scalarTrueDof,
|
|
const int interpolationEntry
|
|
) const {
|
|
return m_surfaceInterpolationWeights[interpolationEntryIndex(
|
|
m_surfaceInterpolationRowOffsets, scalarTrueDof, interpolationEntry
|
|
)];
|
|
}
|
|
|
|
void PreparedFixedInfinityRadialVacuumExtension::requireSurfaceSize(const mfem::Vector &vector) const {
|
|
requireVectorSize(vector, surfaceDisplacementSize(), "Surface displacement");
|
|
}
|
|
void PreparedFixedInfinityRadialVacuumExtension::requireVacuumSize(const mfem::Vector &vector) const {
|
|
requireVectorSize(vector, vacuumDisplacementSize(), "Vacuum displacement");
|
|
}
|
|
void PreparedFixedInfinityRadialVacuumExtension::applyForward(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
mfem::Vector &vacuumDisplacement
|
|
) const {
|
|
gatherSurfaceDisplacement(
|
|
surfaceDisplacement, m_globalSurfaceDisplacementWorkspace, m_surfaceDisplacementCounts,
|
|
m_surfaceDisplacementOffsets, m_communicator
|
|
);
|
|
applySparseForward(
|
|
m_spatialDimension, m_vacuumSupport, m_radialWeights, m_surfaceInterpolationRowOffsets,
|
|
m_surfaceInterpolationGlobalCoordinates, m_surfaceInterpolationWeights,
|
|
m_globalSurfaceDisplacementWorkspace, vacuumDisplacement
|
|
);
|
|
}
|
|
void PreparedFixedInfinityRadialVacuumExtension::applyTranspose(
|
|
const mfem::Vector &vacuumDisplacementDual,
|
|
mfem::Vector &surfaceDisplacementDual
|
|
) const {
|
|
applySparseTranspose(
|
|
m_spatialDimension, m_vacuumSupport, m_radialWeights, m_surfaceInterpolationRowOffsets,
|
|
m_surfaceInterpolationGlobalCoordinates, m_surfaceInterpolationWeights, vacuumDisplacementDual,
|
|
m_localGlobalSurfaceDualWorkspace
|
|
);
|
|
reduceSurfaceDual(
|
|
m_localGlobalSurfaceDualWorkspace, m_globalSurfaceDualWorkspace, surfaceDisplacementDual,
|
|
m_globalSurfaceDisplacementOffset, m_communicator
|
|
);
|
|
}
|
|
void PreparedFixedInfinityRadialVacuumExtension::buildVacuumDisplacement(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
mfem::Vector &vacuumDisplacement
|
|
) const {
|
|
requireSurfaceSize(surfaceDisplacement);
|
|
requireVacuumSize(vacuumDisplacement);
|
|
applyForward(surfaceDisplacement, vacuumDisplacement);
|
|
}
|
|
void PreparedFixedInfinityRadialVacuumExtension::applyJacobian(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
const mfem::Vector &surfaceDisplacementDirection,
|
|
mfem::Vector &vacuumDisplacementDirection
|
|
) const {
|
|
requireSurfaceSize(surfaceDisplacement);
|
|
requireSurfaceSize(surfaceDisplacementDirection);
|
|
requireVacuumSize(vacuumDisplacementDirection);
|
|
applyForward(surfaceDisplacementDirection, vacuumDisplacementDirection);
|
|
}
|
|
void PreparedFixedInfinityRadialVacuumExtension::applyJacobianTranspose(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
const mfem::Vector &vacuumDisplacementDual,
|
|
mfem::Vector &surfaceDisplacementDual
|
|
) const {
|
|
requireSurfaceSize(surfaceDisplacement);
|
|
requireVacuumSize(vacuumDisplacementDual);
|
|
requireSurfaceSize(surfaceDisplacementDual);
|
|
applyTranspose(vacuumDisplacementDual, surfaceDisplacementDual);
|
|
}
|
|
void PreparedFixedInfinityRadialVacuumExtension::applyPullbackDerivative(
|
|
const mfem::Vector &surfaceDisplacement,
|
|
const mfem::Vector &surfaceDisplacementDirection,
|
|
const mfem::Vector &vacuumDisplacementDual,
|
|
mfem::Vector &surfaceDisplacementDualAction
|
|
) const {
|
|
requireSurfaceSize(surfaceDisplacement);
|
|
requireSurfaceSize(surfaceDisplacementDirection);
|
|
requireVacuumSize(vacuumDisplacementDual);
|
|
requireSurfaceSize(surfaceDisplacementDualAction);
|
|
surfaceDisplacementDualAction = 0.0;
|
|
}
|
|
PreparedFixedInfinityRadialVacuumExtension compileVacuumDeformationExtension(
|
|
const FixedInfinityRadialVacuumExtension &extension,
|
|
const RadialDeformationExtensionCompilationContext &context
|
|
) {
|
|
extension.validate();
|
|
return PreparedFixedInfinityRadialVacuumExtension(extension, context);
|
|
}
|
|
} // namespace mean_field::deformation
|