geometry_step.cpp serif/backend/mfem/geometry/deformation/geometry_step.hpp algorithm cmath exception limits optional utility vector mfem/fem/gridfunc.hpp mfem/fem/pfespace.hpp serif/geometry/deformation/detail/determinant_polynomial.hpp serif/backend/mfem/mapping/domain_mapper.hpp serif/backend/mfem/shared/finite.hpp serif/backend/mfem/shared/parallel.hpp serif serif::geometry serif::geometry::deformation serif::geometry::deformation::backend serif::geometry::deformation::backend::mfem #include"serif/backend/mfem/geometry/deformation/geometry_step.hpp" #include<algorithm> #include<cmath> #include<exception> #include<limits> #include<optional> #include<utility> #include<vector> #include"mfem/fem/gridfunc.hpp" #include"mfem/fem/pfespace.hpp" #include"serif/geometry/deformation/detail/determinant_polynomial.hpp" #include"serif/backend/mfem/mapping/domain_mapper.hpp" #include"serif/backend/mfem/shared/finite.hpp" #include"serif/backend/mfem/shared/parallel.hpp" namespaceserif::geometry::deformation::backend::mfem{ namespace{ [[nodiscard]]DeformationStatusvalidate_inputs(constmapping::DomainMapper&mapper,const::mfem::FiniteElementSpace&displacement_space,const::mfem::GridFunction&compactification_coordinate,const::mfem::Vector&accepted,const::mfem::Vector&direction,conststd::span<constGeometrySampleRule>rules,constGeometryStepOptions&options){ if(!std::isfinite(options.maximum_step)||options.maximum_step<=0.0||!std::isfinite(options.determinant_floor)||options.determinant_floor<0.0||!std::isfinite(options.fraction_to_boundary)||options.fraction_to_boundary<=0.0||options.fraction_to_boundary>=1.0){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_configuration,.message="Geometrystepcontrolsrequireafinitepositivemaximumstep,nonnegativedeterminantfloor,andfractiontoboundarystrictlybetweenzeroandone."}); } const::mfem::Mesh*mesh=displacement_space.GetMesh(); const::mfem::FiniteElementSpace*compactification_space=compactification_coordinate.FESpace(); constintdimension=mapper.get_dimension(); const::mfem::Ordering::Typeordering=displacement_space.GetOrdering(); if(mesh==nullptr||compactification_space==nullptr||compactification_space->GetMesh()!=mesh||dimension<1||dimension>3||mesh->Dimension()!=dimension||mesh->SpaceDimension()!=dimension||displacement_space.GetVDim()!=dimension||compactification_space->GetVDim()!=1||compactification_coordinate.Size()!=compactification_space->GetVSize()||(ordering!=::mfem::Ordering::byNODES&&ordering!=::mfem::Ordering::byVDIM)){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Geometrystepestimationrequirescompatiblescalarcompactificationandvectordisplacementspacesononemeshinonetothreedimensions."}); } #ifdefMFEM_USE_MPI if(dynamic_cast<const::mfem::ParFiniteElementSpace*>(&displacement_space)!=nullptr||dynamic_cast<const::mfem::ParFiniteElementSpace*>(compactification_space)!=nullptr){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Geometrystepestimationcurrentlyrequiresserialfinite-elementspaces."}); } #endif if(accepted.Size()!=displacement_space.GetTrueVSize()||direction.Size()!=displacement_space.GetTrueVSize()){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_size,.message="Geometrystepdisplacementvectorsmustmatchthespace'strue-DOFcount."}); } if(!utils::checks::is_finite(accepted)||!utils::checks::is_finite(direction)||!utils::checks::is_finite(compactification_coordinate)){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="Geometrystepdisplacementandcompactificationcoefficientsmustallbefinite."}); } if(rules.empty()||rules.size()>static_cast<std::size_t>(std::numeric_limits<int>::max())){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_configuration,.message="Geometrystepestimationrequiresanonemptylistofsamplingrulesrepresentablebyintegerruleindices."}); } return{}; } [[nodiscard]]DeformationStatusvalidate_rule(constmapping::DomainMapper&mapper,const::mfem::FiniteElementSpace&displacement_space,const::mfem::FiniteElementSpace&compactification_space,constGeometrySampleRule&rule){ ::mfem::Mesh&mesh=*displacement_space.GetMesh(); if(rule.element<0||rule.element>=mesh.GetNE()||rule.integration_rule==nullptr||rule.integration_rule->GetNPoints()<=0){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_configuration,.message="Ageometrysamplerulemustnameanexistingelementandcontainatleastonepoint.",.element=rule.element}); } const::mfem::ElementTransformation*transformation=mesh.GetElementTransformation(rule.element); const::mfem::FiniteElement*displacement_element=displacement_space.GetFE(rule.element); const::mfem::FiniteElement*compactification_element=compactification_space.GetFE(rule.element); constautosupports_mapping=[](const::mfem::FiniteElement*element){ returnelement!=nullptr&&element->GetDof()>0&&element->GetRangeType()==::mfem::FiniteElement::SCALAR&&element->GetMapType()==::mfem::FiniteElement::VALUE&&element->GetDerivType()==::mfem::FiniteElement::GRAD; }; if(transformation==nullptr||!supports_mapping(displacement_element)||!supports_mapping(compactification_element)||transformation->GetSpaceDim()!=mapper.get_dimension()||displacement_element->GetDim()!=mapper.get_dimension()||compactification_element->GetDim()!=mapper.get_dimension()||displacement_element->GetGeomType()!=compactification_element->GetGeomType()){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::incompatible_discretization,.message="Geometrysamplesrequirescalarvalue-mappedfiniteelementswithgradientsandcompatibledimensions.",.element=rule.element}); } if(mapper.is_compactified_element(*transformation)&&!mapper.get_exterior_map().is_affine_in_displacement()){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_configuration,.message="Thedeterminantpolynomialrequiresanexteriormapaffineindisplacementatfixedcompactificationcoordinates.",.element=rule.element}); } return{}; } [[nodiscard]]::mfem::Vectorlocal_displacement(const::mfem::FiniteElementSpace&space,const::mfem::Vector&true_values){ ::mfem::Vectorlocal(space.GetVSize()); const::mfem::Operator*prolongation=space.GetProlongationMatrix(); if(prolongation!=nullptr){ prolongation->Mult(true_values,local); }else{ local=true_values; } returnlocal; } [[nodiscard]]detail::JacobianEntriesjacobian_entries(const::mfem::DenseMatrix&matrix){ detail::JacobianEntriesentries{}; for(introw=0;row<matrix.Height();++row){ for(intcolumn=0;column<matrix.Width();++column){ entries[row][column]=matrix(row,column); } } returnentries; } [[nodiscard]]std::expected<detail::DeterminantPolynomial,DeformationFailure>sample_polynomial(constmapping::DomainMapper&mapper,constmapping::ElementMappingData&element_data,constmapping::ElementDisplacementData&direction_data,::mfem::ElementTransformation&transformation,const::mfem::IntegrationPoint&point,constmapping::DMFixedMeshGeometry*stored_geometry,mapping::DMWorkspace&workspace,mapping::MappingPointContext&context,mapping::MappingPointVariation&variation,constGeometryStepOptions&options){ if(!std::isfinite(point.x)||(mapper.get_dimension()>1&&!std::isfinite(point.y))||(mapper.get_dimension()>2&&!std::isfinite(point.z))){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="Geometrysamplecoordinatesmustbefinite."}); } //Bothmapsusethisexactpointandreferencemesh.Keepthecheckedsamplelocalso //resettingMFEM'sintegrationpointcannotforcerepeatedfixed-Jacobianevaluation. //Astoredsampleisthesamesample_fixed_mesh_geometryresultcomputedoncefortheunchanged //referencemesh;evaluatingthehigh-ordermeshJacobianheredominatedeveryNewtonpreflight. std::expected<mapping::DMFixedMeshGeometry,mapping::MappingStatus>sampled_geometry=std::unexpected(mapping::MappingStatus::VALID); if(stored_geometry==nullptr){ sampled_geometry=mapping::sample_fixed_mesh_geometry(transformation,point); } constmapping::DMFixedMeshGeometry*fixed_geometry=stored_geometry!=nullptr?stored_geometry:(sampled_geometry?&*sampled_geometry:nullptr); if(fixed_geometry==nullptr||mapper.evaluate_point(element_data,transformation,point,workspace,context,*fixed_geometry)!=mapping::MappingStatus::VALID||!std::isfinite(context.mapping_determinate)||context.mapping_determinate<=options.determinant_floor){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_geometry,.message="Theaccepteddisplacementmusthaveavalidmappingstrictlyabovethedeterminantfloorateverysample."}); } if(mapper.evaluate_point_variation(element_data,direction_data,transformation,point,context,workspace,variation,fixed_geometry->inverse_jacobian)!=mapping::MappingStatus::VALID||!utils::checks::is_finite(context.mapping_jacobian)||!utils::checks::is_finite(variation.mapping_jacobian_variation)){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_geometry,.message="Themappingvariationmustbefiniteateverygeometrysample."}); } detail::DeterminantPolynomialcoefficients=detail::determinant_polynomial(jacobian_entries(context.mapping_jacobian),jacobian_entries(variation.mapping_jacobian_variation),mapper.get_dimension(),options.maximum_step,options.determinant_floor); //Preservethemapper'sdeterminantratherthanintroducingasecondround-offpathattheacceptedstate. coefficients[0]=context.mapping_determinate-options.determinant_floor; for(constdoublecoefficient:coefficients){ if(!std::isfinite(coefficient)){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="Thedisplacementpathproducedanon-finitedeterminantpolynomial."}); } } if(!std::isfinite(detail::evaluate_polynomial(coefficients,1.0)+options.determinant_floor)){ returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="Thedeterminantatthemaximumdisplacementstepisnotfinite."}); } returncoefficients; } voidaccumulate_sample(GeometryStepLimit&result,detail::DeterminantPolynomial&limiting_coefficients,constdetail::DeterminantPolynomial&coefficients,constGeometryStepOptions&options,constintelement,constintrule,constintpoint){ ++result.sampled_point_count; result.minimum_determinant_at_accepted=std::min(result.minimum_determinant_at_accepted,coefficients[0]+options.determinant_floor); result.minimum_determinant_at_maximum=std::min(result.minimum_determinant_at_maximum,detail::evaluate_polynomial(coefficients,1.0)+options.determinant_floor); constdoubleboundary_parameter=detail::first_boundary_parameter(coefficients); constdoubleboundary_step=options.maximum_step*boundary_parameter; if(std::isfinite(boundary_step)&&(!result.limited_by_geometry||boundary_step<result.boundary_step)){ result.limited_by_geometry=true; result.boundary_step=boundary_step; result.limiting_element=element; result.limiting_rule=rule; result.limiting_point=point; limiting_coefficients=coefficients; } } structRuleSamples{ GeometryStepLimitlimit{}; detail::DeterminantPolynomiallimiting_coefficients{}; std::optional<DeformationFailure>failure; std::exception_ptrerror; }; voidmerge_rule(GeometryStepLimit&result,detail::DeterminantPolynomial&limiting_coefficients,constRuleSamples&rule){ result.sampled_point_count+=rule.limit.sampled_point_count; result.minimum_determinant_at_accepted=std::min(result.minimum_determinant_at_accepted,rule.limit.minimum_determinant_at_accepted); result.minimum_determinant_at_maximum=std::min(result.minimum_determinant_at_maximum,rule.limit.minimum_determinant_at_maximum); if(rule.limit.limited_by_geometry&&(!result.limited_by_geometry||rule.limit.boundary_step<result.boundary_step)){ result.limited_by_geometry=true; result.boundary_step=rule.limit.boundary_step; result.limiting_element=rule.limit.limiting_element; result.limiting_rule=rule.limit.limiting_rule; result.limiting_point=rule.limit.limiting_point; limiting_coefficients=rule.limiting_coefficients; } } structSampleWorkspace{ explicitSampleWorkspace(constintdimension):mapping(dimension){} mapping::DMWorkspacemapping; mapping::MappingPointContextcontext; mapping::MappingPointVariationvariation; }; } std::expected<GeometryStepLimit,DeformationFailure>estimate_geometry_step_limit(constmapping::DomainMapper&mapper,const::mfem::FiniteElementSpace&displacement_space,const::mfem::GridFunction&compactification_coordinate,const::mfem::Vector&accepted_displacement,const::mfem::Vector&displacement_direction,conststd::span<constGeometrySampleRule>sample_rules,constGeometryStepOptionsoptions){ if(constDeformationStatusvalidation=validate_inputs(mapper,displacement_space,compactification_coordinate,accepted_displacement,displacement_direction,sample_rules,options);!validation){ returnstd::unexpected(validation.error()); } const::mfem::FiniteElementSpace&compactification_space=*compactification_coordinate.FESpace(); for(constGeometrySampleRule&rule:sample_rules){ if(constDeformationStatusvalidation=validate_rule(mapper,displacement_space,compactification_space,rule);!validation){ returnstd::unexpected(validation.error()); } } const::mfem::Vectoraccepted_local=local_displacement(displacement_space,accepted_displacement); const::mfem::Vectordirection_local=local_displacement(displacement_space,displacement_direction); constintrule_count=static_cast<int>(sample_rules.size()); std::vector<RuleSamples>rules(sample_rules.size()); std::vector<SampleWorkspace>workspaces(static_cast<std::size_t>(serif::utils::parallel::worker_count()),SampleWorkspace(mapper.get_dimension())); //Rulesareindependent:eachoneissampledintoitsownslot,thenfoldedinruleorderbelow. serif::utils::parallel::for_each_index(rule_count,[&](constintrule_index,constintworker){ constGeometrySampleRule&rule=sample_rules[static_cast<std::size_t>(rule_index)]; RuleSamples&samples=rules[static_cast<std::size_t>(rule_index)]; SampleWorkspace&workspace=workspaces[static_cast<std::size_t>(worker)]; try{ //LocalMFEMstate:thespace'ssharedDofTransformationandthemesh'ssharedtransformationareneverwritten. ::mfem::IsoparametricTransformationtransformation; serif::utils::parallel::element_transformation(*displacement_space.GetMesh(),rule.element,transformation); ::mfem::Array<int>displacement_dofs; ::mfem::Array<int>compactification_dofs; ::mfem::DofTransformationdisplacement_transform; ::mfem::DofTransformationcompactification_transform; displacement_space.GetElementVDofs(rule.element,displacement_dofs,displacement_transform); compactification_space.GetElementDofs(rule.element,compactification_dofs,compactification_transform); ::mfem::Vectorelement_accepted; ::mfem::Vectorelement_direction; ::mfem::Vectorelement_compactification; accepted_local.GetSubVector(displacement_dofs,element_accepted); direction_local.GetSubVector(displacement_dofs,element_direction); compactification_coordinate.GetSubVector(compactification_dofs,element_compactification); if(displacement_transform.GetDofTransformation()!=nullptr){ displacement_transform.InvTransformPrimal(element_accepted); displacement_transform.InvTransformPrimal(element_direction); } if(compactification_transform.GetDofTransformation()!=nullptr){ compactification_transform.InvTransformPrimal(element_compactification); } //ElementVDofsarecomponent-blockedevenwhentheglobalspaceusesbyVDIMordering. constmapping::ElementDisplacementDataaccepted_data=mapping::element_displacement_data_from_element_vdofs(*displacement_space.GetFE(rule.element),element_accepted); constmapping::ElementDisplacementDatadirection_data=mapping::element_displacement_data_from_element_vdofs(*displacement_space.GetFE(rule.element),element_direction); constmapping::ElementCompactificationDatacompactification_data(*compactification_space.GetFE(rule.element),element_compactification); constmapping::ElementMappingDataelement_data{.displacement=accepted_data,.compactification=compactification_data}; //Usestoredreferencesamplesonlywhentheycoverexactlythisrule'spoints. constboolstored=rule.reference_geometry!=nullptr&& rule.reference_geometry->size()==static_cast<std::size_t>(rule.integration_rule->GetNPoints()); for(intpoint=0;point<rule.integration_rule->GetNPoints();++point){ constmapping::DMFixedMeshGeometry*stored_geometry=stored?&(*rule.reference_geometry)[static_cast<std::size_t>(point)]:nullptr; constautocoefficients=sample_polynomial(mapper,element_data,direction_data,transformation,rule.integration_rule->IntPoint(point),stored_geometry,workspace.mapping,workspace.context,workspace.variation,options); if(!coefficients){ DeformationFailurefailure=coefficients.error(); failure.element=rule.element; failure.point=point; samples.failure=std::move(failure); return; } accumulate_sample(samples.limit,samples.limiting_coefficients,*coefficients,options,rule.element,rule_index,point); } }catch(...){ samples.error=std::current_exception(); } }); GeometryStepLimitresult{.step=options.maximum_step,.boundary_step=options.maximum_step}; detail::DeterminantPolynomiallimiting_coefficients{}; for(constRuleSamples&samples:rules){ if(samples.error){ std::rethrow_exception(samples.error); } if(samples.failure){ returnstd::unexpected(*samples.failure); } merge_rule(result,limiting_coefficients,samples); } result.limiting_point_determinant_at_step=result.minimum_determinant_at_maximum; if(result.limited_by_geometry){ result.step=options.fraction_to_boundary*result.boundary_step; result.limiting_point_determinant_at_step=detail::evaluate_polynomial(limiting_coefficients,result.step/options.maximum_step)+options.determinant_floor; } returnresult; } }