feat(surface): surface deformation prescriptions
restricted the unknown state vector to surface deformation and implemented one prescription, NodalRadialSurface, while the full volumetric displacment field is reconstructed analytically from that. This reduced the number of degrees of freedom in the system by a factor of 80 while also removing many null vectors from the system.
This commit is contained in:
@@ -388,21 +388,21 @@ namespace stellar_equilibrium_test_utils {
|
||||
|
||||
[[nodiscard]] mean_field::operators::StellarEquilibriumDependencies make_dependencies() {
|
||||
return {
|
||||
.discretization = {.identity = 1009, .revision = 3},
|
||||
.density = {.identity = 1013, .revision = 5},
|
||||
.displacement = {.identity = 1019, .revision = 7},
|
||||
.gravityGradient = {.identity = 1021, .revision = 11},
|
||||
.gravityPotential = {.identity = 1031, .revision = 13},
|
||||
.enthalpy = {.identity = 1033, .revision = 17},
|
||||
.bernoulliConstant = {.identity = 1039, .revision = 19},
|
||||
.rotation = {.identity = 1049, .revision = 23},
|
||||
.targetMass = {.identity = 1051, .revision = 29}
|
||||
.discretization = {.identity = 1009, .revision = 3},
|
||||
.density = {.identity = 1013, .revision = 5},
|
||||
.surfaceDeformation = {.identity = 1019, .revision = 7},
|
||||
.gravityGradient = {.identity = 1021, .revision = 11},
|
||||
.gravityPotential = {.identity = 1031, .revision = 13},
|
||||
.enthalpy = {.identity = 1033, .revision = 17},
|
||||
.bernoulliConstant = {.identity = 1039, .revision = 19},
|
||||
.rotation = {.identity = 1049, .revision = 23},
|
||||
.targetMass = {.identity = 1051, .revision = 29}
|
||||
};
|
||||
}
|
||||
|
||||
void increment_all_state_revisions(mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||
++dependencies.density.revision;
|
||||
++dependencies.displacement.revision;
|
||||
++dependencies.surfaceDeformation.revision;
|
||||
++dependencies.gravityGradient.revision;
|
||||
++dependencies.gravityPotential.revision;
|
||||
++dependencies.enthalpy.revision;
|
||||
@@ -424,7 +424,9 @@ namespace stellar_equilibrium_test_utils {
|
||||
assign_value_block(state, layout, densityValue, reducedDensity);
|
||||
}
|
||||
|
||||
assign_value_block(state, layout, displacementValue, project_displacement(f, 0.63));
|
||||
mfem::Vector surfaceDeformation(layout.size(displacementValue));
|
||||
surfaceDeformation = 0.0;
|
||||
assign_value_block(state, layout, displacementValue, surfaceDeformation);
|
||||
assign_value_block(state, layout, gravityGradientValue, project_gravity_gradient(f, 0.43));
|
||||
assign_value_block(state, layout, gravityPotentialValue, project_gravity_potential(f, 0.47));
|
||||
|
||||
@@ -454,7 +456,11 @@ namespace stellar_equilibrium_test_utils {
|
||||
assign_value_block(direction, layout, densityValue, reducedDensityDirection);
|
||||
}
|
||||
|
||||
assign_value_block(direction, layout, displacementValue, project_displacement_direction(f, 0.79));
|
||||
mfem::Vector surfaceDeformationDirection(layout.size(displacementValue));
|
||||
for (int parameter = 0; parameter < surfaceDeformationDirection.Size(); ++parameter) {
|
||||
surfaceDeformationDirection(parameter) = 0.11 * std::cos(0.41 * static_cast<double>(parameter) + 0.79);
|
||||
}
|
||||
assign_value_block(direction, layout, displacementValue, surfaceDeformationDirection);
|
||||
assign_value_block(direction, layout, gravityGradientValue, project_gravity_direction(f, 0.83));
|
||||
assign_value_block(direction, layout, gravityPotentialValue, project_potential_direction(f, 0.89));
|
||||
|
||||
@@ -497,7 +503,7 @@ namespace stellar_equilibrium_test_utils {
|
||||
) {
|
||||
const mean_field::operators::StellarEquilibriumLayout &layout = stellarOperator.GetLayout();
|
||||
const mfem::Vector reducedDensity = const_value_view(state, layout, densityValue);
|
||||
const mfem::Vector displacement = const_value_view(state, layout, displacementValue);
|
||||
const mfem::Vector &displacement = stellarOperator.GetGeneratedVolumeDisplacement();
|
||||
const mfem::Vector gravityGradient = const_value_view(state, layout, gravityGradientValue);
|
||||
const mfem::Vector gravityPotential = const_value_view(state, layout, gravityPotentialValue);
|
||||
|
||||
@@ -513,7 +519,10 @@ namespace stellar_equilibrium_test_utils {
|
||||
stellarOperator.GetGravityOperator().Mult(gravityState, gravity);
|
||||
stellarOperator.GetBarotropicClosureOperator().BuildResidual(closure);
|
||||
stellarOperator.GetDisplacementOperator().BuildResidual(displacementResidualValue);
|
||||
stellarOperator.GetCenteringConstraintOperator().ApplyResidualRows(displacementResidualValue);
|
||||
mfem::Vector surfaceShapeResidualValue(stellarOperator.GetDomainDeformation().parameterCount());
|
||||
stellarOperator.GetDomainDeformation().applyJacobianTranspose(
|
||||
stellarOperator.GetSurfaceDeformationParameters(), displacementResidualValue, surfaceShapeResidualValue
|
||||
);
|
||||
stellarOperator.GetHydrostaticOperator().BuildResidual(hydrostatic);
|
||||
stellarOperator.GetSurfaceConstraintOperator().ApplyResidualRows(hydrostatic);
|
||||
stellarOperator.GetMassNormalizationOperator().BuildResidual(mass);
|
||||
@@ -536,7 +545,7 @@ namespace stellar_equilibrium_test_utils {
|
||||
|
||||
residual_view(result, layout, densityResidual) = closure;
|
||||
|
||||
residual_view(result, layout, displacementResidual) = displacementResidualValue;
|
||||
residual_view(result, layout, displacementResidual) = surfaceShapeResidualValue;
|
||||
|
||||
residual_view(result, layout, enthalpyResidual) = hydrostatic;
|
||||
|
||||
@@ -551,8 +560,12 @@ namespace stellar_equilibrium_test_utils {
|
||||
) {
|
||||
const mean_field::operators::StellarEquilibriumLayout &layout = stellarOperator.GetLayout();
|
||||
|
||||
const mfem::Vector reducedDensityDirection = const_value_view(direction, layout, densityValue);
|
||||
const mfem::Vector displacementDirection = const_value_view(direction, layout, displacementValue);
|
||||
const mfem::Vector reducedDensityDirection = const_value_view(direction, layout, densityValue);
|
||||
const mfem::Vector surfaceDeformationDirection = const_value_view(direction, layout, displacementValue);
|
||||
mfem::Vector displacementDirection(stellarOperator.GetDomainDeformation().volumeDisplacementSize());
|
||||
stellarOperator.GetDomainDeformation().applyJacobian(
|
||||
stellarOperator.GetSurfaceDeformationParameters(), surfaceDeformationDirection, displacementDirection
|
||||
);
|
||||
const mfem::Vector gravityGradientDirection = const_value_view(direction, layout, gravityGradientValue);
|
||||
const mfem::Vector gravityPotentialDirection = const_value_view(direction, layout, gravityPotentialValue);
|
||||
const mfem::Vector reducedEnthalpyDirection = const_value_view(direction, layout, enthalpyValue);
|
||||
@@ -578,7 +591,16 @@ namespace stellar_equilibrium_test_utils {
|
||||
reducedDensityDirection, displacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
|
||||
displacementAction
|
||||
);
|
||||
stellarOperator.GetCenteringConstraintOperator().ApplyJacobianRows(displacementDirection, displacementAction);
|
||||
mfem::Vector surfaceShapeAction(stellarOperator.GetDomainDeformation().parameterCount());
|
||||
stellarOperator.GetDomainDeformation().applyJacobianTranspose(
|
||||
stellarOperator.GetSurfaceDeformationParameters(), displacementAction, surfaceShapeAction
|
||||
);
|
||||
mfem::Vector pullbackDerivative(stellarOperator.GetDomainDeformation().parameterCount());
|
||||
stellarOperator.GetDomainDeformation().applyPullbackDerivative(
|
||||
stellarOperator.GetSurfaceDeformationParameters(), surfaceDeformationDirection,
|
||||
stellarOperator.GetFullMechanicalResidual(), pullbackDerivative
|
||||
);
|
||||
surfaceShapeAction += pullbackDerivative;
|
||||
|
||||
stellarOperator.GetHydrostaticOperator().ApplyCompleteJacobianAction(
|
||||
reducedEnthalpyDirection, gravityPotentialDirection, bernoulliDirection(0), displacementDirection,
|
||||
@@ -608,7 +630,7 @@ namespace stellar_equilibrium_test_utils {
|
||||
|
||||
residual_view(result, layout, densityResidual) = closureAction;
|
||||
|
||||
residual_view(result, layout, displacementResidual) = displacementAction;
|
||||
residual_view(result, layout, displacementResidual) = surfaceShapeAction;
|
||||
|
||||
residual_view(result, layout, enthalpyResidual) = hydrostaticAction;
|
||||
|
||||
@@ -644,8 +666,8 @@ namespace stellar_equilibrium_test_utils {
|
||||
} // namespace stellar_equilibrium_test_utils
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Stellar Equilibrium Uses Supported Field DOFs For Solver Blocks",
|
||||
tags::barotrope &tags::prepared &tags::field &tags::unit
|
||||
"Prepared Stellar Equilibrium Uses Surface Parameters For Its Root Geometry Block",
|
||||
tags::reduced_stellar_geometry &tags::prepared &tags::field &tags::unit
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
@@ -660,12 +682,22 @@ TEST_CASE(
|
||||
);
|
||||
|
||||
CHECK(stellarOperator.GetTargetMass() == stellarModel.targetMass());
|
||||
CHECK(stellarOperator.GetDomainDeformation().matchesCurrentDiscretization());
|
||||
const mean_field::field::ScalarBoundaryDofMap surfaceDeformationMap =
|
||||
mean_field::field::make_stellar_surface_scalar_dof_map<stellar_equilibrium_test_utils::DomainSchema>(
|
||||
*f.surfaceDeformationFes
|
||||
);
|
||||
CHECK(stellarOperator.GetDomainDeformation().parameterCount() == surfaceDeformationMap.local_size());
|
||||
CHECK(stellarOperator.GetDomainDeformation().volumeDisplacementSize() == f.displacementFes->GetTrueVSize());
|
||||
|
||||
const auto &layout = stellarOperator.GetLayout();
|
||||
const stellar_equilibrium_test_utils::FieldMaps maps(f);
|
||||
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::densityValue) == maps.density.reduced_size());
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::displacementValue) == maps.displacement.reduced_size());
|
||||
CHECK(
|
||||
layout.size(stellar_equilibrium_test_utils::displacementValue) ==
|
||||
stellarOperator.GetDomainDeformation().parameterCount()
|
||||
);
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::gravityGradientValue) == maps.gravityFlux.reduced_size());
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::gravityPotentialValue) == maps.gravityPotential.reduced_size());
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::enthalpyValue) == maps.enthalpy.reduced_size());
|
||||
@@ -676,7 +708,10 @@ TEST_CASE(
|
||||
layout.size(stellar_equilibrium_test_utils::gravityPotentialResidual) == maps.gravityPotential.reduced_size()
|
||||
);
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::densityResidual) == maps.density.reduced_size());
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::displacementResidual) == maps.displacement.reduced_size());
|
||||
CHECK(
|
||||
layout.size(stellar_equilibrium_test_utils::displacementResidual) ==
|
||||
stellarOperator.GetDomainDeformation().parameterCount()
|
||||
);
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::enthalpyResidual) == maps.enthalpy.reduced_size());
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::massResidual) == 1);
|
||||
|
||||
@@ -715,9 +750,8 @@ TEST_CASE(
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Stellar Equilibrium Jacobian Is The Exact Restricted Full "
|
||||
"Child Jacobian",
|
||||
tags::barotrope_prepared_jacobian_accuracy &tags::field
|
||||
"Prepared Stellar Equilibrium Jacobian Is The Lifted And Pulled Back Full Child Jacobian",
|
||||
tags::barotrope_prepared_jacobian_accuracy &tags::reduced_stellar_geometry &tags::field
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
@@ -834,8 +868,8 @@ TEST_CASE(
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Stellar Equilibrium Replaces Center Force Rows With A Translational Centering Constraint",
|
||||
tags::translational_centering_enforcement
|
||||
"Reduced Stellar Geometry Uses Surface Parameters And Generates An Orientation Preserving Volume Map",
|
||||
tags::reduced_stellar_geometry &tags::prepared
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
@@ -847,75 +881,25 @@ TEST_CASE(
|
||||
f, *f.domainMapperStateless, stellarModel
|
||||
);
|
||||
|
||||
const auto &layout = stellarOperator.GetLayout();
|
||||
mfem::Vector state = stellar_equilibrium_test_utils::make_state(f, layout);
|
||||
const auto &layout = stellarOperator.GetLayout();
|
||||
const mfem::Vector state = stellar_equilibrium_test_utils::make_state(f, layout);
|
||||
|
||||
mfem::Vector translation(f.mesh->SpaceDimension());
|
||||
translation(0) = 0.17;
|
||||
translation(1) = -0.11;
|
||||
translation(2) = 0.08;
|
||||
mfem::VectorConstantCoefficient translationCoefficient(translation);
|
||||
mfem::ParGridFunction translationField(f.displacementFes.get());
|
||||
translationField.ProjectCoefficient(translationCoefficient);
|
||||
mfem::Vector translationTrue;
|
||||
translationField.GetTrueDofs(translationTrue);
|
||||
stellar_equilibrium_test_utils::assign_value_block(
|
||||
state, layout, stellar_equilibrium_test_utils::displacementValue, translationTrue
|
||||
);
|
||||
|
||||
const auto report = stellarOperator.Prepare(
|
||||
const auto report = stellarOperator.Prepare(
|
||||
state, stellar_equilibrium_test_utils::make_dependencies(), stellar_equilibrium_test_utils::make_zero_rotation()
|
||||
);
|
||||
CHECK(report.centeringConstraint.cachedCenterDisplacement);
|
||||
|
||||
const auto ¢erRows = stellarOperator.GetCenteringConstraintOperator().GetCenterRows();
|
||||
CHECK(stellar_equilibrium_test_utils::global_sum(centerRows.size(), f.mesh->GetComm()) == f.mesh->SpaceDimension());
|
||||
const int surfaceParameterCount = stellarOperator.GetDomainDeformation().parameterCount();
|
||||
const int volumeDisplacementSize = stellarOperator.GetDomainDeformation().volumeDisplacementSize();
|
||||
|
||||
mfem::Vector residual;
|
||||
stellarOperator.BuildResidual(residual);
|
||||
const mfem::Vector displacementState = stellar_equilibrium_test_utils::const_value_view(
|
||||
state, layout, stellar_equilibrium_test_utils::displacementValue
|
||||
);
|
||||
const mfem::Vector displacementResidual = stellar_equilibrium_test_utils::const_residual_view(
|
||||
residual, layout, stellar_equilibrium_test_utils::displacementResidual
|
||||
);
|
||||
for (const int centerRow : centerRows.reduced_dofs()) {
|
||||
CAPTURE(centerRow);
|
||||
CHECK(displacementResidual(centerRow) == displacementState(centerRow));
|
||||
}
|
||||
|
||||
mfem::Vector translationDirection(layout.value_offsets().Last());
|
||||
translationDirection = 0.0;
|
||||
stellar_equilibrium_test_utils::assign_value_block(
|
||||
translationDirection, layout, stellar_equilibrium_test_utils::displacementValue, translationTrue
|
||||
);
|
||||
|
||||
mfem::Vector action;
|
||||
stellarOperator.Mult(translationDirection, action);
|
||||
const mfem::Vector displacementDirection = stellar_equilibrium_test_utils::const_value_view(
|
||||
translationDirection, layout, stellar_equilibrium_test_utils::displacementValue
|
||||
);
|
||||
const mfem::Vector displacementAction = stellar_equilibrium_test_utils::const_residual_view(
|
||||
action, layout, stellar_equilibrium_test_utils::displacementResidual
|
||||
);
|
||||
for (const int centerRow : centerRows.reduced_dofs()) {
|
||||
CAPTURE(centerRow);
|
||||
CHECK(displacementAction(centerRow) == displacementDirection(centerRow));
|
||||
}
|
||||
|
||||
mfem::Vector nonDisplacementDirection = stellar_equilibrium_test_utils::make_direction(f, layout);
|
||||
stellar_equilibrium_test_utils::value_view(
|
||||
nonDisplacementDirection, layout, stellar_equilibrium_test_utils::displacementValue
|
||||
) = 0.0;
|
||||
stellarOperator.Mult(nonDisplacementDirection, action);
|
||||
|
||||
const mfem::Vector nonDisplacementAction = stellar_equilibrium_test_utils::const_residual_view(
|
||||
action, layout, stellar_equilibrium_test_utils::displacementResidual
|
||||
);
|
||||
for (const int centerRow : centerRows.reduced_dofs()) {
|
||||
CAPTURE(centerRow);
|
||||
CHECK(nonDisplacementAction(centerRow) == 0.0);
|
||||
}
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::displacementValue) == surfaceParameterCount);
|
||||
CHECK(layout.size(stellar_equilibrium_test_utils::displacementResidual) == surfaceParameterCount);
|
||||
CHECK(surfaceParameterCount < volumeDisplacementSize);
|
||||
CHECK(stellarOperator.GetSurfaceDeformationParameters().Size() == surfaceParameterCount);
|
||||
CHECK(stellarOperator.GetGeneratedVolumeDisplacement().Size() == volumeDisplacementSize);
|
||||
CHECK(report.generatedVolumeDisplacement);
|
||||
CHECK(report.generatedGeometry.isOrientationPreserving());
|
||||
CHECK(report.generatedDisplacement.identity != 0);
|
||||
CHECK(report.generatedDisplacement.revision == 1);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -949,7 +933,7 @@ TEST_CASE(
|
||||
CHECK(report.displacement.DidAnyWork());
|
||||
CHECK(report.massNormalization.DidAnyWork());
|
||||
CHECK(report.surfaceConstraint.DidAnyWork());
|
||||
CHECK(report.centeringConstraint.DidAnyWork());
|
||||
CHECK(report.generatedVolumeDisplacement);
|
||||
CHECK(report.assembledResidual);
|
||||
CHECK(stellarOperator.IsPrepared());
|
||||
|
||||
@@ -1227,7 +1211,7 @@ TEST_CASE(
|
||||
TEST_CASE(
|
||||
"Prepared Stellar Equilibrium Complete Jacobian Matches Every "
|
||||
"Coupled Centered Difference Block",
|
||||
tags::barotrope &tags::prepared &tags::jacobian &tags::accuracy &tags::geometry
|
||||
tags::barotrope &tags::prepared &tags::jacobian &tags::accuracy &tags::reduced_stellar_geometry
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
@@ -1398,7 +1382,7 @@ TEST_CASE(
|
||||
TEST_CASE(
|
||||
"Prepared Stellar Equilibrium Selectively Invalidates Rows And Never "
|
||||
"Reprepares In Krylov Mult",
|
||||
tags::barotrope &tags::prepared &tags::contexts &tags::mfem_operators
|
||||
tags::barotrope &tags::prepared &tags::contexts &tags::mfem_operators &tags::reduced_stellar_geometry
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
@@ -1423,10 +1407,14 @@ TEST_CASE(
|
||||
stellarOperator.GetDisplacementOperator().GetResidualPreparationCount();
|
||||
const std::uint64_t massPreparations = stellarOperator.GetMassNormalizationOperator().GetPreparationCount();
|
||||
const std::uint64_t rootAssemblies = stellarOperator.GetStatistics().residualAssemblies;
|
||||
const std::uint64_t geometryBuilds = stellarOperator.GetStatistics().generatedGeometryBuilds;
|
||||
const auto generatedDisplacement = stellarOperator.GetGeneratedDisplacementDependency();
|
||||
|
||||
const auto repeated = stellarOperator.Prepare(state, dependencies, rotation);
|
||||
CHECK_FALSE(repeated.DidAnyWork());
|
||||
CHECK(stellarOperator.GetStatistics().residualAssemblies == rootAssemblies);
|
||||
CHECK(stellarOperator.GetStatistics().generatedGeometryBuilds == geometryBuilds);
|
||||
CHECK(stellarOperator.GetGeneratedDisplacementDependency() == generatedDisplacement);
|
||||
|
||||
const mfem::Vector direction = stellar_equilibrium_test_utils::make_direction(f, layout);
|
||||
mfem::Vector action;
|
||||
@@ -1440,6 +1428,26 @@ TEST_CASE(
|
||||
CHECK(stellarOperator.GetMassNormalizationOperator().GetPreparationCount() == massPreparations);
|
||||
CHECK(stellarOperator.GetStatistics().residualAssemblies == rootAssemblies);
|
||||
CHECK(stellarOperator.GetStatistics().jacobianApplications == 3);
|
||||
CHECK(stellarOperator.GetStatistics().generatedGeometryBuilds == geometryBuilds);
|
||||
|
||||
mfem::Vector surfaceDeformation =
|
||||
stellar_equilibrium_test_utils::value_view(state, layout, stellar_equilibrium_test_utils::displacementValue);
|
||||
for (int parameter = 0; parameter < surfaceDeformation.Size(); ++parameter) {
|
||||
surfaceDeformation(parameter) += 1.0e-4;
|
||||
}
|
||||
++dependencies.surfaceDeformation.revision;
|
||||
|
||||
const auto surfaceDeformationReport = stellarOperator.Prepare(state, dependencies, rotation);
|
||||
CHECK(surfaceDeformationReport.generatedVolumeDisplacement);
|
||||
CHECK(surfaceDeformationReport.generatedGeometry.isOrientationPreserving());
|
||||
CHECK(surfaceDeformationReport.generatedDisplacement.identity == generatedDisplacement.identity);
|
||||
CHECK(surfaceDeformationReport.generatedDisplacement.revision == generatedDisplacement.revision + 1);
|
||||
CHECK(stellarOperator.GetStatistics().generatedGeometryBuilds == geometryBuilds + 1);
|
||||
CHECK(surfaceDeformationReport.gravity.DidAnyWork());
|
||||
CHECK(surfaceDeformationReport.barotropicClosure.DidAnyWork());
|
||||
CHECK(surfaceDeformationReport.hydrostatic.DidAnyWork());
|
||||
CHECK(surfaceDeformationReport.displacement.DidAnyWork());
|
||||
CHECK(surfaceDeformationReport.massNormalization.DidAnyWork());
|
||||
|
||||
stellar_equilibrium_test_utils::value_view(state, layout, stellar_equilibrium_test_utils::gravityPotentialValue)
|
||||
.Add(0.03, stellar_equilibrium_test_utils::project_potential_direction(f, 0.41));
|
||||
@@ -1476,8 +1484,79 @@ TEST_CASE(
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Prepared Stellar Equilibrium Matches The Analytic N1 Lane Emden "
|
||||
"State Up To The Mixed Projection Floor",
|
||||
"Accepted Reduced Geometries Remain Valid Across Prepared Stellar Physics Quadrature Rules",
|
||||
tags::reduced_stellar_geometry &tags::prepared &tags::geometry &tags::self_consistency
|
||||
) {
|
||||
mean_field::utils::Args args = test_utils::setup_args();
|
||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||
REQUIRE(f.okay());
|
||||
|
||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||
const auto stellarModel = stellar_equilibrium_test_utils::make_stellar_model(barotrope, 1.15);
|
||||
auto parameterGeometry = stellarModel.compileDomainDeformation(f);
|
||||
const auto &surface = parameterGeometry.surfaceDeformationPrescription();
|
||||
|
||||
mean_field::operators::PreparedStellarEquilibriumOperator stellarOperator(
|
||||
f, *f.domainMapperStateless, stellarModel
|
||||
);
|
||||
const auto &layout = stellarOperator.GetLayout();
|
||||
mfem::Vector state = stellar_equilibrium_test_utils::make_state(f, layout);
|
||||
auto dependencies = stellar_equilibrium_test_utils::make_dependencies();
|
||||
|
||||
mfem::Vector uniformExpansion(surface.parameterCount());
|
||||
mfem::Vector uniformContraction(surface.parameterCount());
|
||||
mfem::Vector oblateSurface(surface.parameterCount());
|
||||
uniformExpansion = 1.0e-2;
|
||||
uniformContraction = -1.0e-2;
|
||||
for (int parameter = 0; parameter < surface.parameterCount(); ++parameter) {
|
||||
const double polarDirection = surface.radialDirection(parameter, 2);
|
||||
oblateSurface(parameter) =
|
||||
1.0e-2 * surface.referenceRadius(parameter) * (1.0 - 3.0 * polarDirection * polarDirection);
|
||||
}
|
||||
|
||||
const std::array<const mfem::Vector *, 3> shapes{
|
||||
&uniformExpansion,
|
||||
&uniformContraction,
|
||||
&oblateSurface,
|
||||
};
|
||||
|
||||
for (int shape = 0; shape < static_cast<int>(shapes.size()); ++shape) {
|
||||
stellar_equilibrium_test_utils::assign_value_block(
|
||||
state, layout, stellar_equilibrium_test_utils::displacementValue, *shapes[shape]
|
||||
);
|
||||
if (shape > 0) {
|
||||
++dependencies.surfaceDeformation.revision;
|
||||
}
|
||||
|
||||
const auto report =
|
||||
stellarOperator.Prepare(state, dependencies, stellar_equilibrium_test_utils::make_zero_rotation());
|
||||
|
||||
CAPTURE(shape);
|
||||
CHECK(report.generatedVolumeDisplacement);
|
||||
CHECK(report.generatedGeometry.isOrientationPreserving());
|
||||
CHECK(std::isfinite(report.generatedGeometry.minimumJacobianDeterminant));
|
||||
|
||||
const auto independentGeometry = stellarOperator.GetDomainDeformation().inspectMappedGeometry(
|
||||
stellarOperator.GetGeneratedVolumeDisplacement()
|
||||
);
|
||||
CHECK(independentGeometry.isOrientationPreserving());
|
||||
CHECK(
|
||||
std::abs(
|
||||
independentGeometry.minimumJacobianDeterminant - report.generatedGeometry.minimumJacobianDeterminant
|
||||
) <= 64.0 * std::numeric_limits<double>::epsilon() *
|
||||
std::max(1.0, std::abs(report.generatedGeometry.minimumJacobianDeterminant))
|
||||
);
|
||||
|
||||
mfem::Vector residual;
|
||||
stellarOperator.BuildResidual(residual);
|
||||
for (int entry = 0; entry < residual.Size(); ++entry) {
|
||||
REQUIRE(std::isfinite(residual(entry)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Reduced Stellar Equilibrium Preserves The Analytic N1 Floor Virtual Work And Rotational Shape Descent",
|
||||
tags::barotrope &tags::prepared &tags::analytic_comparison &tags::accuracy &tags::gravity &tags::hydro
|
||||
&tags::residuals
|
||||
) {
|
||||
@@ -1910,6 +1989,159 @@ TEST_CASE(
|
||||
CHECK(analyticMassError < 5.0e-5 * targetMass);
|
||||
|
||||
CHECK(analyticMassError < 0.10 * perturbedMassError);
|
||||
|
||||
/*
|
||||
* Return to the analytic spherical state before testing the reduced
|
||||
* mechanical dual and its rotational shape response.
|
||||
*/
|
||||
stellar_equilibrium_test_utils::increment_all_state_revisions(dependencies);
|
||||
stellarOperator.Prepare(analyticState, dependencies, zeroRotation);
|
||||
|
||||
auto parameterGeometry = stellarModel.compileDomainDeformation(f);
|
||||
const auto &surface = parameterGeometry.surfaceDeformationPrescription();
|
||||
|
||||
mfem::Vector oblateSurfaceDirection(surface.parameterCount());
|
||||
for (int parameter = 0; parameter < surface.parameterCount(); ++parameter) {
|
||||
const double polarDirection = surface.radialDirection(parameter, 2);
|
||||
oblateSurfaceDirection(parameter) =
|
||||
surface.referenceRadius(parameter) * (1.0 - 3.0 * polarDirection * polarDirection);
|
||||
}
|
||||
|
||||
const mfem::Vector surfaceParameters = stellar_equilibrium_test_utils::const_value_view(
|
||||
analyticState, layout, stellar_equilibrium_test_utils::displacementValue
|
||||
);
|
||||
mfem::Vector liftedOblateDirection(parameterGeometry.volumeDisplacementSize());
|
||||
parameterGeometry.applyJacobian(surfaceParameters, oblateSurfaceDirection, liftedOblateDirection);
|
||||
|
||||
mfem::Vector nonrotatingResidual;
|
||||
stellarOperator.BuildResidual(nonrotatingResidual);
|
||||
const mfem::Vector nonrotatingShapeResidual = stellar_equilibrium_test_utils::const_residual_view(
|
||||
nonrotatingResidual, layout, stellar_equilibrium_test_utils::displacementResidual
|
||||
);
|
||||
|
||||
const double reducedVirtualWork =
|
||||
gravity_prepared_test_utils::global_dot(oblateSurfaceDirection, nonrotatingShapeResidual, f.mesh->GetComm());
|
||||
const double volumeVirtualWork = gravity_prepared_test_utils::global_dot(
|
||||
liftedOblateDirection, stellarOperator.GetFullMechanicalResidual(), f.mesh->GetComm()
|
||||
);
|
||||
const double virtualWorkScale = std::max({1.0, std::abs(reducedVirtualWork), std::abs(volumeVirtualWork)});
|
||||
|
||||
INFO("Reduced mechanical virtual work = " << reducedVirtualWork);
|
||||
INFO("Lifted volume mechanical virtual work = " << volumeVirtualWork);
|
||||
CHECK(std::abs(reducedVirtualWork - volumeVirtualWork) <= 2.0e-12 * virtualWorkScale);
|
||||
|
||||
const double keplerianAngularSpeed =
|
||||
std::sqrt(mean_field::utils::G * targetMass / (stellarRadius * stellarRadius * stellarRadius));
|
||||
const double angularSpeed = 0.25 * keplerianAngularSpeed;
|
||||
|
||||
mfem::Vector angularVelocity(3);
|
||||
angularVelocity = 0.0;
|
||||
angularVelocity(2) = angularSpeed;
|
||||
mfem::Vector rotationCenter(3);
|
||||
rotationCenter = 0.0;
|
||||
const mean_field::physics::RigidRotation rotation(angularVelocity, rotationCenter);
|
||||
|
||||
++dependencies.rotation.revision;
|
||||
stellarOperator.Prepare(analyticState, dependencies, rotation);
|
||||
|
||||
mfem::Vector rotatingSphericalResidual;
|
||||
stellarOperator.BuildResidual(rotatingSphericalResidual);
|
||||
const mfem::Vector rotatingShapeResidual = stellar_equilibrium_test_utils::const_residual_view(
|
||||
rotatingSphericalResidual, layout, stellar_equilibrium_test_utils::displacementResidual
|
||||
);
|
||||
mfem::Vector rotationInducedShapeResidual(rotatingShapeResidual);
|
||||
rotationInducedShapeResidual -= nonrotatingShapeResidual;
|
||||
|
||||
const double rotationInducedWork = gravity_prepared_test_utils::global_dot(
|
||||
rotationInducedShapeResidual, oblateSurfaceDirection, f.mesh->GetComm()
|
||||
);
|
||||
const double rotationInducedNorm =
|
||||
stellar_equilibrium_test_utils::global_norm(rotationInducedShapeResidual, f.mesh->GetComm());
|
||||
const double oblateDirectionNorm =
|
||||
stellar_equilibrium_test_utils::global_norm(oblateSurfaceDirection, f.mesh->GetComm());
|
||||
const double rotationWorkScale = rotationInducedNorm * oblateDirectionNorm;
|
||||
|
||||
INFO("Rotation-induced reduced shape residual norm = " << rotationInducedNorm);
|
||||
INFO("Rotation-induced work against the oblate surface direction = " << rotationInducedWork);
|
||||
REQUIRE(rotationInducedNorm > 0.0);
|
||||
REQUIRE(oblateDirectionNorm > 0.0);
|
||||
REQUIRE(rotationWorkScale > 0.0);
|
||||
CHECK(rotationInducedWork < -1.0e-3 * rotationWorkScale);
|
||||
|
||||
mfem::Vector oblateDirection(layout.value_offsets().Last());
|
||||
oblateDirection = 0.0;
|
||||
stellar_equilibrium_test_utils::assign_value_block(
|
||||
oblateDirection, layout, stellar_equilibrium_test_utils::displacementValue, oblateSurfaceDirection
|
||||
);
|
||||
|
||||
const auto deformationStatisticsBefore = stellarOperator.GetDomainDeformation().actionStatistics();
|
||||
mfem::Vector oblateJacobianAction;
|
||||
stellarOperator.Mult(oblateDirection, oblateJacobianAction);
|
||||
const auto deformationStatisticsAfter = stellarOperator.GetDomainDeformation().actionStatistics();
|
||||
CHECK(
|
||||
deformationStatisticsAfter.pullbackDerivativeApplications ==
|
||||
deformationStatisticsBefore.pullbackDerivativeApplications + 1
|
||||
);
|
||||
|
||||
const mfem::Vector oblateShapeJacobianAction = stellar_equilibrium_test_utils::const_residual_view(
|
||||
oblateJacobianAction, layout, stellar_equilibrium_test_utils::displacementResidual
|
||||
);
|
||||
/*
|
||||
* Test descent for the rotation-induced departure from the nonrotating
|
||||
* state. The analytic finite-element state has a nonzero projection floor,
|
||||
* so minimizing the absolute residual would mix that unrelated floor into
|
||||
* the rotational response. The sign of the best correction along this
|
||||
* single trial coordinate is deliberately not prescribed: a shape-only
|
||||
* correction holds the thermodynamic and gravity unknowns fixed, whereas
|
||||
* the physical oblate equilibrium is a coupled response of every block.
|
||||
*/
|
||||
const double residualDirectionalDerivative = gravity_prepared_test_utils::global_dot(
|
||||
rotationInducedShapeResidual, oblateShapeJacobianAction, f.mesh->GetComm()
|
||||
);
|
||||
const double jacobianDirectionNormSquared = gravity_prepared_test_utils::global_dot(
|
||||
oblateShapeJacobianAction, oblateShapeJacobianAction, f.mesh->GetComm()
|
||||
);
|
||||
|
||||
REQUIRE(std::isfinite(residualDirectionalDerivative));
|
||||
REQUIRE(jacobianDirectionNormSquared > 0.0);
|
||||
REQUIRE(std::abs(residualDirectionalDerivative) > 1.0e-12 * rotationWorkScale);
|
||||
|
||||
const double optimalLinearizedAmplitude = -residualDirectionalDerivative / jacobianDirectionNormSquared;
|
||||
const double appliedShapeAmplitude =
|
||||
std::copysign(std::min(0.25 * std::abs(optimalLinearizedAmplitude), 2.0e-2), optimalLinearizedAmplitude);
|
||||
REQUIRE(appliedShapeAmplitude != 0.0);
|
||||
|
||||
mfem::Vector predictedShapeResidual(rotationInducedShapeResidual);
|
||||
predictedShapeResidual.Add(appliedShapeAmplitude, oblateShapeJacobianAction);
|
||||
const double predictedShapeNorm =
|
||||
stellar_equilibrium_test_utils::global_norm(predictedShapeResidual, f.mesh->GetComm());
|
||||
CHECK(predictedShapeNorm < rotationInducedNorm);
|
||||
|
||||
mfem::Vector correctedShapeState(analyticState);
|
||||
stellar_equilibrium_test_utils::value_view(
|
||||
correctedShapeState, layout, stellar_equilibrium_test_utils::displacementValue
|
||||
)
|
||||
.Add(appliedShapeAmplitude, oblateSurfaceDirection);
|
||||
++dependencies.surfaceDeformation.revision;
|
||||
stellarOperator.Prepare(correctedShapeState, dependencies, rotation);
|
||||
|
||||
mfem::Vector nonlinearCorrectedResidual;
|
||||
stellarOperator.BuildResidual(nonlinearCorrectedResidual);
|
||||
mfem::Vector nonlinearShapeDeparture(
|
||||
stellar_equilibrium_test_utils::const_residual_view(
|
||||
nonlinearCorrectedResidual, layout, stellar_equilibrium_test_utils::displacementResidual
|
||||
)
|
||||
);
|
||||
nonlinearShapeDeparture -= nonrotatingShapeResidual;
|
||||
const double nonlinearShapeDepartureNorm =
|
||||
stellar_equilibrium_test_utils::global_norm(nonlinearShapeDeparture, f.mesh->GetComm());
|
||||
|
||||
INFO("Optimal linearized shape amplitude = " << optimalLinearizedAmplitude);
|
||||
INFO("Applied shape amplitude = " << appliedShapeAmplitude);
|
||||
INFO("Rotation-induced shape residual norm = " << rotationInducedNorm);
|
||||
INFO("Predicted corrected rotational departure norm = " << predictedShapeNorm);
|
||||
INFO("Nonlinear corrected rotational departure norm = " << nonlinearShapeDepartureNorm);
|
||||
CHECK(nonlinearShapeDepartureNorm < rotationInducedNorm);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
@@ -2558,7 +2790,7 @@ TEST_CASE(
|
||||
displacementBlock.Add(appliedOblateAmplitude, oblateDisplacement);
|
||||
}
|
||||
|
||||
++dependencies.displacement.revision;
|
||||
++dependencies.surfaceDeformation.revision;
|
||||
|
||||
stellarOperator.Prepare(oblateState, dependencies, rotation);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user