operators.cpp serif/backend/mfem/solver/normalization/operators.hpp limits stdexcept utility serif/utils/error/errors.hpp serif serif::solver serif::solver::normalization serif::solver::normalization::backend serif::solver::normalization::backend::mfem serif::solver::normalization::backend::mfem::detail #include"serif/backend/mfem/solver/normalization/operators.hpp" #include<limits> #include<stdexcept> #include<utility> #include"serif/utils/error/errors.hpp" namespaceserif::solver::normalization::backend::mfem { namespacedetail { intmfem_size(conststd::size_tsize) { usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::INVALID_DOF_COUNT,std::invalid_argument>(size<=static_cast<std::size_t>(std::numeric_limits<int>::max()),"Normalizationhas{}coordinates,exceedingMFEM'smaximumindex{}.Useacoordinatelayoutsupportedbythisbackend.",size,std::numeric_limits<int>::max()); returnstatic_cast<int>(size); } voidscale_vector(std::span<constdouble>factors,const::mfem::Vector&input,::mfem::Vector&output,constboolinverse) { normalization::detail::require_normalization_size(static_cast<std::size_t>(input.Size()),factors.size()); output.SetSize(input.Size()); conststd::span<constdouble>source{input.HostRead(),factors.size()}; conststd::span<double>destination{output.HostWrite(),factors.size()}; normalization::detail::apply_normalization(factors,source,destination,inverse); } template<boolInverse> ScaledOperator<Inverse>::ScaledOperator(const::mfem::Operator&physical_operator,DiagonalNormalizationnormalization) :::mfem::Operator(mfem_size(Inverse?normalization.state_size():normalization.residual_size()),mfem_size(Inverse?normalization.residual_size():normalization.state_size())), m_physical_operator(&physical_operator),m_normalization(std::move(normalization)),m_physical_input(Width()),m_physical_output(Height()) { usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR,std::invalid_argument>(physical_operator.Width()==Width()&&physical_operator.Height()==Height(),"Thephysicaloperatorhasdimensions{}by{},butnormalizationrequires{}by{}.Preparefactorsusingtheoperator'sstateandresidualcoordinatelayout.",physical_operator.Height(),physical_operator.Width(),Height(),Width()); } template<boolInverse> voidScaledOperator<Inverse>::Mult(const::mfem::Vector&input,::mfem::Vector&output)const { scale_vector(input_factors(),input,m_physical_input,true); ifconstexpr(Inverse) { //Aniterativeinversemuststartfromzeroratherthanapreviousscratchresult. m_physical_output=0.0; } m_physical_operator->Mult(m_physical_input,m_physical_output); scale_vector(output_factors(),m_physical_output,output,false); } template<boolInverse> voidScaledOperator<Inverse>::MultTranspose(const::mfem::Vector&input,::mfem::Vector&output)const { scale_vector(output_factors(),input,m_physical_output,false); ifconstexpr(Inverse) { m_physical_input=0.0; } m_physical_operator->MultTranspose(m_physical_output,m_physical_input); scale_vector(input_factors(),m_physical_input,output,true); } templateclassScaledOperator<false>; templateclassScaledOperator<true>; } ScaledPreconditioner::ScaledPreconditioner(::mfem::Solver&physical_inverse,constScaledJacobianOperator&normalized_jacobian) :::mfem::Solver(normalized_jacobian.Width(),normalized_jacobian.Height(),false), m_physical_inverse(&physical_inverse),m_normalized_jacobian(&normalized_jacobian), m_scaled_inverse(bind_physical_inverse(physical_inverse,normalized_jacobian),normalized_jacobian.normalization()), m_statistics{.operator_bindings=1} { } voidScaledPreconditioner::SetOperator(const::mfem::Operator&normalized_jacobian) { usingnamespaceutils::errors; serif_ensure<SERiFErrorCode::NORMALIZATION_ERROR,std::invalid_argument>(&normalized_jacobian==m_normalized_jacobian,"ThescaledpreconditionerreceivedadifferentnormalizedJacobian.PrepareanewadapterforthatJacobiananditsfrozennormalization."); serif_ensure<SERiFErrorCode::MATRIX_SHAPE_ERROR,std::invalid_argument>(normalized_jacobian.Width()==Height()&&normalized_jacobian.Height()==Width(),"ThenormalizedJacobianhasdimensions{}by{},butthispreconditionerrequires{}by{}.Prepareanewadapterforitscoordinatelayout.",normalized_jacobian.Height(),normalized_jacobian.Width(),Width(),Height()); bind_physical_inverse(*m_physical_inverse,*m_normalized_jacobian); ++m_statistics.operator_bindings; } voidScaledPreconditioner::Mult(const::mfem::Vector&input,::mfem::Vector&output)const { normalization::detail::require_normalization_size(static_cast<std::size_t>(output.Size()),static_cast<std::size_t>(Height())); m_scaled_inverse.Mult(input,output); ++m_statistics.applications; } voidScaledPreconditioner::MultTranspose(const::mfem::Vector&input,::mfem::Vector&output)const { normalization::detail::require_normalization_size(static_cast<std::size_t>(output.Size()),static_cast<std::size_t>(Width())); m_scaled_inverse.MultTranspose(input,output); ++m_statistics.applications; } ::mfem::Solver&ScaledPreconditioner::bind_physical_inverse(::mfem::Solver&inverse,constScaledJacobianOperator&jacobian) { inverse.SetOperator(jacobian.physical_operator()); returninverse; } }