fitted_h1.cpp serif/solver/preconditioner/strategies/surface/fitted_h1.hpp algorithm array cmath limits serif serif::solver serif::solver::preconditioner serif::solver::preconditioner::strategies serif::solver::preconditioner::strategies::surface #include"serif/solver/preconditioner/strategies/surface/fitted_h1.hpp" #include<algorithm> #include<array> #include<cmath> #include<limits> namespaceserif::solver::preconditioner::strategies::surface { std::expected<SurfaceH1Fit,Failure>try_fit_surface_h1_coefficients(constSurfaceH1Moments&moments,FittedSurfaceH1options) { usingutils::errors::SERiFErrorCode; conststd::arrayvalues{moments.mass_mass,moments.mass_stiffness,moments.stiffness_stiffness,moments.mass_target,moments.stiffness_target,moments.target_target,options.relative_mass_coefficient_floor,options.gram_relative_tolerance}; constautofinite=[](doublevalue) { returnstd::isfinite(value); }; if(!std::ranges::all_of(values,finite)||moments.mass_mass<=0.0||moments.stiffness_stiffness<=0.0||moments.target_target<=0.0||options.relative_mass_coefficient_floor<=0.0||options.gram_relative_tolerance<=0.0||options.gram_relative_tolerance>=1.0) { returnstd::unexpected(Failure{.code=SERiFErrorCode::PRECONDITIONER_INVALID_CALIBRATION,.message="SurfaceH1calibrationrequiresfinitepositivemass,stiffness,andtargetenergies,apositivemassfloor,andaGramtolerancebetweenzeroandone."}); } constdoublemass_norm=std::sqrt(moments.mass_mass); constdoublestiffness_norm=std::sqrt(moments.stiffness_stiffness); constdoublecorrelation=moments.mass_stiffness/mass_norm/stiffness_norm; constdoublerelative_determinant=1.0-correlation*correlation; constdoublemass_target=moments.mass_target/mass_norm; constdoublestiffness_target=moments.stiffness_target/stiffness_norm; constdoubleminimum_mass=options.relative_mass_coefficient_floor*(std::sqrt(moments.target_target)/mass_norm); if(!std::isfinite(relative_determinant)||relative_determinant<=options.gram_relative_tolerance||!std::isfinite(mass_target)||!std::isfinite(stiffness_target)||!std::isfinite(minimum_mass)||minimum_mass<=0.0) { returnstd::unexpected(Failure{.code=SERiFErrorCode::PRECONDITIONER_INVALID_CALIBRATION,.message="ThesurfaceH1probescannotdistinguishfinitemassandstiffnessresponses.Increasetraceresolutionorchangethecalibrationtarget."}); } SurfaceH1Fitbest{.relative_residual=std::numeric_limits<double>::infinity(),.relative_gram_determinant=relative_determinant,.moments=moments}; doublebest_residual=std::numeric_limits<double>::infinity(); constautoconsider=[&](doublesign,doublemass,doublestiffness) { if(!std::isfinite(mass)||!std::isfinite(stiffness)||mass<minimum_mass||stiffness<0.0) { return; } constdoublenormalized_mass=mass*mass_norm; constdoublenormalized_stiffness=stiffness*stiffness_norm; constdoubleresidual=moments.target_target+normalized_mass*normalized_mass+2.0*correlation*normalized_mass*normalized_stiffness+normalized_stiffness*normalized_stiffness-2.0*sign*(normalized_mass*mass_target+normalized_stiffness*stiffness_target); if(std::isfinite(residual)&&std::max(0.0,residual)<best_residual) { best_residual=std::max(0.0,residual); best.sign=sign; best.mass_coefficient=mass; best.stiffness_coefficient=stiffness; } }; for(doublesign:{-1.0,1.0}) { consider(sign,sign*(mass_target-correlation*stiffness_target)/relative_determinant/mass_norm,sign*(stiffness_target-correlation*mass_target)/relative_determinant/stiffness_norm); consider(sign,std::max(minimum_mass,sign*mass_target/mass_norm),0.0); consider(sign,minimum_mass,std::max(0.0,(sign*stiffness_target-minimum_mass*mass_norm*correlation)/stiffness_norm)); } if(!std::isfinite(best_residual)) { returnstd::unexpected(Failure{.code=SERiFErrorCode::PRECONDITIONER_INVALID_CALIBRATION,.message="SurfaceH1calibrationhasnorepresentableconstrainedfit.Rescalethereferenceunits."}); } best.relative_residual=std::sqrt(best_residual/moments.target_target); returnbest; } }//namespaceserif::solver::preconditioner::strategies::surface