module; #include #include #include #include #include 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 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( ElementDisplacementDataFromElementVDofs(displacement_element, m_element_displacement) ); m_compactification_data = std::make_unique(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