990 lines
37 KiB
C++
990 lines
37 KiB
C++
module;
|
|
|
|
#include <cmath>
|
|
#include <memory>
|
|
#include <mfem.hpp>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
|
|
module mean_field;
|
|
import :mapping.types;
|
|
import :mapping.compactification;
|
|
import :utils.user;
|
|
|
|
namespace {
|
|
bool vector_is_finite(const mfem::Vector &vector) {
|
|
for (int i = 0; i < vector.Size(); ++i) {
|
|
if (!std::isfinite(vector(i)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool matrix_is_finite(const mfem::DenseMatrix &matrix) {
|
|
for (int i = 0; i < matrix.Height(); ++i) {
|
|
for (int j = 0; j < matrix.Width(); ++j) {
|
|
if (!std::isfinite(matrix(i, j)))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
namespace mean_field::mapping {
|
|
ElementCompactificationData::ElementCompactificationData(
|
|
const mfem::FiniteElement &element, const mfem::Vector &dofs)
|
|
: m_element(&element), m_dofs(dofs) {
|
|
if (element.GetRangeType() != mfem::FiniteElement::SCALAR) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate requires a scalar finite element.");
|
|
}
|
|
|
|
if (element.GetMapType() != mfem::FiniteElement::VALUE) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate requires a value-mapped scalar "
|
|
"finite "
|
|
"element.");
|
|
}
|
|
|
|
if (element.GetDerivType() != mfem::FiniteElement::GRAD) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate finite element must provide a "
|
|
"gradient.");
|
|
}
|
|
|
|
if (element.GetDof() <= 0) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate finite element has no degrees of "
|
|
"freedom.");
|
|
}
|
|
|
|
if (dofs.Size() != element.GetDof()) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate DOF count does not match its "
|
|
"finite "
|
|
"element.");
|
|
}
|
|
}
|
|
|
|
const mfem::FiniteElement &
|
|
ElementCompactificationData::GetElement() const noexcept {
|
|
return *m_element;
|
|
}
|
|
|
|
const mfem::Vector &ElementCompactificationData::GetDofs() const noexcept {
|
|
return m_dofs;
|
|
}
|
|
|
|
int ElementCompactificationData::GetDofCount() const noexcept {
|
|
return m_dofs.Size();
|
|
}
|
|
|
|
ElementDisplacementData::ElementDisplacementData(
|
|
const mfem::FiniteElement &element, const mfem::Vector &displacement_dofs,
|
|
const mfem::Ordering::Type ordering)
|
|
: m_element(&element), m_dimension(0), m_ordering(ordering) {
|
|
const int dof_count = element.GetDof();
|
|
if (dof_count <= 0)
|
|
throw std::invalid_argument(
|
|
"The displacement element must have at least one degree of "
|
|
"freedom.");
|
|
if (displacement_dofs.Size() <= 0 ||
|
|
displacement_dofs.Size() % dof_count != 0) {
|
|
throw std::invalid_argument(
|
|
"The displacement vector size must be a positive multiple of "
|
|
"the "
|
|
"element degree-of-freedom count.");
|
|
}
|
|
|
|
m_dimension = displacement_dofs.Size() / dof_count;
|
|
m_dof_matrix.SetSize(dof_count, m_dimension);
|
|
|
|
if (ordering == mfem::Ordering::byNODES) {
|
|
for (int component = 0; component < m_dimension; ++component) {
|
|
for (int i = 0; i < dof_count; ++i) {
|
|
m_dof_matrix(i, component) =
|
|
displacement_dofs(i + component * dof_count);
|
|
}
|
|
}
|
|
} else if (ordering == mfem::Ordering::byVDIM) {
|
|
for (int i = 0; i < dof_count; ++i) {
|
|
for (int component = 0; component < m_dimension; ++component) {
|
|
m_dof_matrix(i, component) =
|
|
displacement_dofs(component + i * m_dimension);
|
|
}
|
|
}
|
|
} else {
|
|
throw std::invalid_argument("Unsupported MFEM displacement ordering.");
|
|
}
|
|
}
|
|
|
|
const mfem::FiniteElement &
|
|
ElementDisplacementData::GetElement() const noexcept {
|
|
return *m_element;
|
|
}
|
|
|
|
const mfem::DenseMatrix &
|
|
ElementDisplacementData::GetDofMatrix() const noexcept {
|
|
return m_dof_matrix;
|
|
}
|
|
|
|
int ElementDisplacementData::GetDimension() const noexcept {
|
|
return m_dimension;
|
|
}
|
|
|
|
int ElementDisplacementData::GetDofCount() const noexcept {
|
|
return m_element->GetDof();
|
|
}
|
|
|
|
mfem::Ordering::Type ElementDisplacementData::GetOrdering() const noexcept {
|
|
return m_ordering;
|
|
}
|
|
|
|
ElementDisplacementData
|
|
ElementDisplacementDataFromElementVDofs(const mfem::FiniteElement &element,
|
|
const mfem::Vector &displacement_dofs) {
|
|
return ElementDisplacementData(element, displacement_dofs,
|
|
mfem::Ordering::byNODES);
|
|
}
|
|
|
|
DomainMapper::Workspace::Workspace(const int dimension) {
|
|
SetDimension(dimension);
|
|
}
|
|
|
|
void DomainMapper::Workspace::SetDimension(const int dimension) {
|
|
if (dimension <= 0) {
|
|
throw std::invalid_argument(
|
|
"Domain mapping workspace dimension must be positive.");
|
|
}
|
|
|
|
m_dimension = dimension;
|
|
|
|
m_field_value.SetSize(dimension);
|
|
m_field_jacobian.SetSize(dimension, dimension);
|
|
|
|
m_compactification_point.coordinate = 0.0;
|
|
m_compactification_point.coordinate_gradient.SetSize(dimension);
|
|
|
|
m_reference_normal.SetSize(dimension);
|
|
m_mapped_normal.SetSize(dimension);
|
|
m_full_element_jacobian.SetSize(dimension, dimension);
|
|
|
|
m_vector_temp.SetSize(dimension);
|
|
m_matrix_temp_1.SetSize(dimension, dimension);
|
|
m_matrix_temp_2.SetSize(dimension, dimension);
|
|
|
|
m_exterior_result.physical_position.SetSize(dimension);
|
|
m_exterior_result.mapping_jacobian.SetSize(dimension, dimension);
|
|
|
|
m_exterior_variation.physical_position_variation.SetSize(dimension);
|
|
m_exterior_variation.mapping_jacobian_variation.SetSize(dimension, dimension);
|
|
}
|
|
|
|
int DomainMapper::Workspace::GetDimension() const noexcept {
|
|
return m_dimension;
|
|
}
|
|
|
|
DomainMapper::DomainMapper(
|
|
const utils::DomainMapperOptions options,
|
|
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map)
|
|
: m_options(options), m_exterior_map(std::move(exterior_map)) {
|
|
if (m_options.dimension <= 0)
|
|
throw std::invalid_argument(
|
|
"The domain-mapping dimension must be positive.");
|
|
if (m_options.vacuum_element_attribute <= 0)
|
|
throw std::invalid_argument(
|
|
"The vacuum element attribute must be positive.");
|
|
if (!m_exterior_map)
|
|
throw std::invalid_argument(
|
|
"DomainMapper requires an exterior-domain mapping.");
|
|
}
|
|
|
|
bool DomainMapper::IsCompactifiedElement(
|
|
const mfem::ElementTransformation &transformation) const noexcept {
|
|
return transformation.Attribute == m_options.vacuum_element_attribute;
|
|
}
|
|
|
|
int DomainMapper::GetDimension() const noexcept {
|
|
return m_options.dimension;
|
|
}
|
|
|
|
const compactification::ExteriorDomainMap &
|
|
DomainMapper::GetExteriorMap() const noexcept {
|
|
return *m_exterior_map;
|
|
}
|
|
|
|
GridFunctionMappingEvaluator::GridFunctionMappingEvaluator(
|
|
const DomainMapper &mapper,
|
|
const mfem::GridFunction &displacement,
|
|
const mfem::GridFunction &compactification_coordinate)
|
|
: m_mapper(mapper), m_displacement(displacement),
|
|
m_compactification_coordinate(compactification_coordinate),
|
|
m_displacement_space(displacement.FESpace()),
|
|
m_compactification_space(compactification_coordinate.FESpace()),
|
|
m_displacement_space_sequence(
|
|
m_displacement_space != nullptr ? m_displacement_space->GetSequence()
|
|
: -1),
|
|
m_compactification_space_sequence(
|
|
m_compactification_space != nullptr
|
|
? m_compactification_space->GetSequence()
|
|
: -1),
|
|
m_workspace(mapper.GetDimension()) {
|
|
if (m_displacement_space == nullptr) {
|
|
throw std::invalid_argument(
|
|
"Grid-function mapping requires a displacement finite-element space.");
|
|
}
|
|
if (m_compactification_space == nullptr) {
|
|
throw std::invalid_argument(
|
|
"Grid-function mapping requires a compactification finite-element "
|
|
"space.");
|
|
}
|
|
if (m_displacement_space->GetMesh() !=
|
|
m_compactification_space->GetMesh()) {
|
|
throw std::invalid_argument(
|
|
"Grid-function mapping fields must use the same mesh.");
|
|
}
|
|
if (m_displacement.VectorDim() != mapper.GetDimension()) {
|
|
throw std::invalid_argument(
|
|
"The displacement dimension does not match the domain mapper.");
|
|
}
|
|
if (m_compactification_coordinate.VectorDim() != 1) {
|
|
throw std::invalid_argument(
|
|
"The compactification coordinate must be a scalar grid function.");
|
|
}
|
|
if (m_displacement_space->GetMesh()->SpaceDimension() !=
|
|
mapper.GetDimension()) {
|
|
throw std::invalid_argument(
|
|
"The mapping dimension does not match the mesh space dimension.");
|
|
}
|
|
}
|
|
|
|
void GridFunctionMappingEvaluator::InvalidateCache() noexcept {
|
|
m_displacement_data.reset();
|
|
m_compactification_data.reset();
|
|
m_cached_element_id = -1;
|
|
}
|
|
|
|
void GridFunctionMappingEvaluator::ValidateFieldBindings() const {
|
|
if (m_displacement.FESpace() != m_displacement_space) {
|
|
throw std::invalid_argument(
|
|
"The displacement grid function was rebound after construction of "
|
|
"its mapping evaluator.");
|
|
}
|
|
if (m_compactification_coordinate.FESpace() != m_compactification_space) {
|
|
throw std::invalid_argument(
|
|
"The compactification grid function was rebound after construction "
|
|
"of its mapping evaluator.");
|
|
}
|
|
}
|
|
|
|
bool GridFunctionMappingEvaluator::InvalidateForChangedSpaces() {
|
|
const long displacement_sequence = m_displacement_space->GetSequence();
|
|
const long compactification_sequence =
|
|
m_compactification_space->GetSequence();
|
|
|
|
if (displacement_sequence == m_displacement_space_sequence &&
|
|
compactification_sequence == m_compactification_space_sequence) {
|
|
return false;
|
|
}
|
|
|
|
InvalidateCache();
|
|
m_displacement_space_sequence = displacement_sequence;
|
|
m_compactification_space_sequence = compactification_sequence;
|
|
return true;
|
|
}
|
|
|
|
void GridFunctionMappingEvaluator::Refresh() {
|
|
ValidateFieldBindings();
|
|
|
|
if (InvalidateForChangedSpaces()) {
|
|
return;
|
|
}
|
|
|
|
const int element_id = m_cached_element_id;
|
|
InvalidateCache();
|
|
|
|
if (element_id >= 0) {
|
|
LoadElement(element_id);
|
|
}
|
|
}
|
|
|
|
void GridFunctionMappingEvaluator::LoadElement(const int element_id) {
|
|
ValidateFieldBindings();
|
|
(void)InvalidateForChangedSpaces();
|
|
|
|
if (element_id == m_cached_element_id) {
|
|
return;
|
|
}
|
|
|
|
const mfem::FiniteElementSpace &displacement_space = *m_displacement_space;
|
|
const mfem::FiniteElementSpace &compactification_space =
|
|
*m_compactification_space;
|
|
|
|
MFEM_VERIFY(element_id >= 0 &&
|
|
element_id < displacement_space.GetMesh()->GetNE(),
|
|
"Grid-function mapping received an invalid element ID.");
|
|
|
|
mfem::DofTransformation *displacement_transformation =
|
|
displacement_space.GetElementVDofs(element_id, m_displacement_dofs);
|
|
compactification_space.GetElementDofs(element_id, m_compactification_dofs);
|
|
|
|
m_displacement.GetSubVector(m_displacement_dofs, m_element_displacement);
|
|
m_compactification_coordinate.GetSubVector(
|
|
m_compactification_dofs, m_element_compactification);
|
|
|
|
if (displacement_transformation != nullptr) {
|
|
displacement_transformation->InvTransformPrimal(m_element_displacement);
|
|
}
|
|
|
|
const mfem::FiniteElement &displacement_element =
|
|
*displacement_space.GetFE(element_id);
|
|
const mfem::FiniteElement &compactification_element =
|
|
*compactification_space.GetFE(element_id);
|
|
|
|
m_displacement_data = std::make_unique<ElementDisplacementData>(
|
|
ElementDisplacementDataFromElementVDofs(displacement_element,
|
|
m_element_displacement));
|
|
m_compactification_data =
|
|
std::make_unique<ElementCompactificationData>(
|
|
compactification_element, m_element_compactification);
|
|
m_cached_element_id = element_id;
|
|
}
|
|
|
|
MappingStatus GridFunctionMappingEvaluator::EvaluatePoint(
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point,
|
|
MappingPointContext &context) {
|
|
LoadElement(transformation.ElementNo);
|
|
const ElementMappingData data{.displacement = *m_displacement_data,
|
|
.compactification =
|
|
*m_compactification_data};
|
|
return m_mapper.EvaluatePoint(data, transformation, integration_point,
|
|
m_workspace, context);
|
|
}
|
|
|
|
MappingStatus GridFunctionMappingEvaluator::EvaluateVolume(
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point,
|
|
VolumeMappingContext &context) {
|
|
LoadElement(transformation.ElementNo);
|
|
const ElementMappingData data{.displacement = *m_displacement_data,
|
|
.compactification =
|
|
*m_compactification_data};
|
|
return m_mapper.EvaluateVolume(data, transformation, integration_point,
|
|
m_workspace, context);
|
|
}
|
|
|
|
MappingStatus GridFunctionMappingEvaluator::EvaluateFace(
|
|
mfem::FaceElementTransformations &transformation,
|
|
const FaceElementSide side,
|
|
const mfem::IntegrationPoint &integration_point,
|
|
FaceMappingContext &context) {
|
|
mfem::ElementTransformation *element_transformation =
|
|
side == FaceElementSide::element_1 ? transformation.Elem1
|
|
: transformation.Elem2;
|
|
MFEM_VERIFY(element_transformation != nullptr,
|
|
"Grid-function face mapping requires the requested element.");
|
|
LoadElement(element_transformation->ElementNo);
|
|
const ElementMappingData data{.displacement = *m_displacement_data,
|
|
.compactification =
|
|
*m_compactification_data};
|
|
return m_mapper.EvaluateFace(data, transformation, side, integration_point,
|
|
m_workspace, context);
|
|
}
|
|
|
|
VolumeQuadratureContext GridFunctionMappingEvaluator::GetQuadratureContext(
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point) {
|
|
VolumeMappingContext context;
|
|
MFEM_VERIFY(EvaluateVolume(transformation, integration_point, context) ==
|
|
MappingStatus::valid,
|
|
"Volume quadrature encountered an invalid domain mapping.");
|
|
return context.quadrature;
|
|
}
|
|
|
|
FaceQuadratureContext GridFunctionMappingEvaluator::GetFaceQuadratureContext(
|
|
mfem::FaceElementTransformations &transformation,
|
|
const mfem::IntegrationPoint &integration_point,
|
|
const FaceElementSide side) {
|
|
FaceMappingContext context;
|
|
MFEM_VERIFY(EvaluateFace(transformation, side, integration_point, context) ==
|
|
MappingStatus::valid,
|
|
"Face quadrature encountered an invalid domain mapping.");
|
|
return context.quadrature;
|
|
}
|
|
|
|
void GridFunctionMappingEvaluator::GetPhysicalPoint(
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point,
|
|
mfem::Vector &physical_position) {
|
|
MappingPointContext context;
|
|
MFEM_VERIFY(EvaluatePoint(transformation, integration_point, context) ==
|
|
MappingStatus::valid,
|
|
"Physical-point evaluation encountered an invalid domain "
|
|
"mapping.");
|
|
physical_position = context.physical_position;
|
|
}
|
|
|
|
void DomainMapper::ValidateElementData(
|
|
const ElementMappingData &element_data) const {
|
|
const ElementDisplacementData &displacement = element_data.displacement;
|
|
const ElementCompactificationData &compactification =
|
|
element_data.compactification;
|
|
|
|
if (displacement.GetDimension() != m_options.dimension) {
|
|
throw std::invalid_argument(
|
|
"Displacement field dimension does not match the domain mapper "
|
|
"dimension.");
|
|
}
|
|
|
|
if (displacement.GetElement().GetDim() != m_options.dimension) {
|
|
throw std::invalid_argument(
|
|
"Displacement finite element dimension does not match the "
|
|
"domain "
|
|
"mapper dimension.");
|
|
}
|
|
|
|
if (compactification.GetElement().GetDim() != m_options.dimension) {
|
|
throw std::invalid_argument(
|
|
"Compactification finite element dimension does not match the "
|
|
"domain "
|
|
"mapper dimension.");
|
|
}
|
|
|
|
if (displacement.GetElement().GetGeomType() !=
|
|
compactification.GetElement().GetGeomType()) {
|
|
throw std::invalid_argument(
|
|
"Displacement and compactification finite elements have "
|
|
"different "
|
|
"geometries.");
|
|
}
|
|
|
|
if (compactification.GetElement().GetRangeType() !=
|
|
mfem::FiniteElement::SCALAR) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate requires a scalar finite element.");
|
|
}
|
|
|
|
if (compactification.GetElement().GetMapType() !=
|
|
mfem::FiniteElement::VALUE) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate requires a value-mapped finite "
|
|
"element.");
|
|
}
|
|
|
|
if (compactification.GetElement().GetDerivType() !=
|
|
mfem::FiniteElement::GRAD) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate finite element does not provide a "
|
|
"gradient.");
|
|
}
|
|
|
|
if (compactification.GetDofCount() !=
|
|
compactification.GetElement().GetDof()) {
|
|
throw std::invalid_argument(
|
|
"Compactification coordinate DOF count does not match its "
|
|
"finite "
|
|
"element.");
|
|
}
|
|
}
|
|
|
|
MappingStatus DomainMapper::EvaluateCompactificationCoordinate(
|
|
const ElementCompactificationData &compactification,
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
|
|
CompactificationPointData &point_data) const {
|
|
const mfem::FiniteElement &element = compactification.GetElement();
|
|
const mfem::Vector &dofs = compactification.GetDofs();
|
|
const int dof_count = element.GetDof();
|
|
|
|
if (workspace.GetDimension() != m_options.dimension ||
|
|
transformation.GetSpaceDim() != m_options.dimension ||
|
|
element.GetDim() != m_options.dimension) {
|
|
return MappingStatus::invalid_dimension;
|
|
}
|
|
|
|
if (dofs.Size() != dof_count) {
|
|
return MappingStatus::invalid_dimension;
|
|
}
|
|
|
|
for (int i = 0; i < dofs.Size(); ++i) {
|
|
if (!std::isfinite(dofs(i)))
|
|
return MappingStatus::non_finite_input;
|
|
}
|
|
|
|
transformation.SetIntPoint(&integration_point);
|
|
|
|
workspace.m_compactification_shape.SetSize(dof_count);
|
|
workspace.m_compactification_dshape.SetSize(dof_count, m_options.dimension);
|
|
|
|
element.CalcShape(integration_point, workspace.m_compactification_shape);
|
|
element.CalcPhysDShape(transformation, workspace.m_compactification_dshape);
|
|
|
|
point_data.coordinate = dofs * workspace.m_compactification_shape;
|
|
point_data.coordinate_gradient.SetSize(m_options.dimension);
|
|
workspace.m_compactification_dshape.MultTranspose(
|
|
dofs, point_data.coordinate_gradient);
|
|
|
|
if (!std::isfinite(point_data.coordinate)) {
|
|
return MappingStatus::non_finite_result;
|
|
}
|
|
|
|
for (int d = 0; d < point_data.coordinate_gradient.Size(); ++d) {
|
|
if (!std::isfinite(point_data.coordinate_gradient(d)))
|
|
return MappingStatus::non_finite_result;
|
|
}
|
|
|
|
return MappingStatus::valid;
|
|
}
|
|
|
|
void DomainMapper::EvaluateField(
|
|
const ElementDisplacementData &field,
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
|
|
mfem::Vector &value, mfem::DenseMatrix &jacobian) const {
|
|
transformation.SetIntPoint(&integration_point);
|
|
|
|
const mfem::FiniteElement &element = field.GetElement();
|
|
const mfem::DenseMatrix &dof_matrix = field.GetDofMatrix();
|
|
|
|
workspace.m_shape.SetSize(element.GetDof());
|
|
workspace.m_mesh_dshape.SetSize(element.GetDof(), m_options.dimension);
|
|
|
|
element.CalcShape(integration_point, workspace.m_shape);
|
|
element.CalcPhysDShape(transformation, workspace.m_mesh_dshape);
|
|
|
|
value.SetSize(m_options.dimension);
|
|
dof_matrix.MultTranspose(workspace.m_shape, value);
|
|
|
|
jacobian.SetSize(m_options.dimension, m_options.dimension);
|
|
mfem::MultAtB(dof_matrix, workspace.m_mesh_dshape, jacobian);
|
|
}
|
|
|
|
MappingStatus DomainMapper::EvaluatePoint(
|
|
const ElementMappingData &element_data,
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
|
|
MappingPointContext &context) const {
|
|
ValidateElementData(element_data);
|
|
|
|
if (workspace.GetDimension() != m_options.dimension)
|
|
throw std::invalid_argument(
|
|
"The mapping workspace has the wrong dimension.");
|
|
if (transformation.GetSpaceDim() != m_options.dimension)
|
|
throw std::invalid_argument(
|
|
"The element transformation has the wrong spatial dimension.");
|
|
if (transformation.GetGeometryType() !=
|
|
element_data.displacement.GetElement().GetGeomType())
|
|
throw std::invalid_argument(
|
|
"The element transformation geometry does not match the "
|
|
"supplied "
|
|
"element data.");
|
|
|
|
transformation.SetIntPoint(&integration_point);
|
|
|
|
context.reference_position.SetSize(m_options.dimension);
|
|
transformation.Transform(integration_point, context.reference_position);
|
|
|
|
EvaluateField(element_data.displacement, transformation, integration_point,
|
|
workspace, workspace.m_field_value, workspace.m_field_jacobian);
|
|
|
|
if (!vector_is_finite(context.reference_position) ||
|
|
!vector_is_finite(workspace.m_field_value) ||
|
|
!matrix_is_finite(workspace.m_field_jacobian)) {
|
|
return MappingStatus::non_finite_input;
|
|
}
|
|
|
|
context.displaced_position.SetSize(m_options.dimension);
|
|
context.displaced_position = context.reference_position;
|
|
context.displaced_position += workspace.m_field_value;
|
|
|
|
context.displacement_jacobian.SetSize(m_options.dimension,
|
|
m_options.dimension);
|
|
context.displacement_jacobian = workspace.m_field_jacobian;
|
|
for (int i = 0; i < m_options.dimension; ++i)
|
|
context.displacement_jacobian(i, i) += 1.0;
|
|
|
|
context.compactified = IsCompactifiedElement(transformation);
|
|
|
|
if (context.compactified) {
|
|
const MappingStatus coordinate_status = EvaluateCompactificationCoordinate(
|
|
element_data.compactification, transformation, integration_point,
|
|
workspace, workspace.m_compactification_point);
|
|
|
|
if (coordinate_status != MappingStatus::valid)
|
|
return coordinate_status;
|
|
|
|
const compactification::ExteriorMapInput exterior_input{
|
|
.reference_position = context.reference_position,
|
|
.displaced_position = context.displaced_position,
|
|
.displacement_jacobian = context.displacement_jacobian,
|
|
.compactification_coordinate =
|
|
workspace.m_compactification_point.coordinate,
|
|
.compactification_coordinate_gradient =
|
|
workspace.m_compactification_point.coordinate_gradient};
|
|
|
|
const MappingStatus exterior_status =
|
|
m_exterior_map->Evaluate(exterior_input, workspace.m_exterior_result);
|
|
if (exterior_status != MappingStatus::valid)
|
|
return exterior_status;
|
|
|
|
context.physical_position = workspace.m_exterior_result.physical_position;
|
|
context.mapping_jacobian = workspace.m_exterior_result.mapping_jacobian;
|
|
} else {
|
|
context.physical_position = context.displaced_position;
|
|
context.mapping_jacobian = context.displacement_jacobian;
|
|
}
|
|
|
|
if (!vector_is_finite(context.physical_position) ||
|
|
!matrix_is_finite(context.mapping_jacobian))
|
|
return MappingStatus::non_finite_result;
|
|
|
|
context.mapping_determinant = context.mapping_jacobian.Det();
|
|
if (!std::isfinite(context.mapping_determinant))
|
|
return MappingStatus::non_finite_result;
|
|
if (context.mapping_determinant <= 0.0)
|
|
return MappingStatus::non_positive_determinant;
|
|
|
|
context.inverse_mapping_jacobian.SetSize(m_options.dimension,
|
|
m_options.dimension);
|
|
mfem::CalcInverse(context.mapping_jacobian, context.inverse_mapping_jacobian);
|
|
|
|
if (!matrix_is_finite(context.inverse_mapping_jacobian))
|
|
return MappingStatus::non_finite_result;
|
|
|
|
return MappingStatus::valid;
|
|
}
|
|
|
|
MappingStatus DomainMapper::EvaluateVolume(
|
|
const ElementMappingData &element_data,
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
|
|
VolumeMappingContext &context) const {
|
|
const MappingStatus point_status =
|
|
EvaluatePoint(element_data, transformation, integration_point, workspace,
|
|
context.mapping);
|
|
if (point_status != MappingStatus::valid)
|
|
return point_status;
|
|
|
|
transformation.SetIntPoint(&integration_point);
|
|
mfem::Mult(context.mapping.mapping_jacobian, transformation.Jacobian(),
|
|
workspace.m_full_element_jacobian);
|
|
|
|
context.quadrature.J_inv.SetSize(m_options.dimension, m_options.dimension);
|
|
mfem::CalcInverse(workspace.m_full_element_jacobian,
|
|
context.quadrature.J_inv);
|
|
|
|
context.quadrature.detJ = context.mapping.mapping_determinant;
|
|
context.quadrature.weight = integration_point.weight *
|
|
transformation.Weight() *
|
|
context.mapping.mapping_determinant;
|
|
|
|
if (!matrix_is_finite(context.quadrature.J_inv) ||
|
|
!std::isfinite(context.quadrature.weight))
|
|
return MappingStatus::non_finite_result;
|
|
if (context.quadrature.weight <= 0.0)
|
|
return MappingStatus::non_positive_determinant;
|
|
|
|
return MappingStatus::valid;
|
|
}
|
|
|
|
mfem::ElementTransformation &
|
|
DomainMapper::SelectFaceElementTransformation(
|
|
mfem::FaceElementTransformations &transformation,
|
|
const FaceElementSide side) {
|
|
if (side == FaceElementSide::element_1) {
|
|
MFEM_VERIFY(transformation.Elem1 != nullptr,
|
|
"The face does not have an element-1 transformation.");
|
|
return *transformation.Elem1;
|
|
}
|
|
|
|
MFEM_VERIFY(transformation.Elem2 != nullptr,
|
|
"The face does not have an element-2 transformation.");
|
|
return *transformation.Elem2;
|
|
}
|
|
|
|
const mfem::IntegrationPoint &
|
|
DomainMapper::SelectFaceElementIntegrationPoint(
|
|
mfem::FaceElementTransformations &transformation,
|
|
const FaceElementSide side) {
|
|
mfem::ElementTransformation &element_transformation =
|
|
SelectFaceElementTransformation(transformation, side);
|
|
return element_transformation.GetIntPoint();
|
|
}
|
|
|
|
MappingStatus DomainMapper::EvaluateFace(
|
|
const ElementMappingData &element_data,
|
|
mfem::FaceElementTransformations &transformation,
|
|
const FaceElementSide side, const mfem::IntegrationPoint &integration_point,
|
|
Workspace &workspace, FaceMappingContext &context) const {
|
|
transformation.SetAllIntPoints(&integration_point);
|
|
mfem::ElementTransformation &element_transformation =
|
|
SelectFaceElementTransformation(transformation, side);
|
|
const mfem::IntegrationPoint &element_integration_point =
|
|
SelectFaceElementIntegrationPoint(transformation, side);
|
|
|
|
const MappingStatus point_status =
|
|
EvaluatePoint(element_data, element_transformation,
|
|
element_integration_point, workspace, context.mapping);
|
|
if (point_status != MappingStatus::valid)
|
|
return point_status;
|
|
|
|
workspace.m_reference_normal.SetSize(m_options.dimension);
|
|
mfem::CalcOrtho(transformation.Jacobian(), workspace.m_reference_normal);
|
|
if (side == FaceElementSide::element_2)
|
|
workspace.m_reference_normal *= -1.0;
|
|
|
|
const double reference_normal_magnitude =
|
|
workspace.m_reference_normal.Norml2();
|
|
if (!std::isfinite(reference_normal_magnitude) ||
|
|
reference_normal_magnitude <= 0.0)
|
|
return MappingStatus::non_finite_result;
|
|
|
|
context.reference_normal.SetSize(m_options.dimension);
|
|
context.reference_normal = workspace.m_reference_normal;
|
|
context.reference_normal /= reference_normal_magnitude;
|
|
|
|
context.mapping.inverse_mapping_jacobian.MultTranspose(
|
|
workspace.m_reference_normal, workspace.m_mapped_normal);
|
|
workspace.m_mapped_normal *= context.mapping.mapping_determinant;
|
|
|
|
const double mapped_normal_magnitude = workspace.m_mapped_normal.Norml2();
|
|
if (!std::isfinite(mapped_normal_magnitude) || mapped_normal_magnitude <= 0.0)
|
|
return MappingStatus::non_finite_result;
|
|
|
|
context.quadrature.normal.SetSize(m_options.dimension);
|
|
context.quadrature.normal = workspace.m_mapped_normal;
|
|
context.quadrature.normal /= mapped_normal_magnitude;
|
|
|
|
context.reference_surface_weight =
|
|
integration_point.weight * reference_normal_magnitude;
|
|
context.physical_surface_weight =
|
|
integration_point.weight * mapped_normal_magnitude;
|
|
|
|
context.quadrature.ds = context.reference_surface_weight;
|
|
context.quadrature.v_dot_n_scale =
|
|
mapped_normal_magnitude / reference_normal_magnitude;
|
|
|
|
if (!vector_is_finite(context.quadrature.normal) ||
|
|
!std::isfinite(context.reference_surface_weight) ||
|
|
!std::isfinite(context.physical_surface_weight) ||
|
|
!std::isfinite(context.quadrature.v_dot_n_scale)) {
|
|
return MappingStatus::non_finite_result;
|
|
}
|
|
|
|
return MappingStatus::valid;
|
|
}
|
|
|
|
MappingStatus DomainMapper::EvaluatePointVariation(
|
|
const ElementMappingData &element_data,
|
|
const ElementDisplacementData &direction,
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point,
|
|
const MappingPointContext &base_context, Workspace &workspace,
|
|
MappingPointVariation &variation) const {
|
|
ValidateElementData(element_data);
|
|
const ElementMappingData direction_data{.displacement = direction,
|
|
.compactification =
|
|
element_data.compactification};
|
|
ValidateElementData(direction_data);
|
|
|
|
if (element_data.displacement.GetDofCount() != direction.GetDofCount())
|
|
throw std::invalid_argument(
|
|
"The displacement and direction elements have different "
|
|
"degree-of-freedom counts.");
|
|
if (workspace.GetDimension() != m_options.dimension)
|
|
throw std::invalid_argument(
|
|
"The mapping workspace has the wrong dimension.");
|
|
if (base_context.compactified != IsCompactifiedElement(transformation))
|
|
throw std::invalid_argument(
|
|
"The base mapping context does not match the current element "
|
|
"domain.");
|
|
|
|
EvaluateField(direction, transformation, integration_point, workspace,
|
|
workspace.m_field_value, workspace.m_field_jacobian);
|
|
|
|
if (!vector_is_finite(workspace.m_field_value) ||
|
|
!matrix_is_finite(workspace.m_field_jacobian))
|
|
return MappingStatus::non_finite_input;
|
|
|
|
variation.displacement_variation = workspace.m_field_value;
|
|
variation.displacement_jacobian_variation = workspace.m_field_jacobian;
|
|
|
|
if (base_context.compactified) {
|
|
const MappingStatus coordinate_status = EvaluateCompactificationCoordinate(
|
|
element_data.compactification, transformation, integration_point,
|
|
workspace, workspace.m_compactification_point);
|
|
|
|
if (coordinate_status != MappingStatus::valid)
|
|
return coordinate_status;
|
|
|
|
const compactification::ExteriorMapInput exterior_input{
|
|
.reference_position = base_context.reference_position,
|
|
.displaced_position = base_context.displaced_position,
|
|
.displacement_jacobian = base_context.displacement_jacobian,
|
|
.compactification_coordinate =
|
|
workspace.m_compactification_point.coordinate,
|
|
.compactification_coordinate_gradient =
|
|
workspace.m_compactification_point.coordinate_gradient};
|
|
|
|
workspace.m_exterior_result.physical_position =
|
|
base_context.physical_position;
|
|
workspace.m_exterior_result.mapping_jacobian =
|
|
base_context.mapping_jacobian;
|
|
|
|
const compactification::ExteriorMapDirection exterior_direction{
|
|
.displaced_position_variation = variation.displacement_variation,
|
|
.displacement_jacobian_variation =
|
|
variation.displacement_jacobian_variation};
|
|
|
|
// ReSharper disable once CppTooWideScopeInitStatement
|
|
const MappingStatus exterior_status = m_exterior_map->EvaluateVariation(
|
|
exterior_input, workspace.m_exterior_result, exterior_direction,
|
|
workspace.m_exterior_variation);
|
|
|
|
if (exterior_status != MappingStatus::valid) {
|
|
return exterior_status;
|
|
}
|
|
|
|
variation.physical_position_variation =
|
|
workspace.m_exterior_variation.physical_position_variation;
|
|
variation.mapping_jacobian_variation =
|
|
workspace.m_exterior_variation.mapping_jacobian_variation;
|
|
} else {
|
|
variation.physical_position_variation = variation.displacement_variation;
|
|
variation.mapping_jacobian_variation =
|
|
variation.displacement_jacobian_variation;
|
|
}
|
|
|
|
mfem::Mult(base_context.inverse_mapping_jacobian,
|
|
variation.mapping_jacobian_variation, workspace.m_matrix_temp_1);
|
|
|
|
double trace = 0.0;
|
|
for (int i = 0; i < m_options.dimension; ++i)
|
|
trace += workspace.m_matrix_temp_1(i, i);
|
|
variation.mapping_determinant_variation =
|
|
base_context.mapping_determinant * trace;
|
|
|
|
variation.inverse_mapping_jacobian_variation.SetSize(m_options.dimension,
|
|
m_options.dimension);
|
|
mfem::Mult(workspace.m_matrix_temp_1, base_context.inverse_mapping_jacobian,
|
|
variation.inverse_mapping_jacobian_variation);
|
|
variation.inverse_mapping_jacobian_variation *= -1.0;
|
|
|
|
if (!vector_is_finite(variation.physical_position_variation) ||
|
|
!matrix_is_finite(variation.mapping_jacobian_variation) ||
|
|
!matrix_is_finite(variation.inverse_mapping_jacobian_variation) ||
|
|
!std::isfinite(variation.mapping_determinant_variation)) {
|
|
return MappingStatus::non_finite_result;
|
|
}
|
|
|
|
return MappingStatus::valid;
|
|
}
|
|
|
|
MappingStatus DomainMapper::EvaluateVolumeVariation(
|
|
const ElementMappingData &element_data,
|
|
const ElementDisplacementData &direction,
|
|
mfem::ElementTransformation &transformation,
|
|
const mfem::IntegrationPoint &integration_point,
|
|
const VolumeMappingContext &base_context, Workspace &workspace,
|
|
VolumeMappingVariation &variation) const {
|
|
const MappingStatus point_status = EvaluatePointVariation(
|
|
element_data, direction, transformation, integration_point,
|
|
base_context.mapping, workspace, variation.mapping);
|
|
if (point_status != MappingStatus::valid)
|
|
return point_status;
|
|
|
|
transformation.SetIntPoint(&integration_point);
|
|
mfem::Mult(variation.mapping.mapping_jacobian_variation,
|
|
transformation.Jacobian(), workspace.m_full_element_jacobian);
|
|
mfem::Mult(base_context.quadrature.J_inv, workspace.m_full_element_jacobian,
|
|
workspace.m_matrix_temp_1);
|
|
|
|
variation.inverse_element_jacobian_variation.SetSize(m_options.dimension,
|
|
m_options.dimension);
|
|
mfem::Mult(workspace.m_matrix_temp_1, base_context.quadrature.J_inv,
|
|
variation.inverse_element_jacobian_variation);
|
|
variation.inverse_element_jacobian_variation *= -1.0;
|
|
|
|
variation.weight_variation = integration_point.weight *
|
|
transformation.Weight() *
|
|
variation.mapping.mapping_determinant_variation;
|
|
|
|
if (!matrix_is_finite(variation.inverse_element_jacobian_variation) ||
|
|
!std::isfinite(variation.weight_variation))
|
|
return MappingStatus::non_finite_result;
|
|
|
|
return MappingStatus::valid;
|
|
}
|
|
|
|
MappingStatus DomainMapper::EvaluateFaceVariation(
|
|
const ElementMappingData &element_data,
|
|
const ElementDisplacementData &direction,
|
|
mfem::FaceElementTransformations &transformation,
|
|
const FaceElementSide side, const mfem::IntegrationPoint &integration_point,
|
|
const FaceMappingContext &base_context, Workspace &workspace,
|
|
FaceMappingVariation &variation) const {
|
|
transformation.SetAllIntPoints(&integration_point);
|
|
mfem::ElementTransformation &element_transformation =
|
|
SelectFaceElementTransformation(transformation, side);
|
|
const mfem::IntegrationPoint &element_integration_point =
|
|
SelectFaceElementIntegrationPoint(transformation, side);
|
|
|
|
const MappingStatus point_status =
|
|
EvaluatePointVariation(element_data, direction, element_transformation,
|
|
element_integration_point, base_context.mapping,
|
|
workspace, variation.mapping);
|
|
if (point_status != MappingStatus::valid)
|
|
return point_status;
|
|
|
|
workspace.m_reference_normal.SetSize(m_options.dimension);
|
|
mfem::CalcOrtho(transformation.Jacobian(), workspace.m_reference_normal);
|
|
if (side == FaceElementSide::element_2)
|
|
workspace.m_reference_normal *= -1.0;
|
|
|
|
const double reference_normal_magnitude =
|
|
workspace.m_reference_normal.Norml2();
|
|
if (!std::isfinite(reference_normal_magnitude) ||
|
|
reference_normal_magnitude <= 0.0)
|
|
return MappingStatus::non_finite_result;
|
|
|
|
base_context.mapping.inverse_mapping_jacobian.MultTranspose(
|
|
workspace.m_reference_normal, workspace.m_vector_temp);
|
|
workspace.m_mapped_normal = workspace.m_vector_temp;
|
|
workspace.m_mapped_normal *= base_context.mapping.mapping_determinant;
|
|
|
|
variation.physical_normal_variation.SetSize(m_options.dimension);
|
|
variation.mapping.inverse_mapping_jacobian_variation.MultTranspose(
|
|
workspace.m_reference_normal, variation.physical_normal_variation);
|
|
variation.physical_normal_variation *=
|
|
base_context.mapping.mapping_determinant;
|
|
variation.physical_normal_variation.Add(
|
|
variation.mapping.mapping_determinant_variation, workspace.m_vector_temp);
|
|
|
|
const double mapped_normal_magnitude = workspace.m_mapped_normal.Norml2();
|
|
if (!std::isfinite(mapped_normal_magnitude) || mapped_normal_magnitude <= 0.0)
|
|
return MappingStatus::non_finite_result;
|
|
|
|
const double mapped_normal_magnitude_variation =
|
|
base_context.quadrature.normal * variation.physical_normal_variation;
|
|
|
|
variation.physical_normal_variation.Add(-mapped_normal_magnitude_variation,
|
|
base_context.quadrature.normal);
|
|
variation.physical_normal_variation /= mapped_normal_magnitude;
|
|
|
|
variation.physical_surface_weight_variation =
|
|
integration_point.weight * mapped_normal_magnitude_variation;
|
|
variation.normal_flux_scale_variation =
|
|
mapped_normal_magnitude_variation / reference_normal_magnitude;
|
|
|
|
if (!vector_is_finite(variation.physical_normal_variation) ||
|
|
!std::isfinite(variation.physical_surface_weight_variation) ||
|
|
!std::isfinite(variation.normal_flux_scale_variation)) {
|
|
return MappingStatus::non_finite_result;
|
|
}
|
|
|
|
return MappingStatus::valid;
|
|
}
|
|
} // namespace mean_field::mapping
|