geometry_inspection.cpp serif/geometry/deformation/backend/mfem/geometry_inspection.hpp algorithm mfem/fem/gridfunc.hpp mfem/fem/pfespace.hpp serif/discretization/backend/mfem/tensor_basis.hpp serif/mapping/domain_mapper.hpp serif/utils/checks/finite.hpp serif/utils/checks/invertible.hpp serif serif::geometry serif::geometry::deformation serif::geometry::deformation::backend serif::geometry::deformation::backend::mfem #include"serif/geometry/deformation/backend/mfem/geometry_inspection.hpp" #include<algorithm> #include"mfem/fem/gridfunc.hpp" #include"mfem/fem/pfespace.hpp" #include"serif/discretization/backend/mfem/tensor_basis.hpp" #include"serif/mapping/domain_mapper.hpp" #include"serif/utils/checks/finite.hpp" #include"serif/utils/checks/invertible.hpp" namespaceserif::geometry::deformation::backend::mfem { namespace { [[nodiscard]]DeformationStatusvalidate_inspection_space(const::mfem::FiniteElementSpace&space) { const::mfem::Mesh*mesh=space.GetMesh(); if(mesh==nullptr||space.FEColl()==nullptr||mesh->GetNE()==0||mesh->Dimension()!=mesh->SpaceDimension()||mesh->SpaceDimension()<1||mesh->SpaceDimension()>3||space.GetVDim()!=mesh->SpaceDimension()||space.GetTrueVSize()<=0||space.GetTrueVSize()%space.GetVDim()!=0||(space.GetOrdering()!=::mfem::Ordering::byNODES&&space.GetOrdering()!=::mfem::Ordering::byVDIM)) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Geometryinspectionrequiresanonemptyspatialdisplacementspaceonavolumemeshinonetothreedimensions."}); } #ifdefMFEM_USE_MPI if(dynamic_cast<const::mfem::ParFiniteElementSpace*>(&space)!=nullptr) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Deformationgeometryinspectioncurrentlyrequiresaserialfinite-elementspace."}); } #endif for(intelement=0;element<mesh->GetNE();++element) { const::mfem::FiniteElement*finite_element=space.GetFE(element); if(finite_element==nullptr||finite_element->GetDof()<=0||finite_element->GetDim()!=mesh->Dimension()||finite_element->GetRangeType()!=::mfem::FiniteElement::SCALAR||finite_element->GetMapType()!=::mfem::FiniteElement::VALUE||finite_element->GetDerivType()!=::mfem::FiniteElement::GRAD) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Deformationinspectionrequiresscalarvalue-mappedbasiscopieswithspatialgradients.",.element=element}); } } return{}; } [[nodiscard]]DeformationStatusinspect_element(const::mfem::FiniteElementSpace&space,const::mfem::GridFunction&field,constintelement,discretization::backend::mfem::TensorBasisCache&basis_cache,constmapping::DomainMapper*reference_geometry,discretization::backend::mfem::TensorWorkspace&workspace,::mfem::Vector&samples,::mfem::DenseMatrix&gradient,DeformationGeometryReport&report) { ::mfem::ElementTransformation*transformation=space.GetMesh()->GetElementTransformation(element); if(transformation==nullptr) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_geometry,.message="Geometryinspectionrequiresanelementtransformation.",.element=element}); } constintdimension=space.GetVDim(); constintdegree=space.GetFE(element)->GetOrder(); constintorder=std::max(degree+2,2*dimension*degree); const::mfem::IntegrationRule&rule=::mfem::IntRules.Get(transformation->GetGeometryType(),order); constautobasis=dimension==3?basis_cache.get(*space.GetFE(element),rule,discretization::backend::mfem::ReferenceOperation::gradient,dimension):nullptr; if(basis) { ::mfem::Array<int>dofs; const::mfem::DofTransformation*dof_transformation=space.GetElementVDofs(element,dofs); ::mfem::Vectorcoefficients; field.GetSubVector(dofs,coefficients); if(dof_transformation!=nullptr) { dof_transformation->InvTransformPrimal(coefficients); } //MFEM'selementVDofsaregroupedbycomponentforeitherglobalordering. basis->apply(coefficients,samples,workspace); } for(intpoint=0;point<rule.GetNPoints();++point) { transformation->SetIntPoint(&rule.IntPoint(point)); constmapping::DMFixedMeshGeometry*fixed_geometry=basis&&reference_geometry!=nullptr?reference_geometry->get_fixed_mesh_geometry(*transformation,rule.IntPoint(point)):nullptr; const::mfem::DenseMatrix&reference_jacobian=fixed_geometry!=nullptr?fixed_geometry->jacobian:transformation->Jacobian(); if(!utils::checks::is_finite(reference_jacobian)) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="AreferencemeshJacobianisnotfiniteatadeformationinspectionpoint.",.element=element,.point=point}); } if(!utils::checks::has_stable_inverse(reference_jacobian)) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_geometry,.message="AreferencemeshJacobianissingular,nonfinite,ortooill-conditionedforreliableinversion.",.element=element,.point=point}); } if(basis) { const::mfem::DenseMatrix&inverse=fixed_geometry!=nullptr?fixed_geometry->inverse_jacobian:transformation->InverseJacobian(); for(intcomponent=0;component<dimension;++component) { for(intaxis=0;axis<dimension;++axis) { gradient(component,axis)=0.0; for(intreference_axis=0;reference_axis<dimension;++reference_axis) { gradient(component,axis)+=samples[9*point+3*component+reference_axis]*inverse(reference_axis,axis); } } } } else { field.GetVectorGradient(*transformation,gradient); } for(intcomponent=0;component<dimension;++component) { gradient(component,component)+=1.0; } constdoubledeterminant=gradient.Det(); if(!utils::checks::is_finite(gradient)||!std::isfinite(determinant)) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="ThecomputationaldeformationJacobianisnotfinite.",.element=element,.point=point}); } ++report.sampled_point_count; if(determinant<report.minimum_jacobian_determinant) { report.minimum_jacobian_determinant=determinant; report.minimum_element=element; report.minimum_point=point; } } return{}; } }//namespace std::expected<DeformationGeometryReport,DeformationFailure>inspect_deformation_geometry(const::mfem::FiniteElementSpace&displacement_space,const::mfem::Vector&displacement) { discretization::backend::mfem::TensorBasisCachebasis_cache; returninspect_deformation_geometry(displacement_space,displacement,basis_cache); } std::expected<DeformationGeometryReport,DeformationFailure>inspect_deformation_geometry(const::mfem::FiniteElementSpace&displacement_space,const::mfem::Vector&displacement,discretization::backend::mfem::TensorBasisCache&basis_cache,constmapping::DomainMapper*reference_geometry) { if(constDeformationStatusstatus=validate_inspection_space(displacement_space);!status) { returnstd::unexpected(status.error()); } if(constDeformationStatusstatus=validate_deformation_values(std::span<constdouble>(displacement.GetData(),displacement.Size()),displacement_space.GetTrueVSize());!status) { returnstd::unexpected(status.error()); } //MFEMrequiresamutablespacepointerforaGridFunction,althoughtheseoperationsonlywriteitsownedcoefficients. ::mfem::GridFunctionfield(const_cast<::mfem::FiniteElementSpace*>(&displacement_space)); field.SetFromTrueDofs(displacement); discretization::backend::mfem::TensorWorkspaceworkspace; ::mfem::Vectorsamples; ::mfem::DenseMatrixgradient(displacement_space.GetVDim()); DeformationGeometryReportreport; for(intelement=0;element<displacement_space.GetNE();++element) { if(constDeformationStatusstatus=inspect_element(displacement_space,field,element,basis_cache,reference_geometry,workspace,samples,gradient,report);!status) { returnstd::unexpected(status.error()); } } returnreport; } std::expected<DeformationGeometryReport,DeformationFailure>inspect_deformation_geometry(const::mfem::FiniteElementSpace&displacement_space,conststd::span<constdouble>displacement,const::mfem::Ordering::Typecoefficient_ordering) { discretization::backend::mfem::TensorBasisCachebasis_cache; returninspect_deformation_geometry(displacement_space,displacement,coefficient_ordering,basis_cache); } std::expected<DeformationGeometryReport,DeformationFailure>inspect_deformation_geometry(const::mfem::FiniteElementSpace&displacement_space,conststd::span<constdouble>displacement,const::mfem::Ordering::Typecoefficient_ordering,discretization::backend::mfem::TensorBasisCache&basis_cache,constmapping::DomainMapper*reference_geometry) { if(constDeformationStatusstatus=validate_inspection_space(displacement_space);!status) { returnstd::unexpected(status.error()); } if(coefficient_ordering!=::mfem::Ordering::byNODES&&coefficient_ordering!=::mfem::Ordering::byVDIM) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_configuration,.message="DeformationinspectionrequiresbyNODESorbyVDIMcoefficientordering."}); } if(constDeformationStatusstatus=validate_deformation_values(displacement,displacement_space.GetTrueVSize());!status) { returnstd::unexpected(status.error()); } ::mfem::Vectornative_displacement(displacement_space.GetTrueVSize()); constintdimension=displacement_space.GetVDim(); constintscalar_nodes=native_displacement.Size()/dimension; for(intcomponent=0;component<dimension;++component) { for(intnode=0;node<scalar_nodes;++node) { constintinput=coefficient_ordering==::mfem::Ordering::byNODES?node+scalar_nodes*component:component+dimension*node; constintoutput=displacement_space.GetOrdering()==::mfem::Ordering::byNODES?node+scalar_nodes*component:component+dimension*node; native_displacement[output]=displacement[input]; } } returninspect_deformation_geometry(displacement_space,native_displacement,basis_cache,reference_geometry); } }//namespaceserif::geometry::deformation::backend::mfem