operand_evaluation.hpp array concepts type_traits stdexcept utility mfem/fem/fe/fe_base.hpp mfem/fem/eltrans.hpp serif/operators/concepts.hpp serif/backend/mfem/discretization/basis_evaluation.hpp serif/backend/mfem/discretization/dof_map.hpp serif/backend/mfem/mapping/transformations.hpp serif/utils/error/errors.hpp serif/utils/misc/compensated_sum.hpp src/compile_time_checks/serif/operators/plan.cpp src/include/serif/backend/mfem/discretization/field_values.hpp src/include/serif/backend/mfem/operators/element_evaluation.hpp src/include/serif/backend/mfem/operators/element_preparation.hpp src/include/serif/backend/mfem/operators/geometry_derivatives.hpp serif::operators::backend::mfem::OperandEvaluator< OperandT > serif::operators::backend::mfem::BasisWorkspace serif::operators::backend::mfem::TensorBasisCache serif serif::operators serif::operators::backend serif::operators::backend::mfem #pragmaonce #include<array> #include<concepts> #include<type_traits> #include<stdexcept> #include<utility> #include"mfem/fem/fe/fe_base.hpp" #include"mfem/fem/eltrans.hpp" #include"serif/operators/concepts.hpp" #include"serif/backend/mfem/discretization/basis_evaluation.hpp" #include"serif/backend/mfem/discretization/dof_map.hpp" #include"serif/backend/mfem/mapping/transformations.hpp" #include"serif/utils/error/errors.hpp" #include"serif/utils/misc/compensated_sum.hpp" namespaceserif::operators::backend::mfem{ usingdiscretization::backend::mfem::BasisEvaluation; usingdiscretization::backend::mfem::BasisWorkspace; usingdiscretization::backend::mfem::ContractionMode; usingdiscretization::backend::mfem::ReferenceOperation; usingdiscretization::backend::mfem::TensorBasisCache; //Intentionallyundefined:anoperationtagaloneneverenablesevaluation. //Newoperationsmustsupplytheirownimplementationandlinearizationrules. template<typenameOperandT> structOperandEvaluator; template<MappedOperandOperandT> structOperandEvaluator<OperandT>final{ usingTerm=typenameOperandT::term_type; usingValue=discretization::forms::OperandValue<OperandT>; staticconstexprintcomponents=discretization::forms::component_count<Value>; staticconstexprboolglobal_scalar=discretization::blocks::GlobalScalarTerm<Term>; staticconstexprbooldivergence=std::same_as<typenameOperandT::operation_type,discretization::forms::Operation::Divergence>; staticconstexprboolvector_copies=[]{ ifconstexpr(global_scalar){ returnfalse; }else{ returndiscretization::blocks::SpatialTermTraits<Term>::rank==1; } }(); staticconstexprboolh1_divergence=vector_copies&&divergence; staticconstexprboolscalar_gradient=ScalarGradientOperand<OperandT>; staticconstexprboolvector_gradient=VectorGradientOperand<OperandT>; staticconstexprboolneeds_vector_gradient=h1_divergence||vector_gradient; staticconstexprboolgeometry_dependent=needs_vector_gradient||scalar_gradient; //H1divergenceandgradientretainG[c,k]=partial_X_k(u_c)incomponent-majororder. //Onlydivergencecontractsthemappedgradienttoascalar. usingReferenceValue=std::conditional_t<needs_vector_gradient,std::array<double,9>,Value>; staticconstexprintreference_components=needs_vector_gradient?9:components; staticvoidsample_basis( constdiscretization::backend::mfem::DofMap&binding, intelement, const::mfem::IntegrationRule&rule, BasisEvaluation&basis, TensorBasisCache&cache, ContractionModemode, ::mfem::ElementTransformation&transformation, BasisWorkspace&workspace ){ ifconstexpr(global_scalar){ basis.set_constant(rule.GetNPoints()); }else{ sample_basis(*binding.space().GetFE(element),rule,basis,cache,mode,transformation,workspace,binding.reconstruction_element(element)); } } staticvoidsample_basis( const::mfem::FiniteElement&element, const::mfem::IntegrationRule&rule, BasisEvaluation&basis, TensorBasisCache&cache, ContractionModemode, ::mfem::ElementTransformation&transformation, BasisWorkspace&workspace, constdiscretization::backend::mfem::ElementReconstruction*reconstruction=nullptr )requires(!global_scalar){ usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>( element.GetRangeType()==::mfem::FiniteElement::SCALAR&&element.GetMapType()==::mfem::FiniteElement::VALUE, "OperandsamplingrequiresscalarVALUEbases(oneorthreecopies)." ); constautooperation=(needs_vector_gradient||scalar_gradient)?ReferenceOperation::gradient:ReferenceOperation::value; BasisEvaluationcanonical; canonical.sample(element,rule,operation,vector_copies?3:1,cache,mode,reconstruction); ifconstexpr(!geometry_dependent){ basis=std::move(canonical); }else{ basis.reset_from(canonical,reference_components); for(intpoint=0;point<rule.GetNPoints();++point){ transformation.SetIntPoint(&rule.IntPoint(point)); basis.map_point(point,canonical,[&](const::mfem::Vector&input,::mfem::Vector&output){ ifconstexpr(needs_vector_gradient){ const::mfem::DenseMatrix&inverse=transformation.InverseJacobian(); output=0.0; for(intcomponent=0;component<3;++component){ for(intphysical=0;physical<3;++physical){ for(intreference=0;reference<3;++reference){ output[3*component+physical]+=input[3*component+reference]*inverse(reference,physical); } } } }else{ transformation.InverseJacobian().MultTranspose(input,output); } },workspace); } } } staticvoidmap_basis_point( intpoint, constmapping::MappingPointContext&context, constBasisEvaluation&reference, BasisEvaluation&result, BasisWorkspace&workspace, constmapping::MappingPointVariation*variation=nullptr ){ result.map_point(point,reference,[&](const::mfem::Vector&input,::mfem::Vector&output){ ReferenceValuevalue{}; for(intaxis=0;axis<reference_components;++axis){ discretization::forms::component(value,axis)=input[axis]; } constValuemapped=variation?geometry_variation(value,context,*variation):physical_value(value,context); for(intaxis=0;axis<components;++axis){ output[axis]=discretization::forms::component(mapped,axis); } },workspace); } /* *ForascalarVALUEoperand,thefollowingcontractionevaluatesascalar *finite-elementfieldatquadraturepoints.VectorH1andH(div)usethe *samematrix-vectorproductwithitscomponentcopiesorgradientbases. * *Ononeelement,thefieldisrepresentedby * *ρ_h(ξ)=∑_{j=1}^{n}c_jN_j(ξ), * *wherenisthenumberoflocalscalarDOFs,c_jisthecoefficient *associatedwithbasisfunctionN_j,andξisacoordinateinthe *canonicalreferenceelement. * *Therearetwocoordinatemapstodistinguish: * *ξ->X:MFEM'scanonicalelementtothefixedcomputationalmesh. *X->x:SERiF'sadditionalmappingtophysicalspace. * *Hereρ_h(ξ)denotesthescalarfieldpulledbacktothecanonical *element.ItsbasisvaluesusetheordinaryscalarVALUEmapping. * *sample_basisevaluateseachN_jateveryquadraturepointξ_q, *producingthebasisevaluationmatrix * *E_qj=N_j(ξ_q). * *ThephysicalEhasonerowperpointforscalarvaluesanddivergences, *andthreerowsperpointforvectorvalues.Beforetheadditionalmap, *H1divergenceretainsninegradiententriesperpoint.VectorH1hasthreeindependentcomponentcopiesofeachscalarbasis. * *element_valuesextractsthiselement'scoefficientsfromthesupported *fieldvector,includingtherequiredDOFsignandorientationhandling. *Wethensizetheoutputvectortothenumberofquadraturepoints. * *Finally,evaluateperformsthecontractionrepresentedbythematrix-vectorproduct * *operand_values=Ec, * *usingeitherthedensematrixorequivalenttensorcontractions, * *sothat * *operand_values[q]=∑_jN_j(ξ_q)c_j=ρ_h(ξ_q). * *Theexpansionabovewritesc_jN_j,whilethismatrixproductwrites *N_jc_j.Theseareproductsofindividualscalars,whoseordercanbe *exchanged.Despiteappearances(tomeatleast)wearenotexchangingamatrixandavector. */ staticvoidevaluate( constBasisEvaluation&basis, const::mfem::Vector&coefficients, ::mfem::Vector&values, BasisWorkspace&workspace ){ basis.evaluate(coefficients,values,workspace); } staticvoidintegrate( constBasisEvaluation&basis, const::mfem::Vector&values, ::mfem::Vector&result, BasisWorkspace&workspace ){ ifconstexpr(global_scalar){ utils::CompensatedSumsum; for(intpoint=0;point<values.Size();++point){ sum.add(values[point]); } result.SetSize(1); result[0]=sum.value(); }else{ basis.integrate(values,result,workspace); } } staticReferenceValuereference_value( const::mfem::Vector&values, intpoint ){ ReferenceValueresult{}; for(intaxis=0;axis<reference_components;++axis){ discretization::forms::component(result,axis)=values[point*reference_components+axis]; } returnresult; } staticValuephysical_value( constReferenceValue&reference, constmapping::MappingPointContext&context ){ ifconstexpr(h1_divergence){ returngradient_trace(reference,context.inverse_mapping_jacobian); }elseifconstexpr(vector_gradient){ returntransform_vector_gradient(reference,context.inverse_mapping_jacobian); }elseifconstexpr(scalar_gradient){ returntransform_gradient(reference,context.inverse_mapping_jacobian); }else{ returnreference; } } staticValuegeometry_variation( constReferenceValue&reference, constmapping::MappingPointContext&context, constmapping::MappingPointVariation&variation ){ ifconstexpr(h1_divergence){ returngradient_trace(reference,variation.inverse_mapping_jacobian_variation); }elseifconstexpr(vector_gradient){ returntransform_vector_gradient(reference,variation.inverse_mapping_jacobian_variation); }elseifconstexpr(scalar_gradient){ returntransform_gradient(reference,variation.inverse_mapping_jacobian_variation); }else{ return{}; } } private: staticValuetransform_vector_gradient(constReferenceValue&gradient,const::mfem::DenseMatrix&inverse)requiresvector_gradient{ Valueresult{}; for(intcomponent=0;component<3;++component){ for(intphysical=0;physical<3;++physical){ for(intreference=0;reference<3;++reference){ result[component][physical]+=gradient[3*component+reference]*inverse(reference,physical); } } } returnresult; } staticValuetransform_gradient( constReferenceValue&gradient, const::mfem::DenseMatrix&inverse )requiresscalar_gradient{ Valueresult{}; for(intphysical=0;physical<3;++physical){ for(intreference=0;reference<3;++reference){ result[physical]+=inverse(reference,physical)*gradient[reference]; } } returnresult; } staticdoublegradient_trace( constReferenceValue&gradient, const::mfem::DenseMatrix&inverse )requiresh1_divergence{ doubleresult=0.0; for(intcomponent=0;component<3;++component){ for(intderivative=0;derivative<3;++derivative){ result+=gradient[3*component+derivative]*inverse(derivative,component); } } returnresult; } }; template<typenameOperandT> conceptEvaluableOperand=requires( constdiscretization::backend::mfem::DofMap&binding, const::mfem::IntegrationRule&rule, ::mfem::ElementTransformation&transformation, constBasisEvaluation&reference_basis, BasisEvaluation&mapped_basis, TensorBasisCache&cache, BasisWorkspace&workspace, const::mfem::Vector&coefficients, ::mfem::Vector&values, consttypenameOperandEvaluator<OperandT>::ReferenceValue&reference_value, constmapping::MappingPointContext&context, constmapping::MappingPointVariation&variation ){ typenameOperandEvaluator<OperandT>::Value; typenameOperandEvaluator<OperandT>::ReferenceValue; {OperandEvaluator<OperandT>::components}->std::convertible_to<int>; {OperandEvaluator<OperandT>::reference_components}->std::convertible_to<int>; {OperandEvaluator<OperandT>::geometry_dependent}->std::convertible_to<bool>; {OperandEvaluator<OperandT>::sample_basis(binding,0,rule,mapped_basis,cache,ContractionMode::automatic,transformation,workspace)}->std::same_as<void>; {OperandEvaluator<OperandT>::evaluate(reference_basis,coefficients,values,workspace)}->std::same_as<void>; {OperandEvaluator<OperandT>::integrate(reference_basis,coefficients,values,workspace)}->std::same_as<void>; {OperandEvaluator<OperandT>::reference_value(coefficients,0)}->std::same_as<typenameOperandEvaluator<OperandT>::ReferenceValue>; {OperandEvaluator<OperandT>::physical_value(reference_value,context)}->std::same_as<typenameOperandEvaluator<OperandT>::Value>; {OperandEvaluator<OperandT>::geometry_variation(reference_value,context,variation)}->std::same_as<typenameOperandEvaluator<OperandT>::Value>; {OperandEvaluator<OperandT>::map_basis_point(0,context,reference_basis,mapped_basis,workspace)}->std::same_as<void>; {OperandEvaluator<OperandT>::map_basis_point(0,context,reference_basis,mapped_basis,workspace,&variation)}->std::same_as<void>; }; }//namespaceserif::operators::backend::mfem