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
|