reconstruction.cpp serif/backend/mfem/discretization/reconstruction.hpp algorithm array cmath map mfem/fem/fe/fe_h1.hpp serif/backend/mfem/discretization/boundary_faces.hpp serif/backend/mfem/discretization/element_coordinates.hpp serif serif::discretization serif::discretization::backend serif::discretization::backend::mfem #include"serif/backend/mfem/discretization/reconstruction.hpp" #include<algorithm> #include<array> #include<cmath> #include<map> #include"mfem/fem/fe/fe_h1.hpp" #include"serif/backend/mfem/discretization/boundary_faces.hpp" #include"serif/backend/mfem/discretization/element_coordinates.hpp" namespaceserif::discretization::backend::mfem{ namespace{ [[nodiscard]]doublecoordinate(const::mfem::IntegrationPoint&point,intaxis){ if(axis==0){returnpoint.x;} if(axis==1){returnpoint.y;} returnpoint.z; } [[nodiscard]]doubleinward(constElementReconstruction&reconstruction,const::mfem::IntegrationPoint&point){ constdoublevalue=coordinate(point,reconstruction.inward_axis); returnreconstruction.surface_at_high_coordinate?1.0-value:value; } } voidElementReconstruction::radial(const::mfem::FiniteElement&element,doubleposition, ::mfem::Vector&values,::mfem::Vector&derivatives)const{ usingnamespaceutils::errors; constauto*tensor=dynamic_cast<const::mfem::NodalTensorFiniteElement*>(&element); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(profile!=nullptr&&tensor!=nullptr&&inward_axis>=0&&inward_axis<3, "Radialreconstructionrequiresascalarnodaltensorelementandaninwardaxisin[0,2]."); constintcount=element.GetOrder()+1; constauto&ordering=tensor->GetDofMap(); std::vector<double>nodes(count),radial_values(count),radial_derivatives(count); for(intindex=0;index<count;++index){ constintcanonical=surface_at_high_coordinate?count-1-index:index; constintnative=ordering.Size()==0?canonical:ordering[canonical]; constdoublenode=element.GetNodes().IntPoint(native).x; nodes[index]=surface_at_high_coordinate?1.0-node:node; } constdoubleposition_inward=surface_at_high_coordinate?1.0-position:position; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(position_inward>=-5e-13&&position_inward<=1.0+5e-13, "Reconstructedfieldqueryliesoutsideitsreferenceelement(inwardcoordinate{}).",position_inward); //Curvedinversemapscanplaceanacceptedsurfacepointafewulpsoutsidethecube. profile->evaluate(nodes,std::clamp(position_inward,0.0,1.0),radial_values,radial_derivatives); values.SetSize(count); derivatives.SetSize(count); for(intindex=0;index<count;++index){ constintradial_index=surface_at_high_coordinate?count-1-index:index; values[index]=radial_values[radial_index]; derivatives[index]=surface_at_high_coordinate?-radial_derivatives[radial_index]:radial_derivatives[radial_index]; } } voidElementReconstruction::sample(const::mfem::FiniteElement&element,const::mfem::IntegrationPoint&point, ::mfem::Vector&values,::mfem::DenseMatrix&derivatives)const{ usingnamespaceutils::errors; constauto*tensor=dynamic_cast<const::mfem::NodalTensorFiniteElement*>(&element); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(tensor!=nullptr&&element.GetGeomType()==::mfem::Geometry::CUBE, "Reconstructedfieldevaluationrequiresanodalhexahedron."); constintcount=element.GetOrder()+1; std::array<::mfem::Vector,3>factors,slopes; for(intaxis=0;axis<3;++axis){ factors[axis].SetSize(count); slopes[axis].SetSize(count); if(axis==inward_axis){ radial(element,coordinate(point,axis),factors[axis],slopes[axis]); }else{ tensor->GetBasis1D().Eval(coordinate(point,axis),factors[axis],slopes[axis]); } } values.SetSize(element.GetDof()); derivatives.SetSize(element.GetDof(),3); constauto&ordering=tensor->GetDofMap(); for(intz=0;z<count;++z){ for(inty=0;y<count;++y){ for(intx=0;x<count;++x){ constintlexicographic=x+count*(y+count*z); constintnative=ordering.Size()==0?lexicographic:ordering[lexicographic]; values[native]=factors[0][x]*factors[1][y]*factors[2][z]; derivatives(native,0)=slopes[0][x]*factors[1][y]*factors[2][z]; derivatives(native,1)=factors[0][x]*slopes[1][y]*factors[2][z]; derivatives(native,2)=factors[0][x]*factors[1][y]*slopes[2][z]; } } } } ReconstructionPlan::ReconstructionPlan(constDofMap&binding,intboundary_attribute, std::shared_ptr<constreconstruction::RadialProfile>profile) :m_space(&binding.space()),m_space_sequence(binding.space().GetSequence()),m_mesh_sequence(binding.mesh().GetSequence()),m_boundary_attribute(boundary_attribute),m_profile(std::move(profile)),m_elements(binding.mesh().GetNE()){ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(m_profile!=nullptr&&binding.space().GetVDim()==1, "Aradialreconstructionrequiresaprofileandascalarfieldspace."); auto&mesh=binding.mesh(); for(intelement=0;element<mesh.GetNE();++element){ m_domain_attributes.push_back(mesh.GetAttribute(element)); m_element_support.push_back(binding.supports(element)); } for(intboundary=0;boundary<mesh.GetNBE();++boundary){m_boundary_attributes.push_back(mesh.GetBdrAttribute(boundary));} constauto&corners=*::mfem::Geometries.GetVertices(::mfem::Geometry::SQUARE); intselected=0; for(constintface:boundary_leaf_faces(mesh,boundary_attribute)){ auto*transformations=mesh.GetFaceElementTransformations(face); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(transformations!=nullptr, "Cannotinspectface{}whileselectingreconstructedsurfaceelements.",face); constboolfirst=transformations->Elem1No>=0&&binding.supports(transformations->Elem1No); constboolsecond=transformations->Elem2No>=0&&binding.supports(transformations->Elem2No); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(first!=second, "Reconstructionboundary{}musthaveexactlyonesupportedmaterialside;face{}doesnot.Selectthestellarmaterial/vacuuminterface.",boundary_attribute,face); constintelement_index=first?transformations->Elem1No:transformations->Elem2No; constauto*finite_element=binding.space().GetFE(element_index); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(dynamic_cast<const::mfem::H1_HexahedronElement*>(finite_element)!=nullptr, "SurfacereconstructioncurrentlyrequiresstandardH1hexahedra;element{}isincompatible.",element_index); auto&local=first?transformations->Loc1:transformations->Loc2; intnormal_axis=-1; boolhigh=false; for(intaxis=0;axis<3;++axis){ doubleminimum=1.0,maximum=0.0; for(intcorner=0;corner<corners.GetNPoints();++corner){ ::mfem::IntegrationPointpoint; local.Transform(corners.IntPoint(corner),point); minimum=std::min(minimum,coordinate(point,axis)); maximum=std::max(maximum,coordinate(point,axis)); } if(maximum<1e-12||minimum>1.0-1e-12){ normal_axis=axis; high=minimum>0.5; } } serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(normal_axis>=0, "Surfaceface{}isnotacanonicalouterfaceofelement{};reconstructionneedsanalignedsurfacelayer.",face,element_index); ElementReconstructiondescription{.profile=m_profile.get(),.inward_axis=normal_axis,.surface_at_high_coordinate=high}; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(m_elements[element_index].profile==nullptr||m_elements[element_index]==description, "Element{}touchestheselectedsurfaceindifferentinwarddirections.Refineorchangethesurfacelayerbeforeusingradialreconstruction.",element_index); if(m_elements[element_index].profile==nullptr){++selected;} m_elements[element_index]=description; } serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(selected>0, "Reconstructionboundaryattribute{}selectsnomaterialelements.Checktheboundarytypeandmeshlabels.",boundary_attribute); validate_interfaces(binding); } voidReconstructionPlan::validate_binding(constDofMap&binding)const{ usingnamespaceutils::errors; constauto&space=binding.space(); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(&space==m_space, "Rebuildthereconstructionplanforthisfield'sfinite-elementspace;aselectioncannotbereusedonanothermeshorspace."); constauto&mesh=*space.GetMesh(); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(space.GetSequence()==m_space_sequence&&mesh.GetSequence()==m_mesh_sequence&& mesh.GetNE()==static_cast<int>(m_domain_attributes.size())&&mesh.GetNBE()==static_cast<int>(m_boundary_attributes.size()), "Rebuildthereconstructionplanafterchangingthemeshorfinite-elementspace."); for(intelement=0;element<mesh.GetNE();++element){ serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(mesh.GetAttribute(element)==m_domain_attributes[element]&&binding.supports(element)==m_element_support[element], "Rebuildthereconstructionplanafterchangingmaterialdomainlabelsorfieldsupport."); } for(intboundary=0;boundary<mesh.GetNBE();++boundary){ serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(mesh.GetBdrAttribute(boundary)==m_boundary_attributes[boundary], "Rebuildthereconstructionplanafterchangingsurfaceboundarylabels."); } } constElementReconstruction*ReconstructionPlan::element(intindex)const{ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(index>=0&&index<static_cast<int>(m_elements.size()), "Reconstructionelement{}liesoutsidethepreparedmeshof{}elements.",index,m_elements.size()); if(m_elements[index].profile==nullptr){returnnullptr;} return&m_elements[index]; } voidReconstructionPlan::validate_interfaces(constDofMap&binding)const{ usingnamespaceutils::errors; auto&mesh=binding.mesh(); constauto&corners=*::mfem::Geometries.GetVertices(::mfem::Geometry::SQUARE); constautoelements=binding.validated_elements(); for(intface=0;face<mesh.GetNumFaces();++face){ if(mesh.GetFaceInformation(face).tag==::mfem::Mesh::FaceInfoTag::MasterNonconforming){continue;} auto*transformations=mesh.GetFaceElementTransformations(face); if(transformations==nullptr||transformations->Elem1No<0||transformations->Elem2No<0){continue;} constintleft=transformations->Elem1No,right=transformations->Elem2No; if(!binding.supports(left)||!binding.supports(right)){continue;} constauto*left_profile=element(left); constauto*right_profile=element(right); if(left_profile==nullptr&&right_profile==nullptr){continue;} //Equalinwardcoordinatesonalateralfacegivethesameweightedtracespace. //Theonlysupportedtransitiontoanordinarycellistheinnerface,wheret=1. for(intcorner=0;corner<corners.GetNPoints();++corner){ ::mfem::IntegrationPointleft_point,right_point; transformations->Loc1.Transform(corners.IntPoint(corner),left_point); transformations->Loc2.Transform(corners.IntPoint(corner),right_point); constdoubleleft_depth=left_profile==nullptr?1.0:inward(*left_profile,left_point); constdoubleright_depth=right_profile==nullptr?1.0:inward(*right_profile,right_point); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(std::abs(left_depth-right_depth)<2e-12, "Reconstructionisincompatibleacrossface{}betweenelements{}and{}:inwardcoordinates{}and{}disagree.Usematchingradiallayers;independentradialhanginginterfacesarenotsupported.", face,left,right,left_depth,right_depth); } constauto&left_element=*binding.space().GetFE(left); constauto&right_element=*binding.space().GetFE(right); constautoleft_coordinates=elements.coordinates(left); constautoright_coordinates=elements.coordinates(right); constauto&rule=::mfem::IntRules.Get(::mfem::Geometry::SQUARE,2*std::max(left_element.GetOrder(),right_element.GetOrder())+4); ::mfem::Vectorleft_values,right_values; ::mfem::DenseMatrixderivatives; for(intsample=0;sample<rule.GetNPoints();++sample){ ::mfem::IntegrationPointleft_point,right_point; transformations->Loc1.Transform(rule.IntPoint(sample),left_point); transformations->Loc2.Transform(rule.IntPoint(sample),right_point); if(left_profile!=nullptr){left_profile->sample(left_element,left_point,left_values,derivatives);} else{left_values.SetSize(left_element.GetDof());left_element.CalcShape(left_point,left_values);} if(right_profile!=nullptr){right_profile->sample(right_element,right_point,right_values,derivatives);} else{right_values.SetSize(right_element.GetDof());right_element.CalcShape(right_point,right_values);} std::map<int,double>difference; for(intlocal=0;local<left_values.Size();++local){ left_coordinates.for_each_coefficient(local,[&](intcoefficient,doubleweight){difference[coefficient]+=weight*left_values[local];}); } for(intlocal=0;local<right_values.Size();++local){ right_coordinates.for_each_coefficient(local,[&](intcoefficient,doubleweight){difference[coefficient]-=weight*right_values[local];}); } for(constauto&[coefficient,mismatch]:difference){ serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(std::abs(mismatch)<2e-11, "Hanging-nodeconstraintsdonotpreservereconstructeddensityacrossface{}(coefficient{},mismatch{}).Usematchingradiallayersoracompatiblerefinementpattern.",face,coefficient,mismatch); } } } } }