Files
2026-09-04 07:54:10 -04:00

237 lines
12 KiB
C++

#include <concepts>
#include <type_traits>
#include <catch2/catch_test_macros.hpp>
import mean_field;
import test_helpers;
namespace {
namespace blocks = mean_field::utils::blocks;
namespace preconditioning = mean_field::preconditioning;
using Form = blocks::surface_deformed_stellar_equilibrium_form;
using JacobianForm = blocks::surface_deformed_stellar_equilibrium_jacobian_form;
using CentralForm = blocks::central_density_bordered_stellar_equilibrium_form;
using CentralJacobianForm = blocks::central_density_bordered_stellar_equilibrium_jacobian_form;
using DensityIdentity =
preconditioning::IdentityBlock<blocks::density::mass::value, blocks::density::mass::residual>;
using SurfaceIdentity = preconditioning::IdentityBlock<
blocks::surface_deformation::parameters::value,
blocks::surface_deformation::shape_equilibrium::residual>;
using GravityGradientIdentity =
preconditioning::IdentityBlock<blocks::gravity::gradient::value, blocks::gravity::gradient::residual>;
using GravityPotentialIdentity =
preconditioning::IdentityBlock<blocks::gravity::poisson::value, blocks::gravity::poisson::residual>;
using EnthalpyIdentity =
preconditioning::IdentityBlock<blocks::enthalpy::specific::value, blocks::enthalpy::specific::residual>;
using MassIdentity = preconditioning::IdentityBlock<
blocks::fixed_total_mass::mass_normalization::value,
blocks::fixed_total_mass::mass_normalization::residual>;
using CentralDensityIdentity = preconditioning::IdentityBlock<
blocks::fixed_central_density::central_value::value,
blocks::fixed_central_density::central_value::residual>;
using IdentityPlan = preconditioning::PreconditionerPlan<
DensityIdentity,
SurfaceIdentity,
GravityGradientIdentity,
GravityPotentialIdentity,
EnthalpyIdentity,
MassIdentity>;
using CentralIdentityPlan = preconditioning::PreconditionerPlan<
DensityIdentity,
SurfaceIdentity,
GravityGradientIdentity,
GravityPotentialIdentity,
EnthalpyIdentity,
MassIdentity,
CentralDensityIdentity>;
using IncompleteCentralPlan = IdentityPlan;
struct AlternateDensityIdentity final : preconditioning::ComponentDeclaration<
blocks::type_list<blocks::density::mass::value>,
blocks::type_list<blocks::density::mass::residual>,
blocks::type_list<>,
preconditioning::IdentityOperatorCharacteristics,
preconditioning::backend::Identity> { };
using DuplicateOwnershipPlan = preconditioning::PreconditionerPlan<
DensityIdentity,
SurfaceIdentity,
GravityGradientIdentity,
GravityPotentialIdentity,
EnthalpyIdentity,
MassIdentity,
CentralDensityIdentity,
AlternateDensityIdentity>;
using ExplicitOverlapPlan = preconditioning::OverlappingPreconditionerPlan<
DensityIdentity,
SurfaceIdentity,
GravityGradientIdentity,
GravityPotentialIdentity,
EnthalpyIdentity,
MassIdentity,
CentralDensityIdentity,
AlternateDensityIdentity>;
struct ExtraCorrection final : blocks::value_block_base { };
struct ExtraResidual final : blocks::residual_block_base { };
using ExtraIdentity = preconditioning::IdentityBlock<ExtraCorrection, ExtraResidual>;
using UnexpectedOwnershipPlan = preconditioning::PreconditionerPlan<
DensityIdentity,
SurfaceIdentity,
GravityGradientIdentity,
GravityPotentialIdentity,
EnthalpyIdentity,
MassIdentity,
ExtraIdentity>;
using CoupledGravity = preconditioning::ComponentDeclaration<
blocks::type_list<blocks::gravity::gradient::value, blocks::gravity::poisson::value>,
blocks::type_list<blocks::gravity::gradient::residual, blocks::gravity::poisson::residual>,
blocks::type_list<
preconditioning::Coupling<blocks::gravity::gradient::residual, blocks::gravity::gradient::value>,
preconditioning::Coupling<blocks::gravity::gradient::residual, blocks::gravity::poisson::value>,
preconditioning::Coupling<blocks::gravity::poisson::residual, blocks::gravity::gradient::value>>,
preconditioning::IdentityOperatorCharacteristics,
preconditioning::backend::Identity>;
using ValidCoupledPlan = preconditioning::
PreconditionerPlan<DensityIdentity, SurfaceIdentity, CoupledGravity, EnthalpyIdentity, MassIdentity>;
using InvalidGravityCoupling = preconditioning::ComponentDeclaration<
blocks::type_list<blocks::gravity::gradient::value, blocks::gravity::poisson::value>,
blocks::type_list<blocks::gravity::gradient::residual, blocks::gravity::poisson::residual>,
blocks::type_list<preconditioning::Coupling<blocks::gravity::gradient::residual, blocks::density::mass::value>>,
preconditioning::IdentityOperatorCharacteristics,
preconditioning::backend::Identity>;
using InvalidCoupledPlan = preconditioning::
PreconditionerPlan<DensityIdentity, SurfaceIdentity, InvalidGravityCoupling, EnthalpyIdentity, MassIdentity>;
using IncompatibleBackendComponent = preconditioning::ComponentDeclaration<
blocks::type_list<blocks::gravity::gradient::value>,
blocks::type_list<blocks::gravity::gradient::residual>,
blocks::type_list<>,
preconditioning::OperatorCharacteristics<
preconditioning::OperatorCategory::elliptic_like,
preconditioning::OperatorValueStructure::vector,
preconditioning::OperatorSymmetry::symmetric,
preconditioning::OperatorDefiniteness::positive_definite,
preconditioning::OperatorRepresentation::assembled_sparse,
preconditioning::OperatorDistribution::distributed_true_dof,
preconditioning::OperatorFESpace::h_div>,
preconditioning::backend::HypreBoomerAMG<>>;
struct IncoherentPlanDeclaration final {
using ComponentTypes = blocks::type_list<DensityIdentity>;
using CorrectionBlocks = blocks::type_list<blocks::gravity::gradient::value>;
using ResidualBlocks = blocks::type_list<blocks::density::mass::residual>;
using RequiredCouplings = blocks::type_list<>;
};
} // namespace
TEST_CASE(
"Preconditioning Backends Advertise Compile-Time Operator Compatibility",
tags::preconditioning_type_contract
) {
using ScalarElliptic = preconditioning::OperatorCharacteristics<
preconditioning::OperatorCategory::elliptic_like, preconditioning::OperatorValueStructure::scalar,
preconditioning::OperatorSymmetry::symmetric, preconditioning::OperatorDefiniteness::positive_definite,
preconditioning::OperatorRepresentation::assembled_sparse,
preconditioning::OperatorDistribution::distributed_true_dof, preconditioning::OperatorFESpace::h1>;
using VectorElliptic = preconditioning::OperatorCharacteristics<
preconditioning::OperatorCategory::elliptic_like, preconditioning::OperatorValueStructure::vector,
preconditioning::OperatorSymmetry::symmetric, preconditioning::OperatorDefiniteness::positive_definite,
preconditioning::OperatorRepresentation::assembled_sparse,
preconditioning::OperatorDistribution::distributed_true_dof, preconditioning::OperatorFESpace::h_div>;
using LocalDenseBorder = preconditioning::OperatorCharacteristics<
preconditioning::OperatorCategory::dense_border, preconditioning::OperatorValueStructure::block,
preconditioning::OperatorSymmetry::nonsymmetric, preconditioning::OperatorDefiniteness::indefinite,
preconditioning::OperatorRepresentation::assembled_dense, preconditioning::OperatorDistribution::local>;
using FixedAMG = preconditioning::backend::HypreBoomerAMG<preconditioning::backend::FixedCycles>;
STATIC_CHECK(preconditioning::backend::Registered<FixedAMG>);
STATIC_CHECK(preconditioning::backend::Compatible<FixedAMG, ScalarElliptic>);
STATIC_CHECK_FALSE(preconditioning::backend::Compatible<FixedAMG, VectorElliptic>);
STATIC_CHECK(preconditioning::backend::Compatible<preconditioning::backend::DenseDirect, LocalDenseBorder>);
STATIC_CHECK_FALSE(preconditioning::PreconditionerComponent<IncompatibleBackendComponent>);
STATIC_CHECK(
preconditioning::backend::applicationContract<FixedAMG> ==
preconditioning::ApplicationContract::stationary_linear
);
}
TEST_CASE(
"Preconditioner Plans Prove Complete Unique Ownership Of Every Equilibrium Block",
tags::preconditioning_type_contract
) {
STATIC_CHECK(preconditioning::PreconditionerComponent<DensityIdentity>);
STATIC_CHECK(preconditioning::PreconditionerPlanType<IdentityPlan>);
STATIC_CHECK_FALSE(preconditioning::PreconditionerPlanType<IncoherentPlanDeclaration>);
STATIC_CHECK(preconditioning::CompletePreconditionerFor<IdentityPlan, Form>);
STATIC_CHECK(preconditioning::CompatiblePreconditionerFor<IdentityPlan, Form, JacobianForm>);
STATIC_CHECK(preconditioning::CompletePreconditionerFor<CentralIdentityPlan, CentralForm>);
STATIC_CHECK(preconditioning::CompatiblePreconditionerFor<CentralIdentityPlan, CentralForm, CentralJacobianForm>);
STATIC_CHECK(preconditioning::StationaryLinearPreconditionerPlan<CentralIdentityPlan>);
}
TEST_CASE(
"Preconditioner Coverage Reports Missing Generated Borders And Rejects Accidental Overlap",
tags::preconditioning_type_contract
) {
using IncompleteCoverage = preconditioning::PreconditionerCoverage<CentralForm, IncompleteCentralPlan>;
using DuplicateCoverage = preconditioning::PreconditionerCoverage<CentralForm, DuplicateOwnershipPlan>;
STATIC_CHECK_FALSE(preconditioning::CompletePreconditionerFor<IncompleteCentralPlan, CentralForm>);
STATIC_CHECK(IncompleteCoverage::MissingCorrectionBlocks::size == 1);
STATIC_CHECK(IncompleteCoverage::MissingResidualBlocks::size == 1);
STATIC_CHECK(
blocks::contains_type_v<
blocks::fixed_central_density::central_value::value, IncompleteCoverage::MissingCorrectionBlocks>
);
STATIC_CHECK(
blocks::contains_type_v<
blocks::fixed_central_density::central_value::residual, IncompleteCoverage::MissingResidualBlocks>
);
STATIC_CHECK_FALSE(preconditioning::CompletePreconditionerFor<DuplicateOwnershipPlan, CentralForm>);
STATIC_CHECK(DuplicateCoverage::RepeatedCorrectionBlocks::size == 1);
STATIC_CHECK(DuplicateCoverage::RepeatedResidualBlocks::size == 1);
STATIC_CHECK(preconditioning::CompletePreconditionerFor<ExplicitOverlapPlan, CentralForm>);
}
TEST_CASE(
"Preconditioner Coverage Rejects Blocks Outside The Compiled Stellar Form",
tags::preconditioning_type_contract
) {
using Coverage = preconditioning::PreconditionerCoverage<Form, UnexpectedOwnershipPlan>;
STATIC_CHECK_FALSE(preconditioning::CompletePreconditionerFor<UnexpectedOwnershipPlan, Form>);
STATIC_CHECK(Coverage::UnexpectedCorrectionBlocks::size == 1);
STATIC_CHECK(Coverage::UnexpectedResidualBlocks::size == 1);
STATIC_CHECK(blocks::contains_type_v<ExtraCorrection, Coverage::UnexpectedCorrectionBlocks>);
STATIC_CHECK(blocks::contains_type_v<ExtraResidual, Coverage::UnexpectedResidualBlocks>);
}
TEST_CASE(
"Preconditioner Component Dependencies Must Exist In The Compiled Jacobian Graph",
tags::preconditioning_type_contract
) {
STATIC_CHECK(preconditioning::PreconditionerComponent<CoupledGravity>);
STATIC_CHECK(preconditioning::CompletePreconditionerFor<ValidCoupledPlan, Form>);
STATIC_CHECK(preconditioning::CompatiblePreconditionerFor<ValidCoupledPlan, Form, JacobianForm>);
STATIC_CHECK(preconditioning::CompletePreconditionerFor<InvalidCoupledPlan, Form>);
STATIC_CHECK_FALSE(preconditioning::requiredCouplingsExist<InvalidCoupledPlan, JacobianForm>);
STATIC_CHECK_FALSE(preconditioning::CompatiblePreconditionerFor<InvalidCoupledPlan, Form, JacobianForm>);
}