feat(surface): major work on implementing surface constraints in a presciption agnostic manner
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.28)
|
cmake_minimum_required(VERSION 3.28)
|
||||||
project(MeanField CXX)
|
project(MeanField C CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
@@ -138,15 +138,24 @@ target_sources(mean_field
|
|||||||
libmeanfield/interface/operators/kernels/rotation_displacement_force_kernels.cppm
|
libmeanfield/interface/operators/kernels/rotation_displacement_force_kernels.cppm
|
||||||
libmeanfield/interface/operators/prepared_rotation_displacement_force.cppm
|
libmeanfield/interface/operators/prepared_rotation_displacement_force.cppm
|
||||||
libmeanfield/interface/operators/prepared_displacement_operator.cppm
|
libmeanfield/interface/operators/prepared_displacement_operator.cppm
|
||||||
libmeanfield/interface/eos/eos_base.cppm
|
libmeanfield/interface/eos/quantities.cppm
|
||||||
|
libmeanfield/interface/eos/relations.cppm
|
||||||
|
libmeanfield/interface/eos/concepts.cppm
|
||||||
|
libmeanfield/interface/eos/evaluation.cppm
|
||||||
|
libmeanfield/interface/eos/pressure_surface.cppm
|
||||||
|
libmeanfield/interface/eos/runtime.cppm
|
||||||
libmeanfield/interface/eos/polytropic.cppm
|
libmeanfield/interface/eos/polytropic.cppm
|
||||||
libmeanfield/interface/models/structure/structure_base.cppm
|
libmeanfield/interface/models/structure/structure_base.cppm
|
||||||
libmeanfield/interface/models/structure/polytropic.cppm
|
libmeanfield/interface/models/structure/polytropic.cppm
|
||||||
libmeanfield/interface/models/structure_profile.cppm
|
libmeanfield/interface/models/structure_profile.cppm
|
||||||
libmeanfield/interface/surface/surface_base.cppm
|
libmeanfield/interface/surface/constant.cppm
|
||||||
libmeanfield/interface/surface/isobaric.cppm
|
libmeanfield/interface/surface/dependencies.cppm
|
||||||
|
libmeanfield/interface/surface/compiled.cppm
|
||||||
|
libmeanfield/interface/surface/compiler.cppm
|
||||||
libmeanfield/interface/models/stellar_model.cppm
|
libmeanfield/interface/models/stellar_model.cppm
|
||||||
libmeanfield/interface/operators/prepared_mass_normalization.cppm
|
libmeanfield/interface/operators/prepared_mass_normalization.cppm
|
||||||
|
libmeanfield/interface/operators/prepared_centering_constraint.cppm
|
||||||
|
libmeanfield/interface/operators/prepared_surface_constraint.cppm
|
||||||
libmeanfield/interface/operators/prepared_stellar_equilibrium.cppm
|
libmeanfield/interface/operators/prepared_stellar_equilibrium.cppm
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -194,6 +203,12 @@ add_executable(tests
|
|||||||
tests/operators/contexts/gravity_field_context.cpp
|
tests/operators/contexts/gravity_field_context.cpp
|
||||||
tests/physics/gravity_monopole_accuracy.cpp
|
tests/physics/gravity_monopole_accuracy.cpp
|
||||||
tests/physics/barotrope.cpp
|
tests/physics/barotrope.cpp
|
||||||
|
tests/physics/polytropic_eos_characterization.cpp
|
||||||
|
tests/physics/equation_of_state_type_system.cpp
|
||||||
|
tests/physics/equation_of_state_consumer_contracts.cpp
|
||||||
|
tests/physics/polytropic_eos_relations.cpp
|
||||||
|
tests/physics/equation_of_state_runtime_view.cpp
|
||||||
|
tests/surface/constant_surface_compilation.cpp
|
||||||
tests/operators/kernels/barotropic_closure_kernels.cpp
|
tests/operators/kernels/barotropic_closure_kernels.cpp
|
||||||
tests/operators/prepared_barotropic_closure.cpp
|
tests/operators/prepared_barotropic_closure.cpp
|
||||||
tests/operators/contexts/barotropic_closure_linearization_context.cpp
|
tests/operators/contexts/barotropic_closure_linearization_context.cpp
|
||||||
@@ -215,7 +230,6 @@ add_executable(tests
|
|||||||
tests/operators/prepared_rotation_displacement_force_analytic.cpp
|
tests/operators/prepared_rotation_displacement_force_analytic.cpp
|
||||||
tests/operators/prepared_rotation_displacement_force_affine_deformation.cpp
|
tests/operators/prepared_rotation_displacement_force_affine_deformation.cpp
|
||||||
tests/operators/prepared_displacement_operator.cpp
|
tests/operators/prepared_displacement_operator.cpp
|
||||||
tests/surface/isobaric.cpp
|
|
||||||
tests/models/stellar_model.cpp
|
tests/models/stellar_model.cpp
|
||||||
tests/operators/prepared_mass_normalization.cpp
|
tests/operators/prepared_mass_normalization.cpp
|
||||||
tests/operators/prepared_stellar_equilibrium.cpp
|
tests/operators/prepared_stellar_equilibrium.cpp
|
||||||
@@ -233,6 +247,7 @@ target_sources(experiment_mod
|
|||||||
PUBLIC
|
PUBLIC
|
||||||
FILE_SET CXX_MODULES FILES
|
FILE_SET CXX_MODULES FILES
|
||||||
experiments/experiment_results.cppm
|
experiments/experiment_results.cppm
|
||||||
|
experiments/stellar_null_space.cppm
|
||||||
)
|
)
|
||||||
target_link_libraries(experiment_mod
|
target_link_libraries(experiment_mod
|
||||||
PUBLIC
|
PUBLIC
|
||||||
@@ -247,6 +262,21 @@ add_executable(experiments
|
|||||||
|
|
||||||
target_link_libraries(experiments PRIVATE mean_field test_mod experiment_mod Catch2::Catch2 Boost::boost)
|
target_link_libraries(experiments PRIVATE mean_field test_mod experiment_mod Catch2::Catch2 Boost::boost)
|
||||||
|
|
||||||
|
add_executable(stellar_null_space_experiments
|
||||||
|
experiments/experiment_main.cpp
|
||||||
|
experiments/rigid_motion_null_space.cpp
|
||||||
|
experiments/gravity_completed_rigid_motion.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(stellar_null_space_experiments
|
||||||
|
PRIVATE
|
||||||
|
mean_field
|
||||||
|
test_mod
|
||||||
|
experiment_mod
|
||||||
|
Catch2::Catch2
|
||||||
|
Boost::boost
|
||||||
|
)
|
||||||
|
|
||||||
include (CTest)
|
include (CTest)
|
||||||
include (Catch)
|
include (Catch)
|
||||||
catch_discover_tests(
|
catch_discover_tests(
|
||||||
|
|||||||
@@ -24,6 +24,49 @@ Run only the budget and choose its output path with:
|
|||||||
./mean_field_experiments --experiment-output gravity_budget.csv --catch2 "[accuracy]"
|
./mean_field_experiments --experiment-output gravity_budget.csv --catch2 "[accuracy]"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Stellar-equilibrium null-space experiments
|
||||||
|
|
||||||
|
`stellar_null_space_experiments` is a dedicated diagnostic executable rather
|
||||||
|
than an ordinary verification or validation test. It constructs the analytic
|
||||||
|
`n = 3` Lane-Emden seed, probes the three computational translations and three
|
||||||
|
computational rotations, and compares the Jacobian before and after the strong
|
||||||
|
centering-row replacement. It records total and residual-block response norms,
|
||||||
|
the isolated centering contribution, and centered finite-difference errors.
|
||||||
|
|
||||||
|
The experiment prints rank-zero progress messages while it builds the seed,
|
||||||
|
solves its gravity field, and completes each rigid-mode case. Run it with:
|
||||||
|
|
||||||
|
```text
|
||||||
|
mpirun -np 1 ./cmake-build-debug-homebrew/stellar_null_space_experiments \
|
||||||
|
--experiment-output stellar_null_space.csv \
|
||||||
|
--catch2 "[null_space][rigid_motion]"
|
||||||
|
```
|
||||||
|
|
||||||
|
The rotation sweep includes zero rotation and a spherical-state diagnostic at
|
||||||
|
half the Keplerian angular speed. The rotating result is an operator-symmetry
|
||||||
|
probe, not a definitive rotating-equilibrium null-space measurement.
|
||||||
|
|
||||||
|
The gravity-completed probe solves the linearized mixed gravity subsystem for
|
||||||
|
the gravity-gradient and gravity-potential variations accompanying each rigid
|
||||||
|
displacement. It then measures the complete equilibrium response with and
|
||||||
|
without the centering rows:
|
||||||
|
|
||||||
|
```text
|
||||||
|
mpirun -np 1 ./cmake-build-debug-homebrew/stellar_null_space_experiments \
|
||||||
|
--experiment-output gravity_completed_null_space.csv \
|
||||||
|
--catch2 "[null_space][gravity_completed]"
|
||||||
|
```
|
||||||
|
|
||||||
|
The gravity solver prints its convergence summary, while the experiment prints
|
||||||
|
the current mode and completed-case count. This probe prepares each rotation
|
||||||
|
state only once and does not repeat the expensive nonlinear finite-difference
|
||||||
|
calculations from the original rigid-motion diagnostic.
|
||||||
|
|
||||||
|
A whole-Jacobian dense singular-value experiment is intentionally deferred.
|
||||||
|
The checked-in `sandbox.smesh` is too large for a useful dense SVD, and the
|
||||||
|
current matrix-free root operator does not provide a transpose action needed by
|
||||||
|
a scalable smallest-singular-value method.
|
||||||
|
|
||||||
The executable needs the same dependencies, generated module mapping, and
|
The executable needs the same dependencies, generated module mapping, and
|
||||||
configuration registration as the existing Catch2 test executable. Add
|
configuration registration as the existing Catch2 test executable. Add
|
||||||
`experiment_main.cpp` and `gravity_accuracy_budget.cpp` as a second executable
|
`experiment_main.cpp` and `gravity_accuracy_budget.cpp` as a second executable
|
||||||
|
|||||||
273
experiments/gravity_completed_rigid_motion.cpp
Normal file
273
experiments/gravity_completed_rigid_motion.cpp
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <mfem.hpp>
|
||||||
|
#include <mpi.h>
|
||||||
|
|
||||||
|
import experiment;
|
||||||
|
import experiment.stellar_null_space;
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class GravityUnknownJacobian final : public mfem::Operator {
|
||||||
|
public:
|
||||||
|
explicit GravityUnknownJacobian(
|
||||||
|
const mean_field::operators::PreparedStellarEquilibriumOperator &stellarOperator
|
||||||
|
)
|
||||||
|
: mfem::Operator(
|
||||||
|
stellarOperator.GetLayout().size(experiment::null_space::gravityGradientValue) +
|
||||||
|
stellarOperator.GetLayout().size(experiment::null_space::gravityPotentialValue)
|
||||||
|
),
|
||||||
|
m_stellarOperator(stellarOperator),
|
||||||
|
m_gravityGradientSize(stellarOperator.GetLayout().size(experiment::null_space::gravityGradientValue)) {
|
||||||
|
MFEM_VERIFY(Width() == Height(), "The reduced gravity Jacobian must be square.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mult(
|
||||||
|
const mfem::Vector &gravityDirection,
|
||||||
|
mfem::Vector &gravityAction
|
||||||
|
) const override {
|
||||||
|
MFEM_VERIFY(gravityDirection.Size() == Width(), "The reduced gravity direction has the wrong size.");
|
||||||
|
|
||||||
|
const mfem::Vector gravityGradientDirection(
|
||||||
|
const_cast<mfem::real_t *>(gravityDirection.GetData()), m_gravityGradientSize
|
||||||
|
);
|
||||||
|
const mfem::Vector gravityPotentialDirection(
|
||||||
|
const_cast<mfem::real_t *>(gravityDirection.GetData()) + m_gravityGradientSize,
|
||||||
|
Width() - m_gravityGradientSize
|
||||||
|
);
|
||||||
|
|
||||||
|
m_stellarOperator.GetGravityOperator().ApplyGravityUnknowns(
|
||||||
|
gravityGradientDirection,
|
||||||
|
gravityPotentialDirection,
|
||||||
|
m_stellarOperator.GetGravityContext().GetGeometryContext(),
|
||||||
|
gravityAction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int gravity_gradient_size() const noexcept {
|
||||||
|
return m_gravityGradientSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const mean_field::operators::PreparedStellarEquilibriumOperator &m_stellarOperator;
|
||||||
|
int m_gravityGradientSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
void add_block_metrics(
|
||||||
|
std::map<
|
||||||
|
std::string,
|
||||||
|
double> &metrics,
|
||||||
|
const std::string &prefix,
|
||||||
|
const std::array<
|
||||||
|
double,
|
||||||
|
6> &norms
|
||||||
|
) {
|
||||||
|
for (std::size_t block = 0; block < norms.size(); ++block) {
|
||||||
|
metrics.emplace(prefix + experiment::null_space::residualBlockNames[block] + "_norm", norms[block]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] mfem::Vector gravity_residual_blocks(
|
||||||
|
const mfem::Vector &completeAction,
|
||||||
|
const mean_field::operators::StellarEquilibriumLayout &layout
|
||||||
|
) {
|
||||||
|
const mfem::Vector gradient = experiment::null_space::const_residual_view(
|
||||||
|
completeAction, layout, experiment::null_space::gravityGradientResidual
|
||||||
|
);
|
||||||
|
const mfem::Vector potential = experiment::null_space::const_residual_view(
|
||||||
|
completeAction, layout, experiment::null_space::gravityPotentialResidual
|
||||||
|
);
|
||||||
|
|
||||||
|
mfem::Vector result(gradient.Size() + potential.Size());
|
||||||
|
mfem::Vector(result.GetData(), gradient.Size()) = gradient;
|
||||||
|
mfem::Vector(result.GetData() + gradient.Size(), potential.Size()) = potential;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assign_gravity_completion(
|
||||||
|
mfem::Vector &completeDirection,
|
||||||
|
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||||
|
const mfem::Vector &gravityCompletion,
|
||||||
|
const int gravityGradientSize
|
||||||
|
) {
|
||||||
|
const mfem::Vector gravityGradient(
|
||||||
|
const_cast<mfem::real_t *>(gravityCompletion.GetData()), gravityGradientSize
|
||||||
|
);
|
||||||
|
const mfem::Vector gravityPotential(
|
||||||
|
const_cast<mfem::real_t *>(gravityCompletion.GetData()) + gravityGradientSize,
|
||||||
|
gravityCompletion.Size() - gravityGradientSize
|
||||||
|
);
|
||||||
|
experiment::null_space::assign_value_block(
|
||||||
|
completeDirection, layout, experiment::null_space::gravityGradientValue, gravityGradient
|
||||||
|
);
|
||||||
|
experiment::null_space::assign_value_block(
|
||||||
|
completeDirection, layout, experiment::null_space::gravityPotentialValue, gravityPotential
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_centering_rows(
|
||||||
|
const mean_field::operators::PreparedStellarEquilibriumOperator &stellarOperator,
|
||||||
|
const mfem::Vector &direction,
|
||||||
|
mfem::Vector &action
|
||||||
|
) {
|
||||||
|
const auto &layout = stellarOperator.GetLayout();
|
||||||
|
const mfem::Vector displacementDirection =
|
||||||
|
experiment::null_space::const_value_view(direction, layout, experiment::null_space::displacementValue);
|
||||||
|
mfem::Vector displacementAction =
|
||||||
|
experiment::null_space::residual_view(action, layout, experiment::null_space::displacementResidual);
|
||||||
|
stellarOperator.GetCenteringConstraintOperator().ApplyJacobianRows(displacementDirection, displacementAction);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Gravity-Completed Rigid Motion Responses Of The Stellar Equilibrium Jacobian",
|
||||||
|
"[null_space][gravity_completed]"
|
||||||
|
) {
|
||||||
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
args.p.rtol = 1.0e-11;
|
||||||
|
args.p.atol = std::min(args.p.atol, 1.0e-13);
|
||||||
|
args.p.max_iters = std::max(args.p.max_iters, 2000);
|
||||||
|
|
||||||
|
experiment::null_space::N3Equilibrium fixture(std::move(args));
|
||||||
|
const MPI_Comm communicator = fixture.fem().mesh->GetComm();
|
||||||
|
int rank = 0;
|
||||||
|
MPI_Comm_rank(communicator, &rank);
|
||||||
|
|
||||||
|
const auto modes = experiment::null_space::make_rigid_modes(fixture);
|
||||||
|
constexpr std::array<double, 2> rotationFractions{0.0, 0.5};
|
||||||
|
const int totalCases = static_cast<int>(rotationFractions.size() * modes.size());
|
||||||
|
int completedCases = 0;
|
||||||
|
|
||||||
|
for (const double rotationFraction : rotationFractions) {
|
||||||
|
const mean_field::physics::RigidRotation rotation = fixture.rotation(rotationFraction);
|
||||||
|
fixture.prepare(fixture.state(), rotation);
|
||||||
|
|
||||||
|
GravityUnknownJacobian gravityUnknownJacobian(fixture.stellar_operator());
|
||||||
|
mean_field::operators::ReducedGravityFieldPreconditioner gravityPreconditioner(
|
||||||
|
fixture.fem(), fixture.stellar_operator().GetGravityContext().GetGeometryContext()
|
||||||
|
);
|
||||||
|
|
||||||
|
mfem::MINRESSolver gravitySolver(communicator);
|
||||||
|
gravitySolver.SetOperator(gravityUnknownJacobian);
|
||||||
|
gravitySolver.SetPreconditioner(gravityPreconditioner);
|
||||||
|
gravitySolver.SetRelTol(1.0e-11);
|
||||||
|
gravitySolver.SetAbsTol(1.0e-13);
|
||||||
|
gravitySolver.SetMaxIter(2000);
|
||||||
|
gravitySolver.SetPrintLevel(1);
|
||||||
|
|
||||||
|
for (const experiment::null_space::RigidMode &mode : modes) {
|
||||||
|
experiment::null_space::report_progress(
|
||||||
|
communicator, "solving the gravity completion for " + mode.name + " at rotation fraction " +
|
||||||
|
std::to_string(rotationFraction) + " (" + std::to_string(completedCases + 1) + "/" +
|
||||||
|
std::to_string(totalCases) + ")"
|
||||||
|
);
|
||||||
|
|
||||||
|
const mfem::Vector displacementOnlyAction = fixture.unpinned_jacobian_action(mode.direction);
|
||||||
|
mfem::Vector gravityRightHandSide =
|
||||||
|
gravity_residual_blocks(displacementOnlyAction, fixture.stellar_operator().GetLayout());
|
||||||
|
gravityRightHandSide *= -1.0;
|
||||||
|
|
||||||
|
mfem::Vector gravityCompletion(gravityUnknownJacobian.Width());
|
||||||
|
gravityCompletion = 0.0;
|
||||||
|
gravitySolver.Mult(gravityRightHandSide, gravityCompletion);
|
||||||
|
|
||||||
|
REQUIRE(gravitySolver.GetConverged());
|
||||||
|
|
||||||
|
mfem::Vector gravitySolveAction;
|
||||||
|
gravityUnknownJacobian.Mult(gravityCompletion, gravitySolveAction);
|
||||||
|
mfem::Vector gravitySolveResidual(gravitySolveAction);
|
||||||
|
gravitySolveResidual -= gravityRightHandSide;
|
||||||
|
|
||||||
|
const double gravityRightHandSideNorm =
|
||||||
|
experiment::null_space::global_norm(gravityRightHandSide, communicator);
|
||||||
|
const double gravitySolveResidualNorm =
|
||||||
|
experiment::null_space::global_norm(gravitySolveResidual, communicator);
|
||||||
|
const double gravitySolveRelativeResidual =
|
||||||
|
gravitySolveResidualNorm / std::max(gravityRightHandSideNorm, std::numeric_limits<double>::epsilon());
|
||||||
|
|
||||||
|
REQUIRE(std::isfinite(gravitySolveRelativeResidual));
|
||||||
|
|
||||||
|
mfem::Vector completedDirection(mode.direction);
|
||||||
|
assign_gravity_completion(
|
||||||
|
completedDirection, fixture.stellar_operator().GetLayout(), gravityCompletion,
|
||||||
|
gravityUnknownJacobian.gravity_gradient_size()
|
||||||
|
);
|
||||||
|
|
||||||
|
const mfem::Vector completedUnpinnedAction = fixture.unpinned_jacobian_action(completedDirection);
|
||||||
|
mfem::Vector completedConstrainedAction(completedUnpinnedAction);
|
||||||
|
apply_centering_rows(fixture.stellar_operator(), completedDirection, completedConstrainedAction);
|
||||||
|
|
||||||
|
mfem::Vector centeringContribution(completedConstrainedAction);
|
||||||
|
centeringContribution -= completedUnpinnedAction;
|
||||||
|
|
||||||
|
std::map<std::string, double> metrics{
|
||||||
|
{"displacement_only_input_norm", experiment::null_space::global_norm(mode.direction, communicator)},
|
||||||
|
{"gravity_completion_norm", experiment::null_space::global_norm(gravityCompletion, communicator)},
|
||||||
|
{"completed_input_norm", experiment::null_space::global_norm(completedDirection, communicator)},
|
||||||
|
{"displacement_only_action_norm",
|
||||||
|
experiment::null_space::global_norm(displacementOnlyAction, communicator)},
|
||||||
|
{"gravity_completed_unpinned_action_norm",
|
||||||
|
experiment::null_space::global_norm(completedUnpinnedAction, communicator)},
|
||||||
|
{"gravity_completed_constrained_action_norm",
|
||||||
|
experiment::null_space::global_norm(completedConstrainedAction, communicator)},
|
||||||
|
{"centering_contribution_norm",
|
||||||
|
experiment::null_space::global_norm(centeringContribution, communicator)},
|
||||||
|
{"gravity_solve_rhs_norm", gravityRightHandSideNorm},
|
||||||
|
{"gravity_solve_residual_norm", gravitySolveResidualNorm},
|
||||||
|
{"gravity_solve_relative_residual", gravitySolveRelativeResidual},
|
||||||
|
{"gravity_solve_iterations", static_cast<double>(gravitySolver.GetNumIterations())},
|
||||||
|
{"gravity_solve_final_norm", gravitySolver.GetFinalNorm()}
|
||||||
|
};
|
||||||
|
|
||||||
|
add_block_metrics(
|
||||||
|
metrics, "displacement_only_",
|
||||||
|
experiment::null_space::residual_block_norms(
|
||||||
|
displacementOnlyAction, fixture.stellar_operator().GetLayout(), communicator
|
||||||
|
)
|
||||||
|
);
|
||||||
|
add_block_metrics(
|
||||||
|
metrics, "gravity_completed_unpinned_",
|
||||||
|
experiment::null_space::residual_block_norms(
|
||||||
|
completedUnpinnedAction, fixture.stellar_operator().GetLayout(), communicator
|
||||||
|
)
|
||||||
|
);
|
||||||
|
add_block_metrics(
|
||||||
|
metrics, "gravity_completed_constrained_",
|
||||||
|
experiment::null_space::residual_block_norms(
|
||||||
|
completedConstrainedAction, fixture.stellar_operator().GetLayout(), communicator
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rank == 0) {
|
||||||
|
experiment::record_experiment_result(
|
||||||
|
"gravity_completed_stellar_rigid_motion_null_space", mode.name,
|
||||||
|
{{"mode_kind",
|
||||||
|
mode.kind == experiment::null_space::RigidModeKind::translation ? "translation" : "rotation"},
|
||||||
|
{"axis", std::to_string(mode.axis)},
|
||||||
|
{"rotation_fraction_of_keplerian", std::to_string(rotationFraction)},
|
||||||
|
{"mesh_file", test_utils::setup_args().mesh_file},
|
||||||
|
{"local_state_dofs", std::to_string(fixture.stellar_operator().Width())}},
|
||||||
|
std::move(metrics)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
++completedCases;
|
||||||
|
experiment::null_space::report_progress(
|
||||||
|
communicator, "completed " + std::to_string(completedCases) + "/" + std::to_string(totalCases) +
|
||||||
|
" gravity-completed rigid-mode cases"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
experiment::null_space::report_progress(
|
||||||
|
communicator, "gravity-completed rigid-motion probe complete; writing CSV output"
|
||||||
|
);
|
||||||
|
}
|
||||||
174
experiments/rigid_motion_null_space.cpp
Normal file
174
experiments/rigid_motion_null_space.cpp
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <mfem.hpp>
|
||||||
|
#include <mpi.h>
|
||||||
|
|
||||||
|
import experiment;
|
||||||
|
import experiment.stellar_null_space;
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
[[nodiscard]] double relative_difference(
|
||||||
|
const mfem::Vector &computed,
|
||||||
|
const mfem::Vector &reference,
|
||||||
|
const MPI_Comm communicator
|
||||||
|
) {
|
||||||
|
mfem::Vector difference(computed);
|
||||||
|
difference -= reference;
|
||||||
|
const double scale = std::max(
|
||||||
|
{experiment::null_space::global_norm(computed, communicator),
|
||||||
|
experiment::null_space::global_norm(reference, communicator), std::numeric_limits<double>::epsilon()}
|
||||||
|
);
|
||||||
|
return experiment::null_space::global_norm(difference, communicator) / scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_block_metrics(
|
||||||
|
std::map<
|
||||||
|
std::string,
|
||||||
|
double> &metrics,
|
||||||
|
const std::string &prefix,
|
||||||
|
const std::array<
|
||||||
|
double,
|
||||||
|
6> &norms
|
||||||
|
) {
|
||||||
|
for (std::size_t block = 0; block < norms.size(); ++block) {
|
||||||
|
metrics.emplace(prefix + experiment::null_space::residualBlockNames[block] + "_norm", norms[block]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Rigid Motion Responses Of The Stellar Equilibrium Jacobian",
|
||||||
|
"[null_space][rigid_motion]"
|
||||||
|
) {
|
||||||
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
args.p.rtol = 1.0e-12;
|
||||||
|
args.p.atol = std::min(args.p.atol, 1.0e-14);
|
||||||
|
args.p.max_iters = std::max(args.p.max_iters, 2000);
|
||||||
|
|
||||||
|
experiment::null_space::N3Equilibrium fixture(std::move(args));
|
||||||
|
const MPI_Comm communicator = fixture.fem().mesh->GetComm();
|
||||||
|
int rank = 0;
|
||||||
|
MPI_Comm_rank(communicator, &rank);
|
||||||
|
|
||||||
|
const auto modes = experiment::null_space::make_rigid_modes(fixture);
|
||||||
|
constexpr std::array<double, 2> rotationFractions{0.0, 0.5};
|
||||||
|
constexpr std::array<double, 2> finiteDifferenceSteps{1.0e-4, 1.0e-6};
|
||||||
|
const int totalCases = static_cast<int>(rotationFractions.size() * modes.size());
|
||||||
|
int completedCases = 0;
|
||||||
|
|
||||||
|
for (const double rotationFraction : rotationFractions) {
|
||||||
|
const mean_field::physics::RigidRotation rotation = fixture.rotation(rotationFraction);
|
||||||
|
fixture.prepare(fixture.state(), rotation);
|
||||||
|
|
||||||
|
mfem::Vector constrainedResidual;
|
||||||
|
fixture.stellar_operator().BuildResidual(constrainedResidual);
|
||||||
|
const mfem::Vector unpinnedResidual = fixture.unpinned_residual();
|
||||||
|
|
||||||
|
REQUIRE(constrainedResidual.Size() == unpinnedResidual.Size());
|
||||||
|
REQUIRE(std::isfinite(experiment::null_space::global_norm(constrainedResidual, communicator)));
|
||||||
|
REQUIRE(std::isfinite(experiment::null_space::global_norm(unpinnedResidual, communicator)));
|
||||||
|
|
||||||
|
for (const experiment::null_space::RigidMode &mode : modes) {
|
||||||
|
experiment::null_space::report_progress(
|
||||||
|
communicator, "probing " + mode.name + " at rotation fraction " + std::to_string(rotationFraction) +
|
||||||
|
" (" + std::to_string(completedCases + 1) + "/" + std::to_string(totalCases) + ")"
|
||||||
|
);
|
||||||
|
|
||||||
|
fixture.prepare(fixture.state(), rotation);
|
||||||
|
const mfem::Vector unpinnedAction = fixture.unpinned_jacobian_action(mode.direction);
|
||||||
|
mfem::Vector constrainedAction;
|
||||||
|
fixture.stellar_operator().Mult(mode.direction, constrainedAction);
|
||||||
|
|
||||||
|
mfem::Vector centeringContribution(constrainedAction);
|
||||||
|
centeringContribution -= unpinnedAction;
|
||||||
|
|
||||||
|
const double inputNorm = experiment::null_space::global_norm(mode.direction, communicator);
|
||||||
|
const double unpinnedNorm = experiment::null_space::global_norm(unpinnedAction, communicator);
|
||||||
|
const double constrainedNorm = experiment::null_space::global_norm(constrainedAction, communicator);
|
||||||
|
|
||||||
|
REQUIRE(inputNorm > 0.0);
|
||||||
|
REQUIRE(std::isfinite(unpinnedNorm));
|
||||||
|
REQUIRE(std::isfinite(constrainedNorm));
|
||||||
|
|
||||||
|
std::map<std::string, double> metrics{
|
||||||
|
{"input_algebraic_norm", inputNorm},
|
||||||
|
{"unpinned_action_norm", unpinnedNorm},
|
||||||
|
{"unpinned_action_per_input_norm", unpinnedNorm / inputNorm},
|
||||||
|
{"constrained_action_norm", constrainedNorm},
|
||||||
|
{"constrained_action_per_input_norm", constrainedNorm / inputNorm},
|
||||||
|
{"centering_contribution_norm",
|
||||||
|
experiment::null_space::global_norm(centeringContribution, communicator)},
|
||||||
|
{"unpinned_base_residual_norm", experiment::null_space::global_norm(unpinnedResidual, communicator)},
|
||||||
|
{"constrained_base_residual_norm",
|
||||||
|
experiment::null_space::global_norm(constrainedResidual, communicator)}
|
||||||
|
};
|
||||||
|
|
||||||
|
add_block_metrics(
|
||||||
|
metrics, "unpinned_",
|
||||||
|
experiment::null_space::residual_block_norms(
|
||||||
|
unpinnedAction, fixture.stellar_operator().GetLayout(), communicator
|
||||||
|
)
|
||||||
|
);
|
||||||
|
add_block_metrics(
|
||||||
|
metrics, "constrained_",
|
||||||
|
experiment::null_space::residual_block_norms(
|
||||||
|
constrainedAction, fixture.stellar_operator().GetLayout(), communicator
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const double step : finiteDifferenceSteps) {
|
||||||
|
mfem::Vector plusState(fixture.state());
|
||||||
|
plusState.Add(step, mode.direction);
|
||||||
|
fixture.prepare(plusState, rotation);
|
||||||
|
const mfem::Vector plusResidual = fixture.unpinned_residual();
|
||||||
|
|
||||||
|
mfem::Vector minusState(fixture.state());
|
||||||
|
minusState.Add(-step, mode.direction);
|
||||||
|
fixture.prepare(minusState, rotation);
|
||||||
|
const mfem::Vector minusResidual = fixture.unpinned_residual();
|
||||||
|
|
||||||
|
mfem::Vector finiteDifference(plusResidual);
|
||||||
|
finiteDifference -= minusResidual;
|
||||||
|
finiteDifference /= 2.0 * step;
|
||||||
|
|
||||||
|
const std::string stepName = step == finiteDifferenceSteps.front() ? "1e-4" : "1e-6";
|
||||||
|
metrics.emplace(
|
||||||
|
"finite_difference_relative_error_" + stepName,
|
||||||
|
relative_difference(unpinnedAction, finiteDifference, communicator)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixture.prepare(fixture.state(), rotation);
|
||||||
|
|
||||||
|
if (rank == 0) {
|
||||||
|
experiment::record_experiment_result(
|
||||||
|
"stellar_rigid_motion_null_space", mode.name,
|
||||||
|
{{"mode_kind",
|
||||||
|
mode.kind == experiment::null_space::RigidModeKind::translation ? "translation" : "rotation"},
|
||||||
|
{"axis", std::to_string(mode.axis)},
|
||||||
|
{"rotation_fraction_of_keplerian", std::to_string(rotationFraction)},
|
||||||
|
{"mesh_file", test_utils::setup_args().mesh_file},
|
||||||
|
{"local_state_dofs", std::to_string(fixture.stellar_operator().Width())}},
|
||||||
|
std::move(metrics)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
++completedCases;
|
||||||
|
experiment::null_space::report_progress(
|
||||||
|
communicator,
|
||||||
|
"completed " + std::to_string(completedCases) + "/" + std::to_string(totalCases) + " rigid-mode cases"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
experiment::null_space::report_progress(communicator, "rigid-motion probe complete; writing CSV output");
|
||||||
|
}
|
||||||
519
experiments/stellar_null_space.cppm
Normal file
519
experiments/stellar_null_space.cppm
Normal file
@@ -0,0 +1,519 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <mfem.hpp>
|
||||||
|
#include <mpi.h>
|
||||||
|
|
||||||
|
export module experiment.stellar_null_space;
|
||||||
|
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
export namespace experiment::null_space {
|
||||||
|
using Form = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||||
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
using Model = mean_field::models::StellarModel<mean_field::models::structure::PolytropicStructure>;
|
||||||
|
|
||||||
|
constexpr auto densityValue =
|
||||||
|
mean_field::utils::blocks::get_value_block<Form>(mean_field::utils::blocks::density_field.mass_term);
|
||||||
|
constexpr auto displacementValue =
|
||||||
|
mean_field::utils::blocks::get_value_block<Form>(mean_field::utils::blocks::displacement_field.geometry_term);
|
||||||
|
constexpr auto gravityGradientValue =
|
||||||
|
mean_field::utils::blocks::get_value_block<Form>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||||
|
constexpr auto gravityPotentialValue =
|
||||||
|
mean_field::utils::blocks::get_value_block<Form>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||||
|
constexpr auto enthalpyValue =
|
||||||
|
mean_field::utils::blocks::get_value_block<Form>(mean_field::utils::blocks::enthalpy_field.specific_term);
|
||||||
|
constexpr auto bernoulliValue = mean_field::utils::blocks::get_value_block<Form>(
|
||||||
|
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||||
|
);
|
||||||
|
|
||||||
|
constexpr auto gravityGradientResidual =
|
||||||
|
mean_field::utils::blocks::get_residual_block<Form>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||||
|
constexpr auto gravityPotentialResidual =
|
||||||
|
mean_field::utils::blocks::get_residual_block<Form>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||||
|
constexpr auto densityResidual =
|
||||||
|
mean_field::utils::blocks::get_residual_block<Form>(mean_field::utils::blocks::density_field.mass_term);
|
||||||
|
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<Form>(
|
||||||
|
mean_field::utils::blocks::displacement_field.geometry_term
|
||||||
|
);
|
||||||
|
constexpr auto enthalpyResidual =
|
||||||
|
mean_field::utils::blocks::get_residual_block<Form>(mean_field::utils::blocks::enthalpy_field.specific_term);
|
||||||
|
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<Form>(
|
||||||
|
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||||
|
);
|
||||||
|
|
||||||
|
inline constexpr std::array<const char *, 6> residualBlockNames{"gravity_gradient", "gravity_potential", "closure",
|
||||||
|
"displacement", "hydrostatic", "mass"};
|
||||||
|
|
||||||
|
template <int index>
|
||||||
|
[[nodiscard]] mfem::Vector value_view(
|
||||||
|
mfem::Vector &vector,
|
||||||
|
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||||
|
const mean_field::utils::blocks::value_block<index> block
|
||||||
|
) {
|
||||||
|
return mfem::Vector(vector.GetData() + layout.offset(block), layout.size(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int index>
|
||||||
|
[[nodiscard]] mfem::Vector const_value_view(
|
||||||
|
const mfem::Vector &vector,
|
||||||
|
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||||
|
const mean_field::utils::blocks::value_block<index> block
|
||||||
|
) {
|
||||||
|
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + layout.offset(block), layout.size(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int index>
|
||||||
|
[[nodiscard]] mfem::Vector residual_view(
|
||||||
|
mfem::Vector &vector,
|
||||||
|
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||||
|
const mean_field::utils::blocks::residual_block<index> block
|
||||||
|
) {
|
||||||
|
return mfem::Vector(vector.GetData() + layout.offset(block), layout.size(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int index>
|
||||||
|
[[nodiscard]] mfem::Vector const_residual_view(
|
||||||
|
const mfem::Vector &vector,
|
||||||
|
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||||
|
const mean_field::utils::blocks::residual_block<index> block
|
||||||
|
) {
|
||||||
|
return mfem::Vector(const_cast<mfem::real_t *>(vector.GetData()) + layout.offset(block), layout.size(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int index>
|
||||||
|
void assign_value_block(
|
||||||
|
mfem::Vector &vector,
|
||||||
|
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||||
|
const mean_field::utils::blocks::value_block<index> block,
|
||||||
|
const mfem::Vector &source
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(source.Size() == layout.size(block), "Null-space experiment received a block with the wrong size.");
|
||||||
|
value_view(vector, layout, block) = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline double global_norm(
|
||||||
|
const mfem::Vector &vector,
|
||||||
|
const MPI_Comm communicator
|
||||||
|
) {
|
||||||
|
const double localNormSquared = vector * vector;
|
||||||
|
double globalNormSquared = 0.0;
|
||||||
|
MPI_Allreduce(&localNormSquared, &globalNormSquared, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||||
|
return std::sqrt(globalNormSquared);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void report_progress(
|
||||||
|
const MPI_Comm communicator,
|
||||||
|
const std::string &message
|
||||||
|
) {
|
||||||
|
int rank = 0;
|
||||||
|
MPI_Comm_rank(communicator, &rank);
|
||||||
|
if (rank == 0) {
|
||||||
|
std::cout << "[null-space experiment] " << message << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline mean_field::operators::StellarEquilibriumDependencies make_dependencies() {
|
||||||
|
return {
|
||||||
|
.discretization = {.identity = 2003, .revision = 1},
|
||||||
|
.density = {.identity = 2011, .revision = 1},
|
||||||
|
.displacement = {.identity = 2017, .revision = 1},
|
||||||
|
.gravityGradient = {.identity = 2027, .revision = 1},
|
||||||
|
.gravityPotential = {.identity = 2029, .revision = 1},
|
||||||
|
.enthalpy = {.identity = 2039, .revision = 1},
|
||||||
|
.bernoulliConstant = {.identity = 2053, .revision = 1},
|
||||||
|
.rotation = {.identity = 2063, .revision = 1},
|
||||||
|
.targetMass = {.identity = 2069, .revision = 1}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void increment_state_revisions(mean_field::operators::StellarEquilibriumDependencies &dependencies) {
|
||||||
|
++dependencies.density.revision;
|
||||||
|
++dependencies.displacement.revision;
|
||||||
|
++dependencies.gravityGradient.revision;
|
||||||
|
++dependencies.gravityPotential.revision;
|
||||||
|
++dependencies.enthalpy.revision;
|
||||||
|
++dependencies.bernoulliConstant.revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline mfem::Vector pack_gravity_state(
|
||||||
|
const mfem::Vector &density,
|
||||||
|
const mfem::Vector &displacement,
|
||||||
|
const mfem::Vector &gravityGradient,
|
||||||
|
const mfem::Vector &gravityPotential
|
||||||
|
) {
|
||||||
|
const std::array<int, 5> offsets{
|
||||||
|
0, density.Size(), density.Size() + displacement.Size(),
|
||||||
|
density.Size() + displacement.Size() + gravityGradient.Size(),
|
||||||
|
density.Size() + displacement.Size() + gravityGradient.Size() + gravityPotential.Size()
|
||||||
|
};
|
||||||
|
mfem::Vector packed(offsets.back());
|
||||||
|
mfem::Vector(packed.GetData() + offsets[0], density.Size()) = density;
|
||||||
|
mfem::Vector(packed.GetData() + offsets[1], displacement.Size()) = displacement;
|
||||||
|
mfem::Vector(packed.GetData() + offsets[2], gravityGradient.Size()) = gravityGradient;
|
||||||
|
mfem::Vector(packed.GetData() + offsets[3], gravityPotential.Size()) = gravityPotential;
|
||||||
|
return packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline Model make_model() {
|
||||||
|
const double pi = std::acos(-1.0);
|
||||||
|
const double targetMass = mean_field::utils::MASS;
|
||||||
|
constexpr double dimensionlessMass = 2.0182359509662283534;
|
||||||
|
const double polytropicConstant =
|
||||||
|
pi * mean_field::utils::G * std::pow(targetMass / (4.0 * pi * dimensionlessMass), 2.0 / 3.0);
|
||||||
|
|
||||||
|
return Model{
|
||||||
|
mean_field::models::structure::PolytropicStructure{
|
||||||
|
mean_field::eos::Polytrope{3.0, polytropicConstant}, targetMass
|
||||||
|
},
|
||||||
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.0}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class N3Equilibrium final {
|
||||||
|
public:
|
||||||
|
explicit N3Equilibrium(mean_field::utils::Args args)
|
||||||
|
: m_args(std::move(args)),
|
||||||
|
m_fem(
|
||||||
|
mean_field::fem::setup_fem(
|
||||||
|
m_args.mesh_file,
|
||||||
|
m_args,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
m_model(make_model()),
|
||||||
|
m_operator(
|
||||||
|
m_fem,
|
||||||
|
*m_fem.domainMapperStateless,
|
||||||
|
m_model
|
||||||
|
),
|
||||||
|
m_state(m_operator.GetLayout().value_offsets().Last()),
|
||||||
|
m_dependencies(make_dependencies()) {
|
||||||
|
MFEM_VERIFY(m_fem.okay(), "The null-space experiment could not construct the finite-element problem.");
|
||||||
|
m_state = 0.0;
|
||||||
|
initialize_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] mean_field::fem::FEM &fem() noexcept {
|
||||||
|
return m_fem;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const mean_field::fem::FEM &fem() const noexcept {
|
||||||
|
return m_fem;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] mean_field::operators::PreparedStellarEquilibriumOperator &stellar_operator() noexcept {
|
||||||
|
return m_operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const mean_field::operators::PreparedStellarEquilibriumOperator &
|
||||||
|
stellar_operator() const noexcept {
|
||||||
|
return m_operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const mfem::Vector &state() const noexcept {
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] mean_field::physics::RigidRotation rotation(const double fractionOfKeplerian) const {
|
||||||
|
const double radius = mean_field::utils::RADIUS;
|
||||||
|
const double mass = mean_field::utils::MASS;
|
||||||
|
const double keplerianSpeed = std::sqrt(mean_field::utils::G * mass / (radius * radius * radius));
|
||||||
|
|
||||||
|
mfem::Vector angularVelocity(3);
|
||||||
|
angularVelocity = 0.0;
|
||||||
|
angularVelocity(2) = fractionOfKeplerian * keplerianSpeed;
|
||||||
|
|
||||||
|
mfem::Vector center(3);
|
||||||
|
center = 0.0;
|
||||||
|
return mean_field::physics::RigidRotation(angularVelocity, center);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare(
|
||||||
|
const mfem::Vector &state,
|
||||||
|
const mean_field::physics::RigidRotation &rotation
|
||||||
|
) {
|
||||||
|
m_currentState = state;
|
||||||
|
increment_state_revisions(m_dependencies);
|
||||||
|
++m_dependencies.rotation.revision;
|
||||||
|
m_operator.Prepare(state, m_dependencies, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] mfem::Vector unpinned_residual() const {
|
||||||
|
const auto &layout = m_operator.GetLayout();
|
||||||
|
const mfem::Vector reducedDensity = const_value_view(m_currentState, layout, densityValue);
|
||||||
|
const mfem::Vector displacement = const_value_view(m_currentState, layout, displacementValue);
|
||||||
|
const mfem::Vector gravityGradient = const_value_view(m_currentState, layout, gravityGradientValue);
|
||||||
|
const mfem::Vector gravityPotential = const_value_view(m_currentState, layout, gravityPotentialValue);
|
||||||
|
const mfem::Vector gravityState =
|
||||||
|
pack_gravity_state(reducedDensity, displacement, gravityGradient, gravityPotential);
|
||||||
|
|
||||||
|
mfem::Vector gravity;
|
||||||
|
mfem::Vector closure;
|
||||||
|
mfem::Vector displacementRows;
|
||||||
|
mfem::Vector hydrostatic;
|
||||||
|
mfem::Vector mass;
|
||||||
|
m_operator.GetGravityOperator().Mult(gravityState, gravity);
|
||||||
|
m_operator.GetBarotropicClosureOperator().BuildResidual(closure);
|
||||||
|
m_operator.GetDisplacementOperator().BuildResidual(displacementRows);
|
||||||
|
m_operator.GetHydrostaticOperator().BuildResidual(hydrostatic);
|
||||||
|
m_operator.GetSurfaceConstraintOperator().ApplyResidualRows(hydrostatic);
|
||||||
|
m_operator.GetMassNormalizationOperator().BuildResidual(mass);
|
||||||
|
|
||||||
|
return pack_residual(gravity, closure, displacementRows, hydrostatic, mass);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] mfem::Vector unpinned_jacobian_action(const mfem::Vector &direction) const {
|
||||||
|
const auto &layout = m_operator.GetLayout();
|
||||||
|
const mfem::Vector reducedDensityDirection = const_value_view(direction, layout, densityValue);
|
||||||
|
const mfem::Vector displacementDirection = const_value_view(direction, layout, displacementValue);
|
||||||
|
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);
|
||||||
|
const mfem::Vector bernoulliDirection = const_value_view(direction, layout, bernoulliValue);
|
||||||
|
|
||||||
|
const mfem::Vector gravityDirection = pack_gravity_state(
|
||||||
|
reducedDensityDirection, displacementDirection, gravityGradientDirection, gravityPotentialDirection
|
||||||
|
);
|
||||||
|
|
||||||
|
mfem::Vector gravity;
|
||||||
|
mfem::Vector closure;
|
||||||
|
mfem::Vector displacementRows;
|
||||||
|
mfem::Vector hydrostatic;
|
||||||
|
mfem::Vector mass;
|
||||||
|
m_operator.GetGravityJacobianOperator().Mult(gravityDirection, gravity);
|
||||||
|
m_operator.GetBarotropicClosureOperator().Mult(
|
||||||
|
reducedDensityDirection, reducedEnthalpyDirection, displacementDirection, closure
|
||||||
|
);
|
||||||
|
m_operator.GetDisplacementOperator().ApplyCompleteJacobianAction(
|
||||||
|
reducedDensityDirection, displacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
|
||||||
|
displacementRows
|
||||||
|
);
|
||||||
|
m_operator.GetHydrostaticOperator().ApplyCompleteJacobianAction(
|
||||||
|
reducedEnthalpyDirection, gravityPotentialDirection, bernoulliDirection(0), displacementDirection,
|
||||||
|
hydrostatic
|
||||||
|
);
|
||||||
|
m_operator.GetSurfaceConstraintOperator().ApplyJacobianRows(reducedEnthalpyDirection, hydrostatic);
|
||||||
|
m_operator.GetMassNormalizationOperator().ApplyCompleteJacobianAction(
|
||||||
|
reducedDensityDirection, displacementDirection, mass
|
||||||
|
);
|
||||||
|
|
||||||
|
return pack_residual(gravity, closure, displacementRows, hydrostatic, mass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initialize_state() {
|
||||||
|
report_progress(m_fem.mesh->GetComm(), "constructing the analytic n=3 Lane-Emden state");
|
||||||
|
|
||||||
|
constexpr double surfaceCoordinate = 6.8968486193769603755;
|
||||||
|
constexpr int radialSampleCount = 8192;
|
||||||
|
const double pi = std::acos(-1.0);
|
||||||
|
const double radius = mean_field::utils::RADIUS;
|
||||||
|
const double targetMass = mean_field::utils::MASS;
|
||||||
|
constexpr double dimensionlessMass = 2.0182359509662283534;
|
||||||
|
const double polytropicConstant =
|
||||||
|
pi * mean_field::utils::G * std::pow(targetMass / (4.0 * pi * dimensionlessMass), 2.0 / 3.0);
|
||||||
|
const double centralDensity =
|
||||||
|
std::pow(surfaceCoordinate * std::sqrt(polytropicConstant / (pi * mean_field::utils::G)) / radius, 3.0);
|
||||||
|
|
||||||
|
const mean_field::models::structure::StructureSeed seed =
|
||||||
|
m_model.makeInitialSeed({.centralDensity = centralDensity, .radialSampleCount = radialSampleCount});
|
||||||
|
|
||||||
|
const auto interpolate = [](const mfem::Vector &radii, const mfem::Vector &values, const double r) {
|
||||||
|
if (r <= radii(0)) {
|
||||||
|
return values(0);
|
||||||
|
}
|
||||||
|
const int finalIndex = radii.Size() - 1;
|
||||||
|
if (r >= radii(finalIndex)) {
|
||||||
|
return values(finalIndex);
|
||||||
|
}
|
||||||
|
int lower = 0;
|
||||||
|
int upper = finalIndex;
|
||||||
|
while (upper - lower > 1) {
|
||||||
|
const int middle = lower + (upper - lower) / 2;
|
||||||
|
if (radii(middle) <= r) {
|
||||||
|
lower = middle;
|
||||||
|
} else {
|
||||||
|
upper = middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const double fraction = (r - radii(lower)) / (radii(upper) - radii(lower));
|
||||||
|
return (1.0 - fraction) * values(lower) + fraction * values(upper);
|
||||||
|
};
|
||||||
|
|
||||||
|
mfem::FunctionCoefficient densityCoefficient([&seed, &interpolate](const mfem::Vector &position) {
|
||||||
|
const double r = position.Norml2();
|
||||||
|
return r >= seed.stellarRadius ? 0.0 : interpolate(seed.radius, seed.density, r);
|
||||||
|
});
|
||||||
|
mfem::FunctionCoefficient enthalpyCoefficient([&seed, &interpolate](const mfem::Vector &position) {
|
||||||
|
const double r = position.Norml2();
|
||||||
|
return r >= seed.stellarRadius ? 0.0 : interpolate(seed.radius, seed.enthalpy, r);
|
||||||
|
});
|
||||||
|
|
||||||
|
mfem::ParGridFunction densityField(m_fem.densityFes.get());
|
||||||
|
mfem::ParGridFunction enthalpyField(m_fem.enthalpyFes.get());
|
||||||
|
mfem::ParGridFunction displacementField(m_fem.displacementFes.get());
|
||||||
|
densityField = 0.0;
|
||||||
|
enthalpyField = 0.0;
|
||||||
|
displacementField = 0.0;
|
||||||
|
densityField.ProjectCoefficient(densityCoefficient);
|
||||||
|
enthalpyField.ProjectCoefficient(enthalpyCoefficient);
|
||||||
|
*m_fem.displacement = displacementField;
|
||||||
|
|
||||||
|
report_progress(m_fem.mesh->GetComm(), "solving the gravity field for the seed state");
|
||||||
|
const mean_field::physics::GravitySolution gravity =
|
||||||
|
mean_field::physics::solve_gravity_field(m_fem, m_args, densityField, displacementField);
|
||||||
|
|
||||||
|
mfem::Vector densityTrue;
|
||||||
|
mfem::Vector enthalpyTrue;
|
||||||
|
mfem::Vector displacementTrue;
|
||||||
|
mfem::Vector gravityGradientTrue;
|
||||||
|
mfem::Vector gravityPotentialTrue;
|
||||||
|
densityField.GetTrueDofs(densityTrue);
|
||||||
|
enthalpyField.GetTrueDofs(enthalpyTrue);
|
||||||
|
displacementField.GetTrueDofs(displacementTrue);
|
||||||
|
gravity.gradPhi.GetTrueDofs(gravityGradientTrue);
|
||||||
|
gravity.phi.GetTrueDofs(gravityPotentialTrue);
|
||||||
|
|
||||||
|
const auto &layout = m_operator.GetLayout();
|
||||||
|
const mean_field::field::FieldDofMap densityMap =
|
||||||
|
mean_field::field::make_field_dof_map<mean_field::field::Density, DomainSchema>(*m_fem.densityFes);
|
||||||
|
const mean_field::field::FieldDofMap enthalpyMap =
|
||||||
|
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy, DomainSchema>(*m_fem.enthalpyFes);
|
||||||
|
|
||||||
|
assign_value_block(m_state, layout, densityValue, densityMap.gather(densityTrue));
|
||||||
|
assign_value_block(m_state, layout, displacementValue, displacementTrue);
|
||||||
|
assign_value_block(m_state, layout, gravityGradientValue, gravityGradientTrue);
|
||||||
|
assign_value_block(m_state, layout, gravityPotentialValue, gravityPotentialTrue);
|
||||||
|
assign_value_block(m_state, layout, enthalpyValue, enthalpyMap.gather(enthalpyTrue));
|
||||||
|
value_view(m_state, layout, bernoulliValue)(0) = -mean_field::utils::G * targetMass / radius;
|
||||||
|
|
||||||
|
m_currentState = m_state;
|
||||||
|
prepare(m_state, rotation(0.0));
|
||||||
|
report_progress(m_fem.mesh->GetComm(), "analytic state is prepared");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] mfem::Vector pack_residual(
|
||||||
|
const mfem::Vector &gravity,
|
||||||
|
const mfem::Vector &closure,
|
||||||
|
const mfem::Vector &displacementRows,
|
||||||
|
const mfem::Vector &hydrostatic,
|
||||||
|
const mfem::Vector &mass
|
||||||
|
) const {
|
||||||
|
const auto &layout = m_operator.GetLayout();
|
||||||
|
mfem::Vector result(layout.residual_offsets().Last());
|
||||||
|
result = 0.0;
|
||||||
|
const mfem::Vector gravityGradient(gravity.GetData(), layout.size(gravityGradientResidual));
|
||||||
|
const mfem::Vector gravityPotential(
|
||||||
|
gravity.GetData() + layout.size(gravityGradientResidual), layout.size(gravityPotentialResidual)
|
||||||
|
);
|
||||||
|
residual_view(result, layout, gravityGradientResidual) = gravityGradient;
|
||||||
|
residual_view(result, layout, gravityPotentialResidual) = gravityPotential;
|
||||||
|
residual_view(result, layout, densityResidual) = closure;
|
||||||
|
residual_view(result, layout, displacementResidual) = displacementRows;
|
||||||
|
residual_view(result, layout, enthalpyResidual) = hydrostatic;
|
||||||
|
residual_view(result, layout, massResidual) = mass;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
mean_field::utils::Args m_args;
|
||||||
|
mean_field::fem::FEM m_fem;
|
||||||
|
Model m_model;
|
||||||
|
mean_field::operators::PreparedStellarEquilibriumOperator m_operator;
|
||||||
|
mfem::Vector m_state;
|
||||||
|
mfem::Vector m_currentState;
|
||||||
|
mean_field::operators::StellarEquilibriumDependencies m_dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class RigidModeKind : std::uint8_t { translation, rotation };
|
||||||
|
|
||||||
|
struct RigidMode final {
|
||||||
|
std::string name;
|
||||||
|
RigidModeKind kind;
|
||||||
|
int axis;
|
||||||
|
mfem::Vector direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::array<
|
||||||
|
RigidMode,
|
||||||
|
6>
|
||||||
|
make_rigid_modes(const N3Equilibrium &fixture) {
|
||||||
|
const auto &fem = fixture.fem();
|
||||||
|
const auto &layout = fixture.stellar_operator().GetLayout();
|
||||||
|
std::array<RigidMode, 6> modes;
|
||||||
|
|
||||||
|
for (int axis = 0; axis < 3; ++axis) {
|
||||||
|
mfem::ParGridFunction translation(fem.displacementFes.get());
|
||||||
|
mfem::Vector translationValue(3);
|
||||||
|
translationValue = 0.0;
|
||||||
|
translationValue(axis) = 1.0;
|
||||||
|
mfem::VectorConstantCoefficient coefficient(translationValue);
|
||||||
|
translation.ProjectCoefficient(coefficient);
|
||||||
|
mfem::Vector translationTrue;
|
||||||
|
translation.GetTrueDofs(translationTrue);
|
||||||
|
|
||||||
|
mfem::Vector direction(layout.value_offsets().Last());
|
||||||
|
direction = 0.0;
|
||||||
|
assign_value_block(direction, layout, displacementValue, translationTrue);
|
||||||
|
modes[axis] = RigidMode{
|
||||||
|
.name = std::string("translation_") + static_cast<char>('x' + axis),
|
||||||
|
.kind = RigidModeKind::translation,
|
||||||
|
.axis = axis,
|
||||||
|
.direction = std::move(direction)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int axis = 0; axis < 3; ++axis) {
|
||||||
|
mfem::ParGridFunction rotation(fem.displacementFes.get());
|
||||||
|
mfem::VectorFunctionCoefficient coefficient(3, [axis](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
|
value.SetSize(3);
|
||||||
|
value = 0.0;
|
||||||
|
const int first = (axis + 1) % 3;
|
||||||
|
const int second = (axis + 2) % 3;
|
||||||
|
value(first) = -position(second);
|
||||||
|
value(second) = position(first);
|
||||||
|
});
|
||||||
|
rotation.ProjectCoefficient(coefficient);
|
||||||
|
mfem::Vector rotationTrue;
|
||||||
|
rotation.GetTrueDofs(rotationTrue);
|
||||||
|
|
||||||
|
mfem::Vector direction(layout.value_offsets().Last());
|
||||||
|
direction = 0.0;
|
||||||
|
assign_value_block(direction, layout, displacementValue, rotationTrue);
|
||||||
|
modes[3 + axis] = RigidMode{
|
||||||
|
.name = std::string("rotation_") + static_cast<char>('x' + axis),
|
||||||
|
.kind = RigidModeKind::rotation,
|
||||||
|
.axis = axis,
|
||||||
|
.direction = std::move(direction)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return modes;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::array<
|
||||||
|
double,
|
||||||
|
6>
|
||||||
|
residual_block_norms(
|
||||||
|
const mfem::Vector &action,
|
||||||
|
const mean_field::operators::StellarEquilibriumLayout &layout,
|
||||||
|
const MPI_Comm communicator
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
global_norm(const_residual_view(action, layout, gravityGradientResidual), communicator),
|
||||||
|
global_norm(const_residual_view(action, layout, gravityPotentialResidual), communicator),
|
||||||
|
global_norm(const_residual_view(action, layout, densityResidual), communicator),
|
||||||
|
global_norm(const_residual_view(action, layout, displacementResidual), communicator),
|
||||||
|
global_norm(const_residual_view(action, layout, enthalpyResidual), communicator),
|
||||||
|
global_norm(const_residual_view(action, layout, massResidual), communicator)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} // namespace experiment::null_space
|
||||||
@@ -14,20 +14,23 @@ namespace {
|
|||||||
) {
|
) {
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
case mean_field::utils::DOMAINS::CORE:
|
case mean_field::utils::DOMAINS::CORE:
|
||||||
return mean_field::utils::domain::make_attribute_marker<
|
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::Core, DomainSchema>(
|
||||||
mean_field::utils::domain::Core, DomainSchema>(mesh);
|
mesh
|
||||||
|
);
|
||||||
case mean_field::utils::DOMAINS::ENVELOPE:
|
case mean_field::utils::DOMAINS::ENVELOPE:
|
||||||
return mean_field::utils::domain::make_attribute_marker<
|
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::Envelope, DomainSchema>(
|
||||||
mean_field::utils::domain::Envelope, DomainSchema>(mesh);
|
mesh
|
||||||
|
);
|
||||||
case mean_field::utils::DOMAINS::ALL:
|
case mean_field::utils::DOMAINS::ALL:
|
||||||
return mean_field::utils::domain::make_attribute_marker<
|
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::All, DomainSchema>(mesh);
|
||||||
mean_field::utils::domain::All, DomainSchema>(mesh);
|
|
||||||
case mean_field::utils::DOMAINS::STELLAR:
|
case mean_field::utils::DOMAINS::STELLAR:
|
||||||
return mean_field::utils::domain::make_attribute_marker<
|
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::Stellar, DomainSchema>(
|
||||||
mean_field::utils::domain::Stellar, DomainSchema>(mesh);
|
mesh
|
||||||
|
);
|
||||||
case mean_field::utils::DOMAINS::VACUUM:
|
case mean_field::utils::DOMAINS::VACUUM:
|
||||||
return mean_field::utils::domain::make_attribute_marker<
|
return mean_field::utils::domain::make_attribute_marker<mean_field::utils::domain::Vacuum, DomainSchema>(
|
||||||
mean_field::utils::domain::Vacuum, DomainSchema>(mesh);
|
mesh
|
||||||
|
);
|
||||||
}
|
}
|
||||||
MFEM_ABORT("Unsupported integration domain.");
|
MFEM_ABORT("Unsupported integration domain.");
|
||||||
}
|
}
|
||||||
@@ -69,8 +72,7 @@ namespace mean_field::analysis {
|
|||||||
|
|
||||||
if (fem.has_mapping() && coord_space == mapping::COORDINATE_SPACE::PHYSICAL) {
|
if (fem.has_mapping() && coord_space == mapping::COORDINATE_SPACE::PHYSICAL) {
|
||||||
mapping::MappedScalarCoefficient mapped_gf_c(
|
mapping::MappedScalarCoefficient mapped_gf_c(
|
||||||
*fem.domainMapperStateless, *fem.displacement,
|
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate, gf_c
|
||||||
*fem.compactificationCoordinate, gf_c
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// ReSharper disable once CppDFAMemoryLeak // Disabled because MFEM
|
// ReSharper disable once CppDFAMemoryLeak // Disabled because MFEM
|
||||||
@@ -107,16 +109,14 @@ namespace mean_field::analysis {
|
|||||||
) {
|
) {
|
||||||
const int dim = fem.mesh->Dimension();
|
const int dim = fem.mesh->Dimension();
|
||||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||||
*fem.domainMapperStateless, *fem.displacement,
|
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||||
*fem.compactificationCoordinate
|
|
||||||
);
|
);
|
||||||
mfem::Vector local_com(dim);
|
mfem::Vector local_com(dim);
|
||||||
local_com = 0.0;
|
local_com = 0.0;
|
||||||
double local_mass = 0.0;
|
double local_mass = 0.0;
|
||||||
|
|
||||||
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
|
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
|
||||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
|
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(fem.mesh->GetAttribute(i)))
|
||||||
fem.mesh->GetAttribute(i)))
|
|
||||||
continue;
|
continue;
|
||||||
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
||||||
const mfem::IntegrationRule &ir = get_density_rule<field::Density::Form::CenterOfMass>(
|
const mfem::IntegrationRule &ir = get_density_rule<field::Density::Form::CenterOfMass>(
|
||||||
@@ -129,8 +129,7 @@ namespace mean_field::analysis {
|
|||||||
|
|
||||||
mapping::VolumeMappingContext mapping_context;
|
mapping::VolumeMappingContext mapping_context;
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) ==
|
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) == mapping::MappingStatus::valid,
|
||||||
mapping::MappingStatus::valid,
|
|
||||||
"Center-of-mass integration encountered an invalid mapping."
|
"Center-of-mass integration encountered an invalid mapping."
|
||||||
);
|
);
|
||||||
const double weight = mapping_context.quadrature.weight;
|
const double weight = mapping_context.quadrature.weight;
|
||||||
@@ -183,8 +182,7 @@ namespace mean_field::analysis {
|
|||||||
std::unique_ptr<mfem::Coefficient> s2_coeff;
|
std::unique_ptr<mfem::Coefficient> s2_coeff;
|
||||||
if (fem.has_mapping()) {
|
if (fem.has_mapping()) {
|
||||||
s2_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(
|
s2_coeff = std::make_unique<mapping::PhysicalPositionFunctionCoefficient>(
|
||||||
*fem.domainMapperStateless, *fem.displacement,
|
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate, s2_func
|
||||||
*fem.compactificationCoordinate, s2_func
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
s2_coeff = std::make_unique<mfem::FunctionCoefficient>(s2_func);
|
s2_coeff = std::make_unique<mfem::FunctionCoefficient>(s2_func);
|
||||||
@@ -204,8 +202,7 @@ namespace mean_field::analysis {
|
|||||||
double local_I = 0.0;
|
double local_I = 0.0;
|
||||||
if (fem.has_mapping()) {
|
if (fem.has_mapping()) {
|
||||||
mapping::MappedScalarCoefficient mapped_integrand(
|
mapping::MappedScalarCoefficient mapped_integrand(
|
||||||
*fem.domainMapperStateless, *fem.displacement,
|
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate, I_integrand
|
||||||
*fem.compactificationCoordinate, I_integrand
|
|
||||||
);
|
);
|
||||||
auto *integrator = new mfem::DomainLFIntegrator(mapped_integrand);
|
auto *integrator = new mfem::DomainLFIntegrator(mapped_integrand);
|
||||||
integrator->SetIntRule(&integration_rule);
|
integrator->SetIntRule(&integration_rule);
|
||||||
@@ -239,14 +236,12 @@ namespace mean_field::analysis {
|
|||||||
|
|
||||||
double local_volume = 0.0;
|
double local_volume = 0.0;
|
||||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||||
*fem.domainMapperStateless, *fem.displacement,
|
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||||
*fem.compactificationCoordinate
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for (int e = 0; e < mesh.GetNE(); ++e) {
|
for (int e = 0; e < mesh.GetNE(); ++e) {
|
||||||
const int attr = mesh.GetAttribute(e);
|
const int attr = mesh.GetAttribute(e);
|
||||||
const bool selected =
|
const bool selected = domain == utils::DOMAINS::ALL ||
|
||||||
domain == utils::DOMAINS::ALL ||
|
|
||||||
(domain == utils::DOMAINS::STELLAR &&
|
(domain == utils::DOMAINS::STELLAR &&
|
||||||
DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(attr)) ||
|
DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(attr)) ||
|
||||||
(domain == utils::DOMAINS::VACUUM &&
|
(domain == utils::DOMAINS::VACUUM &&
|
||||||
@@ -266,8 +261,7 @@ namespace mean_field::analysis {
|
|||||||
if (physical) {
|
if (physical) {
|
||||||
mapping::VolumeMappingContext context;
|
mapping::VolumeMappingContext context;
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
mapping_evaluator.EvaluateVolume(*T, ip, context) ==
|
mapping_evaluator.EvaluateVolume(*T, ip, context) == mapping::MappingStatus::valid,
|
||||||
mapping::MappingStatus::valid,
|
|
||||||
"Mesh-volume integration encountered an invalid mapping."
|
"Mesh-volume integration encountered an invalid mapping."
|
||||||
);
|
);
|
||||||
dV = context.quadrature.weight;
|
dV = context.quadrature.weight;
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ import :utils.misc;
|
|||||||
import :utils.user;
|
import :utils.user;
|
||||||
|
|
||||||
namespace mean_field::fem {
|
namespace mean_field::fem {
|
||||||
FEM setup_fem(const std::string &filename, const utils::Args &args,
|
FEM setup_fem(
|
||||||
const int extraRefine) {
|
const std::string &filename,
|
||||||
|
const utils::Args &args,
|
||||||
|
const int extraRefine
|
||||||
|
) {
|
||||||
FEM fem;
|
FEM fem;
|
||||||
|
|
||||||
using GravityPotential = field::Gravity::Potential;
|
using GravityPotential = field::Gravity::Potential;
|
||||||
@@ -45,11 +48,9 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
int mpiSize = 1;
|
int mpiSize = 1;
|
||||||
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
|
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
|
||||||
|
|
||||||
const std::unique_ptr<int[]> meshPartitioning(
|
const std::unique_ptr<int[]> meshPartitioning(fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
|
||||||
fem.smesh.mesh->GeneratePartitioning(mpiSize, 1));
|
|
||||||
|
|
||||||
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh,
|
fem.mesh = std::make_unique<mfem::ParMesh>(MPI_COMM_WORLD, *fem.smesh.mesh, meshPartitioning.get(), 1);
|
||||||
meshPartitioning.get(), 1);
|
|
||||||
|
|
||||||
fem.mesh->EnsureNodes();
|
fem.mesh->EnsureNodes();
|
||||||
|
|
||||||
@@ -69,22 +70,22 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
throw std::runtime_error("Values for exterior coordinate not set.");
|
throw std::runtime_error("Values for exterior coordinate not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::FiniteElementSpace &serialCoordinateSpace =
|
const mfem::FiniteElementSpace &serialCoordinateSpace = *fem.smesh.exterior_coordinate->space;
|
||||||
*fem.smesh.exterior_coordinate->space;
|
|
||||||
|
|
||||||
const mfem::GridFunction &serialCoordinate =
|
const mfem::GridFunction &serialCoordinate = *fem.smesh.exterior_coordinate->values;
|
||||||
*fem.smesh.exterior_coordinate->values;
|
|
||||||
|
|
||||||
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
|
if (serialCoordinate.FESpace() != &serialCoordinateSpace) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Exterior coordinate values are not associated with the "
|
"Exterior coordinate values are not associated with the "
|
||||||
"supplied finite-element space.");
|
"supplied finite-element space."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
|
if (serialCoordinateSpace.GetMesh() != fem.smesh.mesh.get()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Exterior coordinate space is not associated with the "
|
"Exterior coordinate space is not associated with the "
|
||||||
"loaded STROID mesh.");
|
"loaded STROID mesh."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serialCoordinateSpace.GetVDim() != 1) {
|
if (serialCoordinateSpace.GetVDim() != 1) {
|
||||||
@@ -94,30 +95,29 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
|
if (serialCoordinate.Size() != serialCoordinateSpace.GetVSize()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Exterior coordinate value count does not match its "
|
"Exterior coordinate value count does not match its "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder();
|
const int compactificationOrder = serialCoordinateSpace.GetMaxElementOrder();
|
||||||
|
|
||||||
const int dimension = fem.mesh->Dimension();
|
const int dimension = fem.mesh->Dimension();
|
||||||
|
|
||||||
fem.compactificationFec =
|
fem.compactificationFec = std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
|
||||||
std::make_unique<mfem::H1_FECollection>(compactificationOrder, dimension);
|
|
||||||
|
|
||||||
fem.compactificationFes = std::make_unique<mfem::ParFiniteElementSpace>(
|
fem.compactificationFes =
|
||||||
fem.mesh.get(), fem.compactificationFec.get());
|
std::make_unique<mfem::ParFiniteElementSpace>(fem.mesh.get(), fem.compactificationFec.get());
|
||||||
|
|
||||||
mfem::ParGridFunction distributedCoordinate(fem.mesh.get(), &serialCoordinate,
|
mfem::ParGridFunction distributedCoordinate(fem.mesh.get(), &serialCoordinate, meshPartitioning.get());
|
||||||
meshPartitioning.get());
|
|
||||||
|
|
||||||
if (distributedCoordinate.Size() != fem.compactificationFes->GetVSize()) {
|
if (distributedCoordinate.Size() != fem.compactificationFes->GetVSize()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Distributed exterior coordinate does not match the "
|
"Distributed exterior coordinate does not match the "
|
||||||
"constructed parallel finite-element space.");
|
"constructed parallel finite-element space."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fem.compactificationCoordinate =
|
fem.compactificationCoordinate = std::make_unique<mfem::ParGridFunction>(fem.compactificationFes.get());
|
||||||
std::make_unique<mfem::ParGridFunction>(fem.compactificationFes.get());
|
|
||||||
|
|
||||||
*fem.compactificationCoordinate = distributedCoordinate;
|
*fem.compactificationCoordinate = distributedCoordinate;
|
||||||
|
|
||||||
@@ -129,8 +129,7 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
const double value = (*fem.compactificationCoordinate)(index);
|
const double value = (*fem.compactificationCoordinate)(index);
|
||||||
|
|
||||||
if (!std::isfinite(value)) {
|
if (!std::isfinite(value)) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Exterior coordinate contains a non-finite value.");
|
||||||
"Exterior coordinate contains a non-finite value.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
localMinimum = std::min(localMinimum, value);
|
localMinimum = std::min(localMinimum, value);
|
||||||
@@ -141,18 +140,17 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
double globalMinimum = 0.0;
|
double globalMinimum = 0.0;
|
||||||
double globalMaximum = 0.0;
|
double globalMaximum = 0.0;
|
||||||
|
|
||||||
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN,
|
MPI_Allreduce(&localMinimum, &globalMinimum, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
|
||||||
MPI_COMM_WORLD);
|
|
||||||
|
|
||||||
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX,
|
MPI_Allreduce(&localMaximum, &globalMaximum, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
|
||||||
MPI_COMM_WORLD);
|
|
||||||
|
|
||||||
constexpr double coordinateTolerance = 1.0e-12;
|
constexpr double coordinateTolerance = 1.0e-12;
|
||||||
|
|
||||||
if (globalMinimum < -coordinateTolerance ||
|
if (globalMinimum < -coordinateTolerance || globalMaximum > 1.0 + coordinateTolerance) {
|
||||||
globalMaximum > 1.0 + coordinateTolerance) {
|
throw std::runtime_error(
|
||||||
throw std::runtime_error("Exterior coordinate lies outside the expected "
|
"Exterior coordinate lies outside the expected "
|
||||||
"interval [0, 1].");
|
"interval [0, 1]."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
@@ -165,8 +163,7 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
|
|
||||||
fem.gravityPotentialFec = GravityField::make_fec<GravityPotential>(dimension);
|
fem.gravityPotentialFec = GravityField::make_fec<GravityPotential>(dimension);
|
||||||
|
|
||||||
fem.gravityPotentialFes = GravityField::make_fespace<GravityPotential>(
|
fem.gravityPotentialFes = GravityField::make_fespace<GravityPotential>(*fem.mesh, *fem.gravityPotentialFec);
|
||||||
*fem.mesh, *fem.gravityPotentialFec);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Gravity flux: H(div)/RT. Basis choices are encoded by field.mfem.
|
// Gravity flux: H(div)/RT. Basis choices are encoded by field.mfem.
|
||||||
@@ -174,21 +171,17 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
|
|
||||||
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
|
fem.gravityFluxFec = GravityField::make_fec<GravityFlux>(dimension);
|
||||||
|
|
||||||
fem.gravityFluxFes =
|
fem.gravityFluxFes = GravityField::make_fespace<GravityFlux>(*fem.mesh, *fem.gravityFluxFec);
|
||||||
GravityField::make_fespace<GravityFlux>(*fem.mesh, *fem.gravityFluxFec);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Displacement: vector H1. Ordering is encoded by field.mfem.
|
// Displacement: vector H1. Ordering is encoded by field.mfem.
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
fem.displacementFec =
|
fem.displacementFec = DisplacementField::make_fec<DisplacementVector>(dimension);
|
||||||
DisplacementField::make_fec<DisplacementVector>(dimension);
|
|
||||||
|
|
||||||
fem.displacementFes = DisplacementField::make_fespace<DisplacementVector>(
|
fem.displacementFes = DisplacementField::make_fespace<DisplacementVector>(*fem.mesh, *fem.displacementFec);
|
||||||
*fem.mesh, *fem.displacementFec);
|
|
||||||
|
|
||||||
fem.displacement =
|
fem.displacement = std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
|
||||||
std::make_unique<mfem::ParGridFunction>(fem.displacementFes.get());
|
|
||||||
|
|
||||||
*fem.displacement = 0.0;
|
*fem.displacement = 0.0;
|
||||||
|
|
||||||
@@ -198,8 +191,7 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
|
|
||||||
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
|
fem.densityFec = DensityField::make_fec<DensityScalar>(dimension);
|
||||||
|
|
||||||
fem.densityFes =
|
fem.densityFes = DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
|
||||||
DensityField::make_fespace<DensityScalar>(*fem.mesh, *fem.densityFec);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Specific enthalpy: scalar continuous H1
|
// Specific enthalpy: scalar continuous H1
|
||||||
@@ -207,8 +199,7 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
|
|
||||||
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
|
fem.enthalpyFec = EnthalpyField::make_fec<EnthalpyScalar>(dimension);
|
||||||
|
|
||||||
fem.enthalpyFes =
|
fem.enthalpyFes = EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
|
||||||
EnthalpyField::make_fespace<EnthalpyScalar>(*fem.mesh, *fem.enthalpyFec);
|
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
// Section 4: Multipole data
|
// Section 4: Multipole data
|
||||||
@@ -233,12 +224,9 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
fem.boundaryContext.inf_bounds = 0;
|
fem.boundaryContext.inf_bounds = 0;
|
||||||
fem.boundaryContext.stellar_bounds = 0;
|
fem.boundaryContext.stellar_bounds = 0;
|
||||||
|
|
||||||
fem.boundaryContext
|
fem.boundaryContext.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
|
||||||
.inf_bounds[static_cast<int>(boundary::Boundaries::INF_SURFACE) - 1] = 1;
|
|
||||||
|
|
||||||
fem.boundaryContext
|
fem.boundaryContext.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) - 1] = 1;
|
||||||
.stellar_bounds[static_cast<int>(boundary::Boundaries::STELLAR_SURFACE) -
|
|
||||||
1] = 1;
|
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
// Section 7: Quadrature policy
|
// Section 7: Quadrature policy
|
||||||
@@ -246,34 +234,30 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
|
|
||||||
const quadrature::QuadratureOptions &quadratureOptions = args.quadrature;
|
const quadrature::QuadratureOptions &quadratureOptions = args.quadrature;
|
||||||
|
|
||||||
if (quadratureOptions.validation.reject_negative_boosts &&
|
if (quadratureOptions.validation.reject_negative_boosts && quadratureOptions.global_boost < 0) {
|
||||||
quadratureOptions.global_boost < 0) {
|
|
||||||
throw std::invalid_argument("Global quadrature boost cannot be negative.");
|
throw std::invalid_argument("Global quadrature boost cannot be negative.");
|
||||||
}
|
}
|
||||||
|
|
||||||
quadrature::RuleSet quadratureRuleSet = quadrature::make_rule_set(
|
quadrature::RuleSet quadratureRuleSet =
|
||||||
quadratureOptions.mode, quadratureOptions.global_boost);
|
quadrature::make_rule_set(quadratureOptions.mode, quadratureOptions.global_boost);
|
||||||
|
|
||||||
if (quadratureOptions.fallback_fixed_order.has_value()) {
|
if (quadratureOptions.fallback_fixed_order.has_value()) {
|
||||||
if (*quadratureOptions.fallback_fixed_order < 0) {
|
if (*quadratureOptions.fallback_fixed_order < 0) {
|
||||||
throw std::invalid_argument(
|
throw std::invalid_argument("Fallback quadrature order cannot be negative.");
|
||||||
"Fallback quadrature order cannot be negative.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quadratureRuleSet.fallback.fixed_order =
|
quadratureRuleSet.fallback.fixed_order = quadratureOptions.fallback_fixed_order;
|
||||||
quadratureOptions.fallback_fixed_order;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto apply_quadrature_options = [&quadratureOptions](
|
auto apply_quadrature_options = [&quadratureOptions](
|
||||||
quadrature::RuleControl &ruleControl,
|
quadrature::RuleControl &ruleControl,
|
||||||
const quadrature::QuadratureTermOptions
|
const quadrature::QuadratureTermOptions &termOptions
|
||||||
&termOptions) {
|
) {
|
||||||
if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) {
|
if (termOptions.fixed_order.has_value() && *termOptions.fixed_order < 0) {
|
||||||
throw std::invalid_argument("Fixed quadrature order cannot be negative.");
|
throw std::invalid_argument("Fixed quadrature order cannot be negative.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quadratureOptions.validation.reject_negative_boosts &&
|
if (quadratureOptions.validation.reject_negative_boosts && termOptions.additional_boost < 0) {
|
||||||
termOptions.additional_boost < 0) {
|
|
||||||
throw std::invalid_argument("Term quadrature boost cannot be negative.");
|
throw std::invalid_argument("Term quadrature boost cannot be negative.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,98 +268,75 @@ FEM setup_fem(const std::string &filename, const utils::Args &args,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.gravity_hdiv_mass,
|
apply_quadrature_options(quadratureRuleSet.gravity_hdiv_mass, quadratureOptions.gravity_hdiv_mass);
|
||||||
quadratureOptions.gravity_hdiv_mass);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.gravity_divergence,
|
apply_quadrature_options(quadratureRuleSet.gravity_divergence, quadratureOptions.gravity_divergence);
|
||||||
quadratureOptions.gravity_divergence);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.gravity_source,
|
apply_quadrature_options(quadratureRuleSet.gravity_source, quadratureOptions.gravity_source);
|
||||||
quadratureOptions.gravity_source);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.gravity_force,
|
apply_quadrature_options(quadratureRuleSet.gravity_force, quadratureOptions.gravity_force);
|
||||||
quadratureOptions.gravity_force);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.gravity_boundary,
|
apply_quadrature_options(quadratureRuleSet.gravity_boundary, quadratureOptions.gravity_boundary);
|
||||||
quadratureOptions.gravity_boundary);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.centrifugal,
|
apply_quadrature_options(quadratureRuleSet.centrifugal, quadratureOptions.centrifugal);
|
||||||
quadratureOptions.centrifugal);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.density_projection,
|
apply_quadrature_options(quadratureRuleSet.density_projection, quadratureOptions.density_projection);
|
||||||
quadratureOptions.density_projection);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.eos_closure,
|
apply_quadrature_options(quadratureRuleSet.eos_closure, quadratureOptions.eos_closure);
|
||||||
quadratureOptions.eos_closure);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium,
|
apply_quadrature_options(quadratureRuleSet.hydrostatic_equilibrium, quadratureOptions.hydrostatic_equilibrium);
|
||||||
quadratureOptions.hydrostatic_equilibrium);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.isobaric_surface,
|
apply_quadrature_options(quadratureRuleSet.isobaric_surface, quadratureOptions.isobaric_surface);
|
||||||
quadratureOptions.isobaric_surface);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.mesh_extension,
|
apply_quadrature_options(quadratureRuleSet.mesh_extension, quadratureOptions.mesh_extension);
|
||||||
quadratureOptions.mesh_extension);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.mass_conservation,
|
apply_quadrature_options(quadratureRuleSet.mass_conservation, quadratureOptions.mass_conservation);
|
||||||
quadratureOptions.mass_conservation);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.mass_normalization,
|
apply_quadrature_options(quadratureRuleSet.mass_normalization, quadratureOptions.mass_normalization);
|
||||||
quadratureOptions.mass_normalization);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.center_of_mass,
|
apply_quadrature_options(quadratureRuleSet.center_of_mass, quadratureOptions.center_of_mass);
|
||||||
quadratureOptions.center_of_mass);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.quadrupole,
|
apply_quadrature_options(quadratureRuleSet.quadrupole, quadratureOptions.quadrupole);
|
||||||
quadratureOptions.quadrupole);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.gravitational_energy,
|
apply_quadrature_options(quadratureRuleSet.gravitational_energy, quadratureOptions.gravitational_energy);
|
||||||
quadratureOptions.gravitational_energy);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.pressure_integral,
|
apply_quadrature_options(quadratureRuleSet.pressure_integral, quadratureOptions.pressure_integral);
|
||||||
quadratureOptions.pressure_integral);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.pressure_force,
|
apply_quadrature_options(quadratureRuleSet.pressure_force, quadratureOptions.pressure_force);
|
||||||
quadratureOptions.pressure_force);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
|
apply_quadrature_options(quadratureRuleSet.virial, quadratureOptions.virial);
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.error_norm,
|
apply_quadrature_options(quadratureRuleSet.error_norm, quadratureOptions.error_norm);
|
||||||
quadratureOptions.error_norm);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.roles.discretization,
|
apply_quadrature_options(quadratureRuleSet.roles.discretization, quadratureOptions.roles.discretization);
|
||||||
quadratureOptions.roles.discretization);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.roles.preconditioner,
|
apply_quadrature_options(quadratureRuleSet.roles.preconditioner, quadratureOptions.roles.preconditioner);
|
||||||
quadratureOptions.roles.preconditioner);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.roles.diagnostic,
|
apply_quadrature_options(quadratureRuleSet.roles.diagnostic, quadratureOptions.roles.diagnostic);
|
||||||
quadratureOptions.roles.diagnostic);
|
|
||||||
|
|
||||||
apply_quadrature_options(quadratureRuleSet.roles.projection,
|
apply_quadrature_options(quadratureRuleSet.roles.projection, quadratureOptions.roles.projection);
|
||||||
quadratureOptions.roles.projection);
|
|
||||||
|
|
||||||
fem.quadratureFactory = std::make_unique<quadrature::RuleFactory>(
|
fem.quadratureFactory =
|
||||||
quadrature::Policy(std::move(quadratureRuleSet)));
|
std::make_unique<quadrature::RuleFactory>(quadrature::Policy(std::move(quadratureRuleSet)));
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
// Section 11: Stateless domain mapper
|
// Section 11: Stateless domain mapper
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
|
|
||||||
auto exteriorDomain =
|
auto exteriorDomain =
|
||||||
std::make_unique<const mapping::compactification::KelvinCompactification>(
|
std::make_unique<const mapping::compactification::KelvinCompactification>(args.kelvin_options);
|
||||||
args.kelvin_options);
|
|
||||||
|
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
args.domain_mapper_options.vacuum_element_attribute ==
|
args.domain_mapper_options.vacuum_element_attribute ==
|
||||||
DomainSchema::template material_attribute<utils::domain::Vacuum>(),
|
DomainSchema::template material_attribute<utils::domain::Vacuum>(),
|
||||||
"The domain-mapper compactification attribute must match the vacuum "
|
"The domain-mapper compactification attribute must match the vacuum "
|
||||||
"material registered by the "
|
"material registered by the "
|
||||||
"production domain schema.");
|
"production domain schema."
|
||||||
|
);
|
||||||
|
|
||||||
fem.domainMapperStateless = std::make_unique<mapping::DomainMapper>(
|
fem.domainMapperStateless =
|
||||||
args.domain_mapper_options, std::move(exteriorDomain));
|
std::make_unique<mapping::DomainMapper>(args.domain_mapper_options, std::move(exteriorDomain));
|
||||||
|
|
||||||
return fem;
|
return fem;
|
||||||
}
|
}
|
||||||
} // namespace mean_field::fem
|
} // namespace mean_field::fem
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ namespace mean_field::integrators {
|
|||||||
const mfem::GridFunction &displacement,
|
const mfem::GridFunction &displacement,
|
||||||
const mfem::GridFunction &compactification_coordinate
|
const mfem::GridFunction &compactification_coordinate
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate) {
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvectionIntegrator::AssembleElementVector(
|
void AdvectionIntegrator::AssembleElementVector(
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ namespace mean_field::integrators {
|
|||||||
const mfem::GridFunction &compactification_coordinate,
|
const mfem::GridFunction &compactification_coordinate,
|
||||||
const mfem::Vector &omega
|
const mfem::Vector &omega
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_omega(3) {
|
m_omega(3) {
|
||||||
MFEM_ASSERT(omega.Size() == 3, "Omega vector must be 3D");
|
MFEM_ASSERT(omega.Size() == 3, "Omega vector must be 3D");
|
||||||
m_omega = omega;
|
m_omega = omega;
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ namespace mean_field::integrators {
|
|||||||
const mfem::GridFunction &compactification_coordinate,
|
const mfem::GridFunction &compactification_coordinate,
|
||||||
const mfem::Vector &omega
|
const mfem::Vector &omega
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_omega(omega) {
|
m_omega(omega) {
|
||||||
m_omega_mat.SetSize(3, 3);
|
m_omega_mat.SetSize(3, 3);
|
||||||
m_omega_mat = 0.0;
|
m_omega_mat = 0.0;
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ namespace mean_field::integrators {
|
|||||||
const mfem::GridFunction &compactification_coordinate,
|
const mfem::GridFunction &compactification_coordinate,
|
||||||
const GravityForceJacobianMode jacobian_mode
|
const GravityForceJacobianMode jacobian_mode
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_jacobian_mode(jacobian_mode) {
|
m_jacobian_mode(jacobian_mode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ namespace mean_field::integrators {
|
|||||||
const mfem::GridFunction &displacement,
|
const mfem::GridFunction &displacement,
|
||||||
const mfem::GridFunction &compactification_coordinate
|
const mfem::GridFunction &compactification_coordinate
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate) { };
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
) { };
|
||||||
|
|
||||||
void ContinuityVolumeIntegrator::AssembleElementVector(
|
void ContinuityVolumeIntegrator::AssembleElementVector(
|
||||||
const mfem::Array<const mfem::FiniteElement *> &el,
|
const mfem::Array<const mfem::FiniteElement *> &el,
|
||||||
@@ -174,7 +178,11 @@ namespace mean_field::integrators {
|
|||||||
const mfem::GridFunction &displacement,
|
const mfem::GridFunction &displacement,
|
||||||
const mfem::GridFunction &compactification_coordinate
|
const mfem::GridFunction &compactification_coordinate
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate) {
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContinuityFaceIntegrator::AssembleFaceVector(
|
void ContinuityFaceIntegrator::AssembleFaceVector(
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ namespace mean_field::integrators {
|
|||||||
const double mu,
|
const double mu,
|
||||||
const int quad_boost
|
const int quad_boost
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_mu(mu),
|
m_mu(mu),
|
||||||
m_quad_boost(quad_boost) {
|
m_quad_boost(quad_boost) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ namespace mean_field::mapping {
|
|||||||
Coefficient &coeff,
|
Coefficient &coeff,
|
||||||
const COORDINATE_SPACE coord_space
|
const COORDINATE_SPACE coord_space
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_coeff(coeff),
|
m_coeff(coeff),
|
||||||
m_coord_space(coord_space) { };
|
m_coord_space(coord_space) { };
|
||||||
|
|
||||||
@@ -63,7 +67,11 @@ namespace mean_field::mapping {
|
|||||||
const int dim
|
const int dim
|
||||||
)
|
)
|
||||||
: MatrixCoefficient(dim),
|
: MatrixCoefficient(dim),
|
||||||
m_mapping(mapper, displacement, compactification_coordinate),
|
m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_scalar(&sigma),
|
m_scalar(&sigma),
|
||||||
m_tensor(nullptr) { };
|
m_tensor(nullptr) { };
|
||||||
|
|
||||||
@@ -74,7 +82,11 @@ namespace mean_field::mapping {
|
|||||||
MatrixCoefficient &sigma
|
MatrixCoefficient &sigma
|
||||||
)
|
)
|
||||||
: MatrixCoefficient(sigma.GetHeight()),
|
: MatrixCoefficient(sigma.GetHeight()),
|
||||||
m_mapping(mapper, displacement, compactification_coordinate),
|
m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_scalar(nullptr),
|
m_scalar(nullptr),
|
||||||
m_tensor(&sigma) { };
|
m_tensor(&sigma) { };
|
||||||
|
|
||||||
@@ -120,7 +132,11 @@ namespace mean_field::mapping {
|
|||||||
VectorCoefficient &coeff
|
VectorCoefficient &coeff
|
||||||
)
|
)
|
||||||
: VectorCoefficient(coeff.GetVDim()),
|
: VectorCoefficient(coeff.GetVDim()),
|
||||||
m_mapping(mapper, displacement, compactification_coordinate),
|
m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_coeff(coeff) { };
|
m_coeff(coeff) { };
|
||||||
|
|
||||||
void MappedVectorCoefficient::Eval(
|
void MappedVectorCoefficient::Eval(
|
||||||
@@ -157,7 +173,11 @@ namespace mean_field::mapping {
|
|||||||
Func f // std::function<double(const mfem::Vector&)>
|
Func f // std::function<double(const mfem::Vector&)>
|
||||||
)
|
)
|
||||||
: m_f(std::move(f)),
|
: m_f(std::move(f)),
|
||||||
m_mapping(mapper, displacement, compactification_coordinate) { };
|
m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
) { };
|
||||||
|
|
||||||
double PhysicalPositionFunctionCoefficient::Eval(
|
double PhysicalPositionFunctionCoefficient::Eval(
|
||||||
mfem::ElementTransformation &T,
|
mfem::ElementTransformation &T,
|
||||||
@@ -179,7 +199,11 @@ namespace mean_field::mapping {
|
|||||||
const int dim
|
const int dim
|
||||||
)
|
)
|
||||||
: MatrixCoefficient(dim),
|
: MatrixCoefficient(dim),
|
||||||
m_mapping(mapper, displacement, compactification_coordinate) {
|
m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappedHDivMassCoefficient::Eval(
|
void MappedHDivMassCoefficient::Eval(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ namespace mean_field::models::structure {
|
|||||||
validate();
|
validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
const eos::EquationOfState &PolytropicStructure::equationOfState() const noexcept {
|
const eos::Polytrope &PolytropicStructure::equationOfState() const noexcept {
|
||||||
return m_equationOfState;
|
return m_equationOfState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,9 @@ namespace mean_field::models::structure {
|
|||||||
const double polytropicIndex = m_equationOfState.polytropic_index();
|
const double polytropicIndex = m_equationOfState.polytropic_index();
|
||||||
const std::vector<LaneEmdenPoint> laneEmdenSolution = solveLaneEmden(polytropicIndex);
|
const std::vector<LaneEmdenPoint> laneEmdenSolution = solveLaneEmden(polytropicIndex);
|
||||||
const double surfaceCoordinate = laneEmdenSolution.back().coordinate;
|
const double surfaceCoordinate = laneEmdenSolution.back().coordinate;
|
||||||
const double centralEnthalpy = m_equationOfState.enthalpy_from_density(request.centralDensity);
|
const double centralEnthalpy =
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(m_equationOfState, eos::DensityValue{request.centralDensity})
|
||||||
|
.value();
|
||||||
const double radialScaleSquared =
|
const double radialScaleSquared =
|
||||||
centralEnthalpy / (4.0 * std::numbers::pi_v<double> * mean_field::utils::G * request.centralDensity);
|
centralEnthalpy / (4.0 * std::numbers::pi_v<double> * mean_field::utils::G * request.centralDensity);
|
||||||
|
|
||||||
@@ -65,7 +67,8 @@ namespace mean_field::models::structure {
|
|||||||
|
|
||||||
seed.radius(sampleIndex) = radialScale * dimensionlessRadius;
|
seed.radius(sampleIndex) = radialScale * dimensionlessRadius;
|
||||||
seed.density(sampleIndex) = density;
|
seed.density(sampleIndex) = density;
|
||||||
seed.enthalpy(sampleIndex) = m_equationOfState.enthalpy_from_density(density);
|
seed.enthalpy(sampleIndex) =
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(m_equationOfState, eos::DensityValue{density}).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
seed.radius(0) = 0.0;
|
seed.radius(0) = 0.0;
|
||||||
|
|||||||
@@ -80,13 +80,19 @@ namespace mean_field::operators::context::hydrostatic {
|
|||||||
: m_f(f),
|
: m_f(f),
|
||||||
m_domainMapper(domainMapper),
|
m_domainMapper(domainMapper),
|
||||||
m_enthalpyMap(
|
m_enthalpyMap(
|
||||||
field::make_field_dof_map<field::Enthalpy, DomainSchema>(*f.enthalpyFes)
|
field::make_field_dof_map<
|
||||||
|
field::Enthalpy,
|
||||||
|
DomainSchema>(*f.enthalpyFes)
|
||||||
),
|
),
|
||||||
m_gravityPotentialMap(
|
m_gravityPotentialMap(
|
||||||
field::make_field_dof_map<field::Gravity, DomainSchema>(*f.gravityPotentialFes)
|
field::make_field_dof_map<
|
||||||
|
field::Gravity,
|
||||||
|
DomainSchema>(*f.gravityPotentialFes)
|
||||||
),
|
),
|
||||||
m_displacementMap(
|
m_displacementMap(
|
||||||
field::make_field_dof_map<field::Displacement, DomainSchema>(*f.displacementFes)
|
field::make_field_dof_map<
|
||||||
|
field::Displacement,
|
||||||
|
DomainSchema>(*f.displacementFes)
|
||||||
) {
|
) {
|
||||||
MFEM_VERIFY(m_f.mesh != nullptr, "HydrostaticEquilibriumContext requires a mesh.");
|
MFEM_VERIFY(m_f.mesh != nullptr, "HydrostaticEquilibriumContext requires a mesh.");
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@ import :field.registry;
|
|||||||
import :utils.domain;
|
import :utils.domain;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
namespace eos = mean_field::eos;
|
||||||
|
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
|
using ClosureDomain = mean_field::field::FieldDomainT<mean_field::field::Density>;
|
||||||
|
|
||||||
@@ -336,11 +338,21 @@ namespace {
|
|||||||
if (closureAction == ClosureAction::residual) {
|
if (closureAction == ClosureAction::residual) {
|
||||||
const double density = elementDensityInput * densityShape;
|
const double density = elementDensityInput * densityShape;
|
||||||
|
|
||||||
integrand = density - barotrope.density_from_enthalpy(baseEnthalpy);
|
const double equationOfStateDensity =
|
||||||
|
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{baseEnthalpy})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
integrand = density - equationOfStateDensity;
|
||||||
} else {
|
} else {
|
||||||
const double enthalpyVariation = elementEnthalpyVariation * enthalpyShape;
|
const double enthalpyVariation = elementEnthalpyVariation * enthalpyShape;
|
||||||
|
|
||||||
integrand = -barotrope.density_derivative_from_enthalpy(baseEnthalpy) * enthalpyVariation;
|
const double densityDerivative =
|
||||||
|
eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{baseEnthalpy}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
integrand = -densityDerivative * enthalpyVariation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,7 +644,10 @@ namespace mean_field::operators::kernels {
|
|||||||
|
|
||||||
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
|
const double enthalpyValue = elementBaseEnthalpy * enthalpyShape;
|
||||||
|
|
||||||
const double closureValue = densityValue - barotrope.density_from_enthalpy(enthalpyValue);
|
const double equationOfStateDensity =
|
||||||
|
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{enthalpyValue}).value();
|
||||||
|
|
||||||
|
const double closureValue = densityValue - equationOfStateDensity;
|
||||||
|
|
||||||
const double geometryActionValue = closureValue * mappingVariation.weight_variation;
|
const double geometryActionValue = closureValue * mappingVariation.weight_variation;
|
||||||
|
|
||||||
|
|||||||
@@ -11,61 +11,64 @@ module mean_field;
|
|||||||
import :operators.kernels.gravity_displacement_force;
|
import :operators.kernels.gravity_displacement_force;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
||||||
return DomainSchema::template attribute_belongs_to<
|
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
|
||||||
mean_field::utils::domain::Vacuum>(attribute);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
enum class GravityDisplacementForceAction {
|
enum class GravityDisplacementForceAction { residual, density, gravityGradient, displacement, complete };
|
||||||
residual,
|
|
||||||
density,
|
|
||||||
gravityGradient,
|
|
||||||
displacement,
|
|
||||||
complete
|
|
||||||
};
|
|
||||||
|
|
||||||
void true_to_local(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
void true_to_local(
|
||||||
const mfem::Vector &trueVector, mfem::Vector &localVector) {
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
const mfem::Vector &trueVector,
|
||||||
"The gravity-displacement-force true vector has the wrong size.");
|
mfem::Vector &localVector
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||||
|
"The gravity-displacement-force true vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||||
finiteElementSpace.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->Mult(trueVector, localVector);
|
prolongation->Mult(trueVector, localVector);
|
||||||
} else {
|
} else {
|
||||||
localVector = trueVector;
|
localVector = trueVector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
void local_to_true(
|
||||||
const mfem::Vector &localVector, mfem::Vector &trueVector) {
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
|
const mfem::Vector &localVector,
|
||||||
|
mfem::Vector &trueVector
|
||||||
|
) {
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
localVector.Size() == finiteElementSpace.GetVSize(),
|
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||||
"The gravity-displacement-force local vector has the wrong size.");
|
"The gravity-displacement-force local vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||||
trueVector = 0.0;
|
trueVector = 0.0;
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||||
finiteElementSpace.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->MultTranspose(localVector, trueVector);
|
prolongation->MultTranspose(localVector, trueVector);
|
||||||
} else {
|
} else {
|
||||||
trueVector = localVector;
|
trueVector = localVector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int vector_dof_index(const mfem::Ordering::Type ordering,
|
[[nodiscard]] int vector_dof_index(
|
||||||
const int scalarDof, const int component,
|
const mfem::Ordering::Type ordering,
|
||||||
|
const int scalarDof,
|
||||||
|
const int component,
|
||||||
const int scalarDofCount,
|
const int scalarDofCount,
|
||||||
const int dimension) {
|
const int dimension
|
||||||
|
) {
|
||||||
if (ordering == mfem::Ordering::byNODES) {
|
if (ordering == mfem::Ordering::byNODES) {
|
||||||
return scalarDof + component * scalarDofCount;
|
return scalarDof + component * scalarDofCount;
|
||||||
}
|
}
|
||||||
@@ -74,120 +77,143 @@ void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
|||||||
return scalarDof * dimension + component;
|
return scalarDof * dimension + component;
|
||||||
}
|
}
|
||||||
|
|
||||||
MFEM_ABORT("The gravity-displacement-force test space uses an unsupported "
|
MFEM_ABORT(
|
||||||
"ordering.");
|
"The gravity-displacement-force test space uses an unsupported "
|
||||||
|
"ordering."
|
||||||
|
);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const mfem::IntegrationRule &
|
[[nodiscard]] const mfem::IntegrationRule &get_gravity_force_rule(
|
||||||
get_gravity_force_rule(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mfem::FiniteElement &densityElement,
|
const mfem::FiniteElement &densityElement,
|
||||||
const mfem::FiniteElement &gravityGradientElement,
|
const mfem::FiniteElement &gravityGradientElement,
|
||||||
const mfem::FiniteElement &displacementElement,
|
const mfem::FiniteElement &displacementElement,
|
||||||
const mfem::ElementTransformation &transformation) {
|
const mfem::ElementTransformation &transformation
|
||||||
using DisplacementField =
|
) {
|
||||||
mean_field::field::Field<mean_field::field::Displacement>;
|
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
||||||
|
|
||||||
MFEM_VERIFY(densityElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Density::Scalar::familyOrder,
|
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||||
"The gravity-displacement-force density element does not match "
|
"The gravity-displacement-force density element does not match "
|
||||||
"the registered density field.");
|
"the registered density field."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(gravityGradientElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Gravity::Flux::familyOrder + 1,
|
gravityGradientElement.GetOrder() == mean_field::field::Gravity::Flux::familyOrder + 1,
|
||||||
"The gravity-displacement-force RT element does not match the "
|
"The gravity-displacement-force RT element does not match the "
|
||||||
"registered gravity-gradient field.");
|
"registered gravity-gradient field."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(displacementElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Displacement::Vector::familyOrder,
|
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
|
||||||
"The gravity-displacement-force test element does not match the "
|
"The gravity-displacement-force test element does not match the "
|
||||||
"registered displacement field.");
|
"registered displacement field."
|
||||||
|
);
|
||||||
|
|
||||||
const mean_field::quadrature::Query query = DisplacementField::make_query<
|
const mean_field::quadrature::Query query =
|
||||||
mean_field::field::Displacement::Form::GravityForce>(
|
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>(
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
mean_field::quadrature::MappingKind::general);
|
);
|
||||||
|
|
||||||
const mean_field::quadrature::MfemRule rule =
|
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
|
||||||
|
|
||||||
MFEM_VERIFY(rule.integration_rule != nullptr,
|
MFEM_VERIFY(
|
||||||
"The quadrature policy did not return a gravity-displacement-"
|
rule.integration_rule != nullptr, "The quadrature policy did not return a gravity-displacement-"
|
||||||
"force integration rule.");
|
"force integration rule."
|
||||||
|
);
|
||||||
|
|
||||||
return *rule.integration_rule;
|
return *rule.integration_rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_finite_vector(const mfem::Vector &vector, const char *message) {
|
void validate_finite_vector(
|
||||||
|
const mfem::Vector &vector,
|
||||||
|
const char *message
|
||||||
|
) {
|
||||||
for (int index = 0; index < vector.Size(); ++index) {
|
for (int index = 0; index < vector.Size(); ++index) {
|
||||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_common_inputs(
|
void validate_common_inputs(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper,
|
const mean_field::mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &displacementTrue) {
|
const mfem::Vector &displacementTrue
|
||||||
MFEM_VERIFY(f.mesh != nullptr,
|
) {
|
||||||
"The gravity-displacement-force kernel requires a mesh.");
|
MFEM_VERIFY(f.mesh != nullptr, "The gravity-displacement-force kernel requires a mesh.");
|
||||||
|
|
||||||
MFEM_VERIFY(f.densityFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force kernel requires the density "
|
f.densityFes != nullptr, "The gravity-displacement-force kernel requires the density "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.gravityFluxFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force kernel requires the gravity-"
|
f.gravityFluxFes != nullptr, "The gravity-displacement-force kernel requires the gravity-"
|
||||||
"gradient finite-element space.");
|
"gradient finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.displacementFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force kernel requires the displacement "
|
f.displacementFes != nullptr, "The gravity-displacement-force kernel requires the displacement "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.compactificationFes != nullptr &&
|
MFEM_VERIFY(
|
||||||
f.compactificationCoordinate != nullptr,
|
f.compactificationFes != nullptr && f.compactificationCoordinate != nullptr,
|
||||||
"The gravity-displacement-force kernel requires the "
|
"The gravity-displacement-force kernel requires the "
|
||||||
"compactification coordinate.");
|
"compactification coordinate."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force kernel requires the quadrature "
|
f.quadratureFactory != nullptr, "The gravity-displacement-force kernel requires the quadrature "
|
||||||
"rule factory.");
|
"rule factory."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
MFEM_VERIFY(
|
||||||
|
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||||
"The gravity-displacement-force displacement vector has the "
|
"The gravity-displacement-force displacement vector has the "
|
||||||
"wrong size.");
|
"wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(domainMapper.GetDimension() == f.mesh->Dimension(),
|
MFEM_VERIFY(
|
||||||
|
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||||
"The gravity-displacement-force mapper dimension does not match "
|
"The gravity-displacement-force mapper dimension does not match "
|
||||||
"the mesh dimension.");
|
"the mesh dimension."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
MFEM_VERIFY(
|
||||||
|
f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
||||||
"The gravity-displacement-force displacement dimension does not "
|
"The gravity-displacement-force displacement dimension does not "
|
||||||
"match the mesh dimension.");
|
"match the mesh dimension."
|
||||||
|
);
|
||||||
|
|
||||||
validate_finite_vector(
|
validate_finite_vector(
|
||||||
displacementTrue,
|
displacementTrue, "The gravity-displacement-force displacement contains a "
|
||||||
"The gravity-displacement-force displacement contains a "
|
"non-finite value."
|
||||||
"non-finite value.");
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_density(const mean_field::fem::FEM &f,
|
void validate_density(
|
||||||
const mfem::Vector &density, const char *message) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const mfem::Vector &density,
|
||||||
|
const char *message
|
||||||
|
) {
|
||||||
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
|
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
|
||||||
validate_finite_vector(density, message);
|
validate_finite_vector(density, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_gravity_gradient(const mean_field::fem::FEM &f,
|
void validate_gravity_gradient(
|
||||||
|
const mean_field::fem::FEM &f,
|
||||||
const mfem::Vector &gravityGradient,
|
const mfem::Vector &gravityGradient,
|
||||||
const char *message) {
|
const char *message
|
||||||
MFEM_VERIFY(gravityGradient.Size() == f.gravityFluxFes->GetTrueVSize(),
|
) {
|
||||||
message);
|
MFEM_VERIFY(gravityGradient.Size() == f.gravityFluxFes->GetTrueVSize(), message);
|
||||||
|
|
||||||
validate_finite_vector(gravityGradient, message);
|
validate_finite_vector(gravityGradient, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_gravity_displacement_force_action(
|
void apply_gravity_displacement_force_action(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper,
|
const mean_field::mapping::DomainMapper &domainMapper,
|
||||||
const GravityDisplacementForceAction requestedAction,
|
const GravityDisplacementForceAction requestedAction,
|
||||||
@@ -196,85 +222,90 @@ void apply_gravity_displacement_force_action(
|
|||||||
const mfem::Vector *baseGravityGradientTrue,
|
const mfem::Vector *baseGravityGradientTrue,
|
||||||
const mfem::Vector *gravityGradientVariationTrue,
|
const mfem::Vector *gravityGradientVariationTrue,
|
||||||
const mfem::Vector *displacementVariationTrue,
|
const mfem::Vector *displacementVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
validate_common_inputs(f, domainMapper, displacementTrue);
|
validate_common_inputs(f, domainMapper, displacementTrue);
|
||||||
|
|
||||||
const bool needsBaseDensity =
|
const bool needsBaseDensity = requestedAction == GravityDisplacementForceAction::residual ||
|
||||||
requestedAction == GravityDisplacementForceAction::residual ||
|
|
||||||
requestedAction == GravityDisplacementForceAction::gravityGradient ||
|
requestedAction == GravityDisplacementForceAction::gravityGradient ||
|
||||||
requestedAction == GravityDisplacementForceAction::displacement ||
|
requestedAction == GravityDisplacementForceAction::displacement ||
|
||||||
requestedAction == GravityDisplacementForceAction::complete;
|
requestedAction == GravityDisplacementForceAction::complete;
|
||||||
|
|
||||||
const bool needsDensityVariation =
|
const bool needsDensityVariation = requestedAction == GravityDisplacementForceAction::density ||
|
||||||
requestedAction == GravityDisplacementForceAction::density ||
|
|
||||||
requestedAction == GravityDisplacementForceAction::complete;
|
requestedAction == GravityDisplacementForceAction::complete;
|
||||||
|
|
||||||
const bool needsBaseGravityGradient =
|
const bool needsBaseGravityGradient = requestedAction == GravityDisplacementForceAction::residual ||
|
||||||
requestedAction == GravityDisplacementForceAction::residual ||
|
|
||||||
requestedAction == GravityDisplacementForceAction::density ||
|
requestedAction == GravityDisplacementForceAction::density ||
|
||||||
requestedAction == GravityDisplacementForceAction::displacement ||
|
requestedAction == GravityDisplacementForceAction::displacement ||
|
||||||
requestedAction == GravityDisplacementForceAction::complete;
|
requestedAction == GravityDisplacementForceAction::complete;
|
||||||
|
|
||||||
const bool needsGravityGradientVariation =
|
const bool needsGravityGradientVariation = requestedAction == GravityDisplacementForceAction::gravityGradient ||
|
||||||
requestedAction == GravityDisplacementForceAction::gravityGradient ||
|
|
||||||
requestedAction == GravityDisplacementForceAction::complete;
|
requestedAction == GravityDisplacementForceAction::complete;
|
||||||
|
|
||||||
const bool needsDisplacementVariation =
|
const bool needsDisplacementVariation = requestedAction == GravityDisplacementForceAction::displacement ||
|
||||||
requestedAction == GravityDisplacementForceAction::displacement ||
|
|
||||||
requestedAction == GravityDisplacementForceAction::complete;
|
requestedAction == GravityDisplacementForceAction::complete;
|
||||||
|
|
||||||
if (needsBaseDensity) {
|
if (needsBaseDensity) {
|
||||||
MFEM_VERIFY(baseDensityTrue != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force action requires a base "
|
baseDensityTrue != nullptr, "The gravity-displacement-force action requires a base "
|
||||||
"density.");
|
"density."
|
||||||
|
);
|
||||||
|
|
||||||
validate_density(f, *baseDensityTrue,
|
validate_density(f, *baseDensityTrue, "The gravity-displacement-force base density is invalid.");
|
||||||
"The gravity-displacement-force base density is invalid.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsDensityVariation) {
|
if (needsDensityVariation) {
|
||||||
MFEM_VERIFY(densityVariationTrue != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force action requires a density "
|
densityVariationTrue != nullptr, "The gravity-displacement-force action requires a density "
|
||||||
"variation.");
|
"variation."
|
||||||
|
);
|
||||||
|
|
||||||
validate_density(f, *densityVariationTrue,
|
validate_density(
|
||||||
|
f, *densityVariationTrue,
|
||||||
"The gravity-displacement-force density variation is "
|
"The gravity-displacement-force density variation is "
|
||||||
"invalid.");
|
"invalid."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsBaseGravityGradient) {
|
if (needsBaseGravityGradient) {
|
||||||
MFEM_VERIFY(baseGravityGradientTrue != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force action requires a base "
|
baseGravityGradientTrue != nullptr, "The gravity-displacement-force action requires a base "
|
||||||
"gravity gradient.");
|
"gravity gradient."
|
||||||
|
);
|
||||||
|
|
||||||
validate_gravity_gradient(
|
validate_gravity_gradient(
|
||||||
f, *baseGravityGradientTrue,
|
f, *baseGravityGradientTrue,
|
||||||
"The gravity-displacement-force base gravity gradient is "
|
"The gravity-displacement-force base gravity gradient is "
|
||||||
"invalid.");
|
"invalid."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsGravityGradientVariation) {
|
if (needsGravityGradientVariation) {
|
||||||
MFEM_VERIFY(gravityGradientVariationTrue != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force action requires a gravity-"
|
gravityGradientVariationTrue != nullptr, "The gravity-displacement-force action requires a gravity-"
|
||||||
"gradient variation.");
|
"gradient variation."
|
||||||
|
);
|
||||||
|
|
||||||
validate_gravity_gradient(
|
validate_gravity_gradient(
|
||||||
f, *gravityGradientVariationTrue,
|
f, *gravityGradientVariationTrue,
|
||||||
"The gravity-displacement-force gravity-gradient variation "
|
"The gravity-displacement-force gravity-gradient variation "
|
||||||
"is invalid.");
|
"is invalid."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
MFEM_VERIFY(displacementVariationTrue != nullptr &&
|
MFEM_VERIFY(
|
||||||
displacementVariationTrue->Size() ==
|
displacementVariationTrue != nullptr &&
|
||||||
f.displacementFes->GetTrueVSize(),
|
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
|
||||||
"The gravity-displacement-force displacement variation is "
|
"The gravity-displacement-force displacement variation is "
|
||||||
"invalid.");
|
"invalid."
|
||||||
|
);
|
||||||
|
|
||||||
validate_finite_vector(
|
validate_finite_vector(
|
||||||
*displacementVariationTrue,
|
*displacementVariationTrue, "The gravity-displacement-force displacement variation "
|
||||||
"The gravity-displacement-force displacement variation "
|
"contains a non-finite value."
|
||||||
"contains a non-finite value.");
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
mfem::Vector baseDensityLocal;
|
mfem::Vector baseDensityLocal;
|
||||||
@@ -293,27 +324,23 @@ void apply_gravity_displacement_force_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (needsBaseGravityGradient) {
|
if (needsBaseGravityGradient) {
|
||||||
true_to_local(*f.gravityFluxFes, *baseGravityGradientTrue,
|
true_to_local(*f.gravityFluxFes, *baseGravityGradientTrue, baseGravityGradientLocal);
|
||||||
baseGravityGradientLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsGravityGradientVariation) {
|
if (needsGravityGradientVariation) {
|
||||||
true_to_local(*f.gravityFluxFes, *gravityGradientVariationTrue,
|
true_to_local(*f.gravityFluxFes, *gravityGradientVariationTrue, gravityGradientVariationLocal);
|
||||||
gravityGradientVariationLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
true_to_local(*f.displacementFes, *displacementVariationTrue,
|
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
|
||||||
displacementVariationLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mfem::Vector localAction(f.displacementFes->GetVSize());
|
mfem::Vector localAction(f.displacementFes->GetVSize());
|
||||||
localAction = 0.0;
|
localAction = 0.0;
|
||||||
|
|
||||||
mean_field::mapping::DomainMapper::Workspace workspace(
|
mean_field::mapping::DomainMapper::Workspace workspace(f.mesh->Dimension());
|
||||||
f.mesh->Dimension());
|
|
||||||
|
|
||||||
mfem::Array<int> densityDofs;
|
mfem::Array<int> densityDofs;
|
||||||
mfem::Array<int> gravityGradientDofs;
|
mfem::Array<int> gravityGradientDofs;
|
||||||
@@ -345,16 +372,15 @@ void apply_gravity_displacement_force_action(
|
|||||||
|
|
||||||
const int dimension = f.mesh->Dimension();
|
const int dimension = f.mesh->Dimension();
|
||||||
|
|
||||||
const mfem::Ordering::Type displacementOrdering =
|
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
|
||||||
f.displacementFes->GetOrdering();
|
|
||||||
|
|
||||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||||
mfem::ElementTransformation *transformation =
|
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||||
f.mesh->GetElementTransformation(elementId);
|
|
||||||
|
|
||||||
MFEM_VERIFY(transformation != nullptr,
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force kernel received a null "
|
transformation != nullptr, "The gravity-displacement-force kernel received a null "
|
||||||
"element transformation.");
|
"element transformation."
|
||||||
|
);
|
||||||
|
|
||||||
if (is_vacuum_attribute(transformation->Attribute)) {
|
if (is_vacuum_attribute(transformation->Attribute)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -362,17 +388,13 @@ void apply_gravity_displacement_force_action(
|
|||||||
|
|
||||||
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
|
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
|
||||||
|
|
||||||
const mfem::FiniteElement &gravityGradientElement =
|
const mfem::FiniteElement &gravityGradientElement = *f.gravityFluxFes->GetFE(elementId);
|
||||||
*f.gravityFluxFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &displacementElement =
|
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||||
*f.displacementFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &compactificationElement =
|
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||||
*f.compactificationFes->GetFE(elementId);
|
|
||||||
|
|
||||||
mfem::DofTransformation *densityDofTransformation =
|
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||||
f.densityFes->GetElementDofs(elementId, densityDofs);
|
|
||||||
|
|
||||||
mfem::DofTransformation *gravityGradientDofTransformation =
|
mfem::DofTransformation *gravityGradientDofTransformation =
|
||||||
f.gravityFluxFes->GetElementVDofs(elementId, gravityGradientDofs);
|
f.gravityFluxFes->GetElementVDofs(elementId, gravityGradientDofs);
|
||||||
@@ -392,24 +414,20 @@ void apply_gravity_displacement_force_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (needsBaseGravityGradient) {
|
if (needsBaseGravityGradient) {
|
||||||
baseGravityGradientLocal.GetSubVector(gravityGradientDofs,
|
baseGravityGradientLocal.GetSubVector(gravityGradientDofs, elementBaseGravityGradient);
|
||||||
elementBaseGravityGradient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsGravityGradientVariation) {
|
if (needsGravityGradientVariation) {
|
||||||
gravityGradientVariationLocal.GetSubVector(
|
gravityGradientVariationLocal.GetSubVector(gravityGradientDofs, elementGravityGradientVariation);
|
||||||
gravityGradientDofs, elementGravityGradientVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
displacementVariationLocal.GetSubVector(displacementDofs,
|
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||||
elementDisplacementVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.compactificationCoordinate->GetSubVector(compactificationDofs,
|
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
|
|
||||||
if (densityDofTransformation != nullptr) {
|
if (densityDofTransformation != nullptr) {
|
||||||
if (needsBaseDensity) {
|
if (needsBaseDensity) {
|
||||||
@@ -423,13 +441,11 @@ void apply_gravity_displacement_force_action(
|
|||||||
|
|
||||||
if (gravityGradientDofTransformation != nullptr) {
|
if (gravityGradientDofTransformation != nullptr) {
|
||||||
if (needsBaseGravityGradient) {
|
if (needsBaseGravityGradient) {
|
||||||
gravityGradientDofTransformation->InvTransformPrimal(
|
gravityGradientDofTransformation->InvTransformPrimal(elementBaseGravityGradient);
|
||||||
elementBaseGravityGradient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsGravityGradientVariation) {
|
if (needsGravityGradientVariation) {
|
||||||
gravityGradientDofTransformation->InvTransformPrimal(
|
gravityGradientDofTransformation->InvTransformPrimal(elementGravityGradientVariation);
|
||||||
elementGravityGradientVariation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,42 +453,42 @@ void apply_gravity_displacement_force_action(
|
|||||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
displacementDofTransformation->InvTransformPrimal(
|
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||||
elementDisplacementVariation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compactificationDofTransformation != nullptr) {
|
if (compactificationDofTransformation != nullptr) {
|
||||||
compactificationDofTransformation->InvTransformPrimal(
|
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||||
displacementElement, elementDisplacement);
|
|
||||||
|
|
||||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||||
compactificationElement, elementCompactification);
|
compactificationElement, elementCompactification
|
||||||
|
);
|
||||||
|
|
||||||
const mean_field::mapping::ElementMappingData mappingData{
|
const mean_field::mapping::ElementMappingData mappingData{
|
||||||
.displacement = displacementData,
|
.displacement = displacementData, .compactification = compactificationData
|
||||||
.compactification = compactificationData};
|
};
|
||||||
|
|
||||||
std::optional<mean_field::mapping::ElementDisplacementData>
|
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
|
||||||
displacementVariationData;
|
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
displacementVariationData.emplace(
|
displacementVariationData.emplace(
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||||
displacementElement, elementDisplacementVariation));
|
displacementElement, elementDisplacementVariation
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
||||||
|
|
||||||
MFEM_VERIFY(displacementDofs.Size() ==
|
MFEM_VERIFY(
|
||||||
scalarDisplacementDofCount * dimension,
|
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
|
||||||
"The gravity-displacement-force element displacement vector "
|
"The gravity-displacement-force element displacement vector "
|
||||||
"has the wrong size.");
|
"has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
densityShape.SetSize(densityElement.GetDof());
|
densityShape.SetSize(densityElement.GetDof());
|
||||||
displacementShape.SetSize(scalarDisplacementDofCount);
|
displacementShape.SetSize(scalarDisplacementDofCount);
|
||||||
@@ -489,42 +505,38 @@ void apply_gravity_displacement_force_action(
|
|||||||
elementAction = 0.0;
|
elementAction = 0.0;
|
||||||
|
|
||||||
const mfem::IntegrationRule &integrationRule =
|
const mfem::IntegrationRule &integrationRule =
|
||||||
get_gravity_force_rule(f, densityElement, gravityGradientElement,
|
get_gravity_force_rule(f, densityElement, gravityGradientElement, displacementElement, *transformation);
|
||||||
displacementElement, *transformation);
|
|
||||||
|
|
||||||
for (int quadratureIndex = 0;
|
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
||||||
quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
|
||||||
const mfem::IntegrationPoint &integrationPoint =
|
|
||||||
integrationRule.IntPoint(quadratureIndex);
|
|
||||||
|
|
||||||
transformation->SetIntPoint(&integrationPoint);
|
transformation->SetIntPoint(&integrationPoint);
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus mappingStatus =
|
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||||
domainMapper.EvaluateVolume(mappingData, *transformation,
|
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||||
integrationPoint, workspace,
|
);
|
||||||
mappingContext);
|
|
||||||
|
|
||||||
MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
|
MFEM_VERIFY(
|
||||||
|
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"Stateless mapping failed in the gravity-displacement-"
|
"Stateless mapping failed in the gravity-displacement-"
|
||||||
"force kernel. Element: "
|
"force kernel. Element: "
|
||||||
<< elementId
|
<< elementId << ", attribute: " << transformation->Attribute
|
||||||
<< ", attribute: " << transformation->Attribute
|
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||||
<< ", quadrature point: " << quadratureIndex
|
);
|
||||||
<< ", status: " << static_cast<int>(mappingStatus));
|
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
const mean_field::mapping::MappingStatus variationStatus =
|
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||||
domainMapper.EvaluateVolumeVariation(
|
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||||
mappingData, *displacementVariationData, *transformation,
|
workspace, mappingVariation
|
||||||
integrationPoint, mappingContext, workspace, mappingVariation);
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
variationStatus == mean_field::mapping::MappingStatus::valid,
|
variationStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"Stateless mapping variation failed in the gravity-"
|
"Stateless mapping variation failed in the gravity-"
|
||||||
"displacement-force kernel. Element: "
|
"displacement-force kernel. Element: "
|
||||||
<< elementId << ", attribute: " << transformation->Attribute
|
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
|
||||||
<< ", quadrature point: " << quadratureIndex
|
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
|
||||||
<< ", status: " << static_cast<int>(variationStatus));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
densityElement.CalcShape(integrationPoint, densityShape);
|
densityElement.CalcShape(integrationPoint, densityShape);
|
||||||
@@ -545,28 +557,27 @@ void apply_gravity_displacement_force_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (needsBaseGravityGradient) {
|
if (needsBaseGravityGradient) {
|
||||||
gravityGradientShape.MultTranspose(elementBaseGravityGradient,
|
gravityGradientShape.MultTranspose(elementBaseGravityGradient, baseGravityReferenceValue);
|
||||||
baseGravityReferenceValue);
|
|
||||||
|
|
||||||
mappingContext.mapping.mapping_jacobian.Mult(baseGravityReferenceValue,
|
mappingContext.mapping.mapping_jacobian.Mult(baseGravityReferenceValue, mappedBaseGravity);
|
||||||
mappedBaseGravity);
|
|
||||||
} else {
|
} else {
|
||||||
mappedBaseGravity = 0.0;
|
mappedBaseGravity = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsGravityGradientVariation) {
|
if (needsGravityGradientVariation) {
|
||||||
gravityGradientShape.MultTranspose(elementGravityGradientVariation,
|
gravityGradientShape.MultTranspose(elementGravityGradientVariation, gravityVariationReferenceValue);
|
||||||
gravityVariationReferenceValue);
|
|
||||||
|
|
||||||
mappingContext.mapping.mapping_jacobian.Mult(
|
mappingContext.mapping.mapping_jacobian.Mult(
|
||||||
gravityVariationReferenceValue, mappedGravityVariation);
|
gravityVariationReferenceValue, mappedGravityVariation
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
mappedGravityVariation = 0.0;
|
mappedGravityVariation = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
mappingVariation.mapping.mapping_jacobian_variation.Mult(
|
mappingVariation.mapping.mapping_jacobian_variation.Mult(
|
||||||
baseGravityReferenceValue, mappedGeometryVariation);
|
baseGravityReferenceValue, mappedGeometryVariation
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
mappedGeometryVariation = 0.0;
|
mappedGeometryVariation = 0.0;
|
||||||
}
|
}
|
||||||
@@ -601,24 +612,22 @@ void apply_gravity_displacement_force_action(
|
|||||||
* differentiating the Piola map and physical volume weight,
|
* differentiating the Piola map and physical volume weight,
|
||||||
* but avoids a numerically pointless cancellation.
|
* but avoids a numerically pointless cancellation.
|
||||||
*/
|
*/
|
||||||
const double referenceWeight =
|
const double referenceWeight = integrationPoint.weight * transformation->Weight();
|
||||||
integrationPoint.weight * transformation->Weight();
|
|
||||||
|
|
||||||
forceValue *= referenceWeight;
|
forceValue *= referenceWeight;
|
||||||
|
|
||||||
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount;
|
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
|
||||||
++scalarDof) {
|
|
||||||
for (int component = 0; component < dimension; ++component) {
|
for (int component = 0; component < dimension; ++component) {
|
||||||
const int vectorDof =
|
const int vectorDof = vector_dof_index(
|
||||||
vector_dof_index(displacementOrdering, scalarDof, component,
|
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
|
||||||
scalarDisplacementDofCount, dimension);
|
);
|
||||||
|
|
||||||
const double contribution =
|
const double contribution = displacementShape(scalarDof) * forceValue(component);
|
||||||
displacementShape(scalarDof) * forceValue(component);
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(contribution),
|
MFEM_VERIFY(
|
||||||
"The gravity-displacement-force kernel "
|
std::isfinite(contribution), "The gravity-displacement-force kernel "
|
||||||
"encountered a non-finite contribution.");
|
"encountered a non-finite contribution."
|
||||||
|
);
|
||||||
|
|
||||||
elementAction(vectorDof) += contribution;
|
elementAction(vectorDof) += contribution;
|
||||||
}
|
}
|
||||||
@@ -633,66 +642,82 @@ void apply_gravity_displacement_force_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
local_to_true(*f.displacementFes, localAction, actionTrue);
|
local_to_true(*f.displacementFes, localAction, actionTrue);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace mean_field::operators::kernels {
|
namespace mean_field::operators::kernels {
|
||||||
void apply_gravity_displacement_force_residual(
|
void apply_gravity_displacement_force_residual(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
const mfem::Vector &densityTrue, const mfem::Vector &gravityGradientTrue,
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &residualTrue) {
|
const mfem::Vector &densityTrue,
|
||||||
|
const mfem::Vector &gravityGradientTrue,
|
||||||
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &residualTrue
|
||||||
|
) {
|
||||||
apply_gravity_displacement_force_action(
|
apply_gravity_displacement_force_action(
|
||||||
f, domainMapper, GravityDisplacementForceAction::residual, &densityTrue,
|
f, domainMapper, GravityDisplacementForceAction::residual, &densityTrue, nullptr, &gravityGradientTrue,
|
||||||
nullptr, &gravityGradientTrue, nullptr, nullptr, displacementTrue,
|
nullptr, nullptr, displacementTrue, residualTrue
|
||||||
residualTrue);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_gravity_displacement_force_density_action(
|
void apply_gravity_displacement_force_density_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &densityVariationTrue,
|
const mfem::Vector &densityVariationTrue,
|
||||||
const mfem::Vector &baseGravityGradientTrue,
|
const mfem::Vector &baseGravityGradientTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
apply_gravity_displacement_force_action(
|
apply_gravity_displacement_force_action(
|
||||||
f, domainMapper, GravityDisplacementForceAction::density, nullptr,
|
f, domainMapper, GravityDisplacementForceAction::density, nullptr, &densityVariationTrue,
|
||||||
&densityVariationTrue, &baseGravityGradientTrue, nullptr, nullptr,
|
&baseGravityGradientTrue, nullptr, nullptr, displacementTrue, actionTrue
|
||||||
displacementTrue, actionTrue);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_gravity_displacement_force_gradient_action(
|
void apply_gravity_displacement_force_gradient_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &baseDensityTrue,
|
const mfem::Vector &baseDensityTrue,
|
||||||
const mfem::Vector &gravityGradientVariationTrue,
|
const mfem::Vector &gravityGradientVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
apply_gravity_displacement_force_action(
|
apply_gravity_displacement_force_action(
|
||||||
f, domainMapper, GravityDisplacementForceAction::gravityGradient,
|
f, domainMapper, GravityDisplacementForceAction::gravityGradient, &baseDensityTrue, nullptr, nullptr,
|
||||||
&baseDensityTrue, nullptr, nullptr, &gravityGradientVariationTrue,
|
&gravityGradientVariationTrue, nullptr, displacementTrue, actionTrue
|
||||||
nullptr, displacementTrue, actionTrue);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_gravity_displacement_force_displacement_action(
|
void apply_gravity_displacement_force_displacement_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &baseDensityTrue,
|
const mfem::Vector &baseDensityTrue,
|
||||||
const mfem::Vector &baseGravityGradientTrue,
|
const mfem::Vector &baseGravityGradientTrue,
|
||||||
const mfem::Vector &displacementVariationTrue,
|
const mfem::Vector &displacementVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
apply_gravity_displacement_force_action(
|
apply_gravity_displacement_force_action(
|
||||||
f, domainMapper, GravityDisplacementForceAction::displacement,
|
f, domainMapper, GravityDisplacementForceAction::displacement, &baseDensityTrue, nullptr,
|
||||||
&baseDensityTrue, nullptr, &baseGravityGradientTrue, nullptr,
|
&baseGravityGradientTrue, nullptr, &displacementVariationTrue, displacementTrue, actionTrue
|
||||||
&displacementVariationTrue, displacementTrue, actionTrue);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_gravity_displacement_force_complete_action(
|
void apply_gravity_displacement_force_complete_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &baseDensityTrue,
|
const mfem::Vector &baseDensityTrue,
|
||||||
const mfem::Vector &densityVariationTrue,
|
const mfem::Vector &densityVariationTrue,
|
||||||
const mfem::Vector &baseGravityGradientTrue,
|
const mfem::Vector &baseGravityGradientTrue,
|
||||||
const mfem::Vector &gravityGradientVariationTrue,
|
const mfem::Vector &gravityGradientVariationTrue,
|
||||||
const mfem::Vector &displacementVariationTrue,
|
const mfem::Vector &displacementVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
apply_gravity_displacement_force_action(
|
apply_gravity_displacement_force_action(
|
||||||
f, domainMapper, GravityDisplacementForceAction::complete,
|
f, domainMapper, GravityDisplacementForceAction::complete, &baseDensityTrue, &densityVariationTrue,
|
||||||
&baseDensityTrue, &densityVariationTrue, &baseGravityGradientTrue,
|
&baseGravityGradientTrue, &gravityGradientVariationTrue, &displacementVariationTrue, displacementTrue,
|
||||||
&gravityGradientVariationTrue, &displacementVariationTrue,
|
actionTrue
|
||||||
displacementTrue, actionTrue);
|
);
|
||||||
}
|
}
|
||||||
} // namespace mean_field::operators::kernels
|
} // namespace mean_field::operators::kernels
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -11,138 +11,146 @@ module mean_field;
|
|||||||
import :operators.kernels.hydrostatic_equilibrium;
|
import :operators.kernels.hydrostatic_equilibrium;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
||||||
return DomainSchema::template attribute_belongs_to<
|
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
|
||||||
mean_field::utils::domain::Vacuum>(attribute);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void true_to_local(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
void true_to_local(
|
||||||
const mfem::Vector &trueVector, mfem::Vector &localVector) {
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
const mfem::Vector &trueVector,
|
||||||
"True vector has the wrong size.");
|
mfem::Vector &localVector
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(), "True vector has the wrong size.");
|
||||||
|
|
||||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||||
finiteElementSpace.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->Mult(trueVector, localVector);
|
prolongation->Mult(trueVector, localVector);
|
||||||
} else {
|
} else {
|
||||||
localVector = trueVector;
|
localVector = trueVector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
void local_to_true(
|
||||||
const mfem::Vector &localVector, mfem::Vector &trueVector) {
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(),
|
const mfem::Vector &localVector,
|
||||||
"Local vector has the wrong size.");
|
mfem::Vector &trueVector
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(), "Local vector has the wrong size.");
|
||||||
|
|
||||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||||
|
|
||||||
trueVector = 0.0;
|
trueVector = 0.0;
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||||
finiteElementSpace.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->MultTranspose(localVector, trueVector);
|
prolongation->MultTranspose(localVector, trueVector);
|
||||||
} else {
|
} else {
|
||||||
trueVector = localVector;
|
trueVector = localVector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_fem(
|
void validate_fem(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper) {
|
const mean_field::mapping::DomainMapper &domainMapper
|
||||||
|
) {
|
||||||
MFEM_VERIFY(f.mesh != nullptr, "The hydrostatic kernel requires a mesh.");
|
MFEM_VERIFY(f.mesh != nullptr, "The hydrostatic kernel requires a mesh.");
|
||||||
|
|
||||||
MFEM_VERIFY(f.enthalpyFes != nullptr, "The hydrostatic kernel requires the "
|
MFEM_VERIFY(
|
||||||
"enthalpy finite-element space.");
|
f.enthalpyFes != nullptr, "The hydrostatic kernel requires the "
|
||||||
|
"enthalpy finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.gravityPotentialFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic kernel requires the "
|
f.gravityPotentialFes != nullptr, "The hydrostatic kernel requires the "
|
||||||
"gravity-potential finite-element space.");
|
"gravity-potential finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.displacementFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic kernel requires the "
|
f.displacementFes != nullptr, "The hydrostatic kernel requires the "
|
||||||
"displacement finite-element space.");
|
"displacement finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.compactificationFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic kernel requires the "
|
f.compactificationFes != nullptr, "The hydrostatic kernel requires the "
|
||||||
"compactification finite-element space.");
|
"compactification finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.compactificationCoordinate != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic kernel requires the "
|
f.compactificationCoordinate != nullptr, "The hydrostatic kernel requires the "
|
||||||
"compactification coordinate.");
|
"compactification coordinate."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic kernel requires the "
|
f.quadratureFactory != nullptr, "The hydrostatic kernel requires the "
|
||||||
"quadrature-rule factory.");
|
"quadrature-rule factory."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.mesh->Dimension() == 3,
|
MFEM_VERIFY(
|
||||||
"The rigid-rotation hydrostatic kernel "
|
f.mesh->Dimension() == 3, "The rigid-rotation hydrostatic kernel "
|
||||||
"currently requires a three-dimensional mesh.");
|
"currently requires a three-dimensional mesh."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(domainMapper.GetDimension() == f.mesh->Dimension(),
|
MFEM_VERIFY(
|
||||||
"The domain-mapper dimension does not match "
|
domainMapper.GetDimension() == f.mesh->Dimension(), "The domain-mapper dimension does not match "
|
||||||
"the mesh dimension.");
|
"the mesh dimension."
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const mfem::IntegrationRule &
|
const mfem::IntegrationRule &get_hydrostatic_rule(
|
||||||
get_hydrostatic_rule(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mfem::FiniteElement &enthalpyElement,
|
const mfem::FiniteElement &enthalpyElement,
|
||||||
const mfem::FiniteElement &potentialElement,
|
const mfem::FiniteElement &potentialElement,
|
||||||
const mfem::ElementTransformation &transformation) {
|
const mfem::ElementTransformation &transformation
|
||||||
|
) {
|
||||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||||
|
|
||||||
MFEM_VERIFY(enthalpyElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||||
"The hydrostatic test element does not match "
|
"The hydrostatic test element does not match "
|
||||||
"the registered enthalpy field.");
|
"the registered enthalpy field."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(potentialElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Gravity::Potential::familyOrder,
|
potentialElement.GetOrder() == mean_field::field::Gravity::Potential::familyOrder,
|
||||||
"The hydrostatic potential element does not "
|
"The hydrostatic potential element does not "
|
||||||
"match the registered gravity-potential field.");
|
"match the registered gravity-potential field."
|
||||||
|
);
|
||||||
|
|
||||||
const auto enthalpyQuery = EnthalpyField::make_query<
|
const auto enthalpyQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
|
||||||
mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
);
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
|
||||||
const auto gravityQuery = EnthalpyField::make_query<
|
const auto gravityQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumGravity>(
|
||||||
mean_field::field::Enthalpy::Form::EquilibriumGravity>(
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
);
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
|
||||||
const auto rotationQuery = EnthalpyField::make_query<
|
const auto rotationQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumRotation>(
|
||||||
mean_field::field::Enthalpy::Form::EquilibriumRotation>(
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{2},
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
transformation.OrderW(), std::array<int, 1>{2},
|
);
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
|
||||||
const auto constantQuery = EnthalpyField::make_query<
|
const auto constantQuery = EnthalpyField::make_query<mean_field::field::Enthalpy::Form::EquilibriumConstant>(
|
||||||
mean_field::field::Enthalpy::Form::EquilibriumConstant>(
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
);
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
|
||||||
int integrationOrder = 0;
|
int integrationOrder = 0;
|
||||||
|
|
||||||
const auto update_order = [&f, &transformation, &integrationOrder](
|
const auto update_order = [&f, &transformation, &integrationOrder](const mean_field::quadrature::Query &query) {
|
||||||
const mean_field::quadrature::Query &query) {
|
const auto rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||||
const auto rule =
|
|
||||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
|
||||||
|
|
||||||
MFEM_VERIFY(rule.integration_rule != nullptr,
|
MFEM_VERIFY(
|
||||||
"The quadrature policy did not return "
|
rule.integration_rule != nullptr, "The quadrature policy did not return "
|
||||||
"a hydrostatic-equilibrium rule.");
|
"a hydrostatic-equilibrium rule."
|
||||||
|
);
|
||||||
|
|
||||||
integrationOrder = std::max(integrationOrder, rule.resolution.order);
|
integrationOrder = std::max(integrationOrder, rule.resolution.order);
|
||||||
};
|
};
|
||||||
@@ -153,9 +161,9 @@ get_hydrostatic_rule(const mean_field::fem::FEM &f,
|
|||||||
update_order(constantQuery);
|
update_order(constantQuery);
|
||||||
|
|
||||||
return mfem::IntRules.Get(transformation.GetGeometryType(), integrationOrder);
|
return mfem::IntRules.Get(transformation.GetGeometryType(), integrationOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HydrostaticAssemblyRequest {
|
struct HydrostaticAssemblyRequest {
|
||||||
const mean_field::physics::RigidRotation *rotation{nullptr};
|
const mean_field::physics::RigidRotation *rotation{nullptr};
|
||||||
|
|
||||||
const mfem::Vector *baseEnthalpyTrue{nullptr};
|
const mfem::Vector *baseEnthalpyTrue{nullptr};
|
||||||
@@ -169,70 +177,78 @@ struct HydrostaticAssemblyRequest {
|
|||||||
double constantVariation{0.0};
|
double constantVariation{0.0};
|
||||||
|
|
||||||
bool buildResidual{false};
|
bool buildResidual{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
void assemble_hydrostatic_form(
|
void assemble_hydrostatic_form(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper,
|
const mean_field::mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &displacementTrue,
|
const mfem::Vector &displacementTrue,
|
||||||
const HydrostaticAssemblyRequest &request, mfem::Vector &result) {
|
const HydrostaticAssemblyRequest &request,
|
||||||
|
mfem::Vector &result
|
||||||
|
) {
|
||||||
validate_fem(f, domainMapper);
|
validate_fem(f, domainMapper);
|
||||||
|
|
||||||
MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
MFEM_VERIFY(
|
||||||
"The hydrostatic displacement vector has "
|
displacementTrue.Size() == f.displacementFes->GetTrueVSize(), "The hydrostatic displacement vector has "
|
||||||
"the wrong size.");
|
"the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(request.bernoulliConstant),
|
MFEM_VERIFY(std::isfinite(request.bernoulliConstant), "The Bernoulli constant is non-finite.");
|
||||||
"The Bernoulli constant is non-finite.");
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(request.constantVariation),
|
MFEM_VERIFY(std::isfinite(request.constantVariation), "The Bernoulli-constant variation is non-finite.");
|
||||||
"The Bernoulli-constant variation is non-finite.");
|
|
||||||
|
|
||||||
const bool requiresBaseState =
|
const bool requiresBaseState = request.buildResidual || request.displacementVariationTrue != nullptr;
|
||||||
request.buildResidual || request.displacementVariationTrue != nullptr;
|
|
||||||
|
|
||||||
if (requiresBaseState) {
|
if (requiresBaseState) {
|
||||||
MFEM_VERIFY(request.rotation != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic residual or geometry "
|
request.rotation != nullptr, "The hydrostatic residual or geometry "
|
||||||
"action requires the rotation model.");
|
"action requires the rotation model."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(request.baseEnthalpyTrue != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic residual or geometry "
|
request.baseEnthalpyTrue != nullptr, "The hydrostatic residual or geometry "
|
||||||
"action requires the base enthalpy.");
|
"action requires the base enthalpy."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(request.basePotentialTrue != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic residual or geometry "
|
request.basePotentialTrue != nullptr, "The hydrostatic residual or geometry "
|
||||||
"action requires the base potential.");
|
"action requires the base potential."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.baseEnthalpyTrue != nullptr) {
|
if (request.baseEnthalpyTrue != nullptr) {
|
||||||
MFEM_VERIFY(request.baseEnthalpyTrue->Size() ==
|
MFEM_VERIFY(
|
||||||
f.enthalpyFes->GetTrueVSize(),
|
request.baseEnthalpyTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||||
"The base enthalpy vector has the wrong size.");
|
"The base enthalpy vector has the wrong size."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.basePotentialTrue != nullptr) {
|
if (request.basePotentialTrue != nullptr) {
|
||||||
MFEM_VERIFY(request.basePotentialTrue->Size() ==
|
MFEM_VERIFY(
|
||||||
f.gravityPotentialFes->GetTrueVSize(),
|
request.basePotentialTrue->Size() == f.gravityPotentialFes->GetTrueVSize(),
|
||||||
"The base potential vector has the wrong size.");
|
"The base potential vector has the wrong size."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.enthalpyVariationTrue != nullptr) {
|
if (request.enthalpyVariationTrue != nullptr) {
|
||||||
MFEM_VERIFY(request.enthalpyVariationTrue->Size() ==
|
MFEM_VERIFY(
|
||||||
f.enthalpyFes->GetTrueVSize(),
|
request.enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||||
"The enthalpy variation has the wrong size.");
|
"The enthalpy variation has the wrong size."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.potentialVariationTrue != nullptr) {
|
if (request.potentialVariationTrue != nullptr) {
|
||||||
MFEM_VERIFY(request.potentialVariationTrue->Size() ==
|
MFEM_VERIFY(
|
||||||
f.gravityPotentialFes->GetTrueVSize(),
|
request.potentialVariationTrue->Size() == f.gravityPotentialFes->GetTrueVSize(),
|
||||||
"The potential variation has the wrong size.");
|
"The potential variation has the wrong size."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.displacementVariationTrue != nullptr) {
|
if (request.displacementVariationTrue != nullptr) {
|
||||||
MFEM_VERIFY(request.displacementVariationTrue->Size() ==
|
MFEM_VERIFY(
|
||||||
f.displacementFes->GetTrueVSize(),
|
request.displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
|
||||||
"The displacement variation has the wrong size.");
|
"The displacement variation has the wrong size."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
mfem::Vector displacementLocal;
|
mfem::Vector displacementLocal;
|
||||||
@@ -249,31 +265,26 @@ void assemble_hydrostatic_form(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.basePotentialTrue != nullptr) {
|
if (request.basePotentialTrue != nullptr) {
|
||||||
true_to_local(*f.gravityPotentialFes, *request.basePotentialTrue,
|
true_to_local(*f.gravityPotentialFes, *request.basePotentialTrue, basePotentialLocal);
|
||||||
basePotentialLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.enthalpyVariationTrue != nullptr) {
|
if (request.enthalpyVariationTrue != nullptr) {
|
||||||
true_to_local(*f.enthalpyFes, *request.enthalpyVariationTrue,
|
true_to_local(*f.enthalpyFes, *request.enthalpyVariationTrue, enthalpyVariationLocal);
|
||||||
enthalpyVariationLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.potentialVariationTrue != nullptr) {
|
if (request.potentialVariationTrue != nullptr) {
|
||||||
true_to_local(*f.gravityPotentialFes, *request.potentialVariationTrue,
|
true_to_local(*f.gravityPotentialFes, *request.potentialVariationTrue, potentialVariationLocal);
|
||||||
potentialVariationLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.displacementVariationTrue != nullptr) {
|
if (request.displacementVariationTrue != nullptr) {
|
||||||
true_to_local(*f.displacementFes, *request.displacementVariationTrue,
|
true_to_local(*f.displacementFes, *request.displacementVariationTrue, displacementVariationLocal);
|
||||||
displacementVariationLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mfem::Vector localResult(f.enthalpyFes->GetVSize());
|
mfem::Vector localResult(f.enthalpyFes->GetVSize());
|
||||||
|
|
||||||
localResult = 0.0;
|
localResult = 0.0;
|
||||||
|
|
||||||
mean_field::mapping::DomainMapper::Workspace workspace(
|
mean_field::mapping::DomainMapper::Workspace workspace(f.mesh->Dimension());
|
||||||
f.mesh->Dimension());
|
|
||||||
|
|
||||||
mfem::Array<int> enthalpyDofs;
|
mfem::Array<int> enthalpyDofs;
|
||||||
mfem::Array<int> potentialDofs;
|
mfem::Array<int> potentialDofs;
|
||||||
@@ -293,31 +304,26 @@ void assemble_hydrostatic_form(
|
|||||||
mfem::Vector potentialShape;
|
mfem::Vector potentialShape;
|
||||||
|
|
||||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||||
mfem::ElementTransformation *transformation =
|
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||||
f.mesh->GetElementTransformation(elementId);
|
|
||||||
|
|
||||||
MFEM_VERIFY(transformation != nullptr,
|
MFEM_VERIFY(
|
||||||
"The hydrostatic kernel received a null "
|
transformation != nullptr, "The hydrostatic kernel received a null "
|
||||||
"element transformation.");
|
"element transformation."
|
||||||
|
);
|
||||||
|
|
||||||
if (is_vacuum_attribute(transformation->Attribute)) {
|
if (is_vacuum_attribute(transformation->Attribute)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::FiniteElement &enthalpyElement =
|
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
|
||||||
*f.enthalpyFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &potentialElement =
|
const mfem::FiniteElement &potentialElement = *f.gravityPotentialFes->GetFE(elementId);
|
||||||
*f.gravityPotentialFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &displacementElement =
|
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||||
*f.displacementFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &compactificationElement =
|
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||||
*f.compactificationFes->GetFE(elementId);
|
|
||||||
|
|
||||||
mfem::DofTransformation *enthalpyDofTransformation =
|
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
|
||||||
|
|
||||||
mfem::DofTransformation *potentialDofTransformation =
|
mfem::DofTransformation *potentialDofTransformation =
|
||||||
f.gravityPotentialFes->GetElementDofs(elementId, potentialDofs);
|
f.gravityPotentialFes->GetElementDofs(elementId, potentialDofs);
|
||||||
@@ -330,8 +336,7 @@ void assemble_hydrostatic_form(
|
|||||||
|
|
||||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||||
|
|
||||||
f.compactificationCoordinate->GetSubVector(compactificationDofs,
|
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
|
|
||||||
if (request.baseEnthalpyTrue != nullptr) {
|
if (request.baseEnthalpyTrue != nullptr) {
|
||||||
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
||||||
@@ -342,18 +347,15 @@ void assemble_hydrostatic_form(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.enthalpyVariationTrue != nullptr) {
|
if (request.enthalpyVariationTrue != nullptr) {
|
||||||
enthalpyVariationLocal.GetSubVector(enthalpyDofs,
|
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation);
|
||||||
elementEnthalpyVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.potentialVariationTrue != nullptr) {
|
if (request.potentialVariationTrue != nullptr) {
|
||||||
potentialVariationLocal.GetSubVector(potentialDofs,
|
potentialVariationLocal.GetSubVector(potentialDofs, elementPotentialVariation);
|
||||||
elementPotentialVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.displacementVariationTrue != nullptr) {
|
if (request.displacementVariationTrue != nullptr) {
|
||||||
displacementVariationLocal.GetSubVector(displacementDofs,
|
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||||
elementDisplacementVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enthalpyDofTransformation != nullptr) {
|
if (enthalpyDofTransformation != nullptr) {
|
||||||
@@ -372,8 +374,7 @@ void assemble_hydrostatic_form(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.potentialVariationTrue != nullptr) {
|
if (request.potentialVariationTrue != nullptr) {
|
||||||
potentialDofTransformation->InvTransformPrimal(
|
potentialDofTransformation->InvTransformPrimal(elementPotentialVariation);
|
||||||
elementPotentialVariation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,34 +382,33 @@ void assemble_hydrostatic_form(
|
|||||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||||
|
|
||||||
if (request.displacementVariationTrue != nullptr) {
|
if (request.displacementVariationTrue != nullptr) {
|
||||||
displacementDofTransformation->InvTransformPrimal(
|
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||||
elementDisplacementVariation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compactificationDofTransformation != nullptr) {
|
if (compactificationDofTransformation != nullptr) {
|
||||||
compactificationDofTransformation->InvTransformPrimal(
|
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||||
displacementElement, elementDisplacement);
|
|
||||||
|
|
||||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||||
compactificationElement, elementCompactification);
|
compactificationElement, elementCompactification
|
||||||
|
);
|
||||||
|
|
||||||
const mean_field::mapping::ElementMappingData mappingData{
|
const mean_field::mapping::ElementMappingData mappingData{
|
||||||
.displacement = displacementData,
|
.displacement = displacementData, .compactification = compactificationData
|
||||||
.compactification = compactificationData};
|
};
|
||||||
|
|
||||||
std::optional<mean_field::mapping::ElementDisplacementData>
|
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
|
||||||
displacementVariationData;
|
|
||||||
|
|
||||||
if (request.displacementVariationTrue != nullptr) {
|
if (request.displacementVariationTrue != nullptr) {
|
||||||
displacementVariationData.emplace(
|
displacementVariationData.emplace(
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||||
displacementElement, elementDisplacementVariation));
|
displacementElement, elementDisplacementVariation
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
elementResult.SetSize(enthalpyElement.GetDof());
|
elementResult.SetSize(enthalpyElement.GetDof());
|
||||||
@@ -419,28 +419,27 @@ void assemble_hydrostatic_form(
|
|||||||
|
|
||||||
potentialShape.SetSize(potentialElement.GetDof());
|
potentialShape.SetSize(potentialElement.GetDof());
|
||||||
|
|
||||||
const mfem::IntegrationRule &integrationRule = get_hydrostatic_rule(
|
const mfem::IntegrationRule &integrationRule =
|
||||||
f, enthalpyElement, potentialElement, *transformation);
|
get_hydrostatic_rule(f, enthalpyElement, potentialElement, *transformation);
|
||||||
|
|
||||||
for (int quadraturePoint = 0;
|
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
|
||||||
quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
|
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadraturePoint);
|
||||||
const mfem::IntegrationPoint &integrationPoint =
|
|
||||||
integrationRule.IntPoint(quadraturePoint);
|
|
||||||
|
|
||||||
transformation->SetIntPoint(&integrationPoint);
|
transformation->SetIntPoint(&integrationPoint);
|
||||||
|
|
||||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus mappingStatus =
|
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||||
domainMapper.EvaluateVolume(mappingData, *transformation,
|
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||||
integrationPoint, workspace,
|
);
|
||||||
mappingContext);
|
|
||||||
|
|
||||||
MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
|
MFEM_VERIFY(
|
||||||
|
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"The base mapping is invalid in the "
|
"The base mapping is invalid in the "
|
||||||
"hydrostatic kernel. Element: "
|
"hydrostatic kernel. Element: "
|
||||||
<< elementId << ", quadrature point: " << quadraturePoint
|
<< elementId << ", quadrature point: " << quadraturePoint
|
||||||
<< ", status: " << static_cast<int>(mappingStatus));
|
<< ", status: " << static_cast<int>(mappingStatus)
|
||||||
|
);
|
||||||
|
|
||||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||||
|
|
||||||
@@ -453,16 +452,14 @@ void assemble_hydrostatic_form(
|
|||||||
|
|
||||||
const double potentialValue = elementBasePotential * potentialShape;
|
const double potentialValue = elementBasePotential * potentialShape;
|
||||||
|
|
||||||
const double rotationPotential = request.rotation->potential(
|
const double rotationPotential =
|
||||||
mappingContext.mapping.physical_position);
|
request.rotation->potential(mappingContext.mapping.physical_position);
|
||||||
|
|
||||||
baseIntegrand = enthalpyValue + potentialValue - rotationPotential -
|
baseIntegrand = enthalpyValue + potentialValue - rotationPotential - request.bernoulliConstant;
|
||||||
request.bernoulliConstant;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.buildResidual) {
|
if (request.buildResidual) {
|
||||||
elementResult.Add(mappingContext.quadrature.weight * baseIntegrand,
|
elementResult.Add(mappingContext.quadrature.weight * baseIntegrand, enthalpyShape);
|
||||||
enthalpyShape);
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -477,29 +474,27 @@ void assemble_hydrostatic_form(
|
|||||||
materialVariation += elementPotentialVariation * potentialShape;
|
materialVariation += elementPotentialVariation * potentialShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
double weightedVariation =
|
double weightedVariation = mappingContext.quadrature.weight * materialVariation;
|
||||||
mappingContext.quadrature.weight * materialVariation;
|
|
||||||
|
|
||||||
if (request.displacementVariationTrue != nullptr) {
|
if (request.displacementVariationTrue != nullptr) {
|
||||||
mean_field::mapping::VolumeMappingVariation mappingVariation;
|
mean_field::mapping::VolumeMappingVariation mappingVariation;
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus variationStatus =
|
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||||
domainMapper.EvaluateVolumeVariation(
|
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||||
mappingData, *displacementVariationData, *transformation,
|
workspace, mappingVariation
|
||||||
integrationPoint, mappingContext, workspace, mappingVariation);
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(variationStatus ==
|
MFEM_VERIFY(
|
||||||
mean_field::mapping::MappingStatus::valid,
|
variationStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"The mapping variation is invalid "
|
"The mapping variation is invalid "
|
||||||
"in the hydrostatic kernel.");
|
"in the hydrostatic kernel."
|
||||||
|
);
|
||||||
|
|
||||||
const double rotationVariation =
|
const double rotationVariation = request.rotation->potential_directional_derivative(
|
||||||
request.rotation->potential_directional_derivative(
|
mappingContext.mapping.physical_position, mappingVariation.mapping.physical_position_variation
|
||||||
mappingContext.mapping.physical_position,
|
);
|
||||||
mappingVariation.mapping.physical_position_variation);
|
|
||||||
|
|
||||||
weightedVariation +=
|
weightedVariation += baseIntegrand * mappingVariation.weight_variation -
|
||||||
baseIntegrand * mappingVariation.weight_variation -
|
|
||||||
rotationVariation * mappingContext.quadrature.weight;
|
rotationVariation * mappingContext.quadrature.weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,15 +509,20 @@ void assemble_hydrostatic_form(
|
|||||||
}
|
}
|
||||||
|
|
||||||
local_to_true(*f.enthalpyFes, localResult, result);
|
local_to_true(*f.enthalpyFes, localResult, result);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace mean_field::operators::kernels {
|
namespace mean_field::operators::kernels {
|
||||||
void apply_hydrostatic_equilibrium(
|
void apply_hydrostatic_equilibrium(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
const physics::RigidRotation &rotation, const mfem::Vector &enthalpyTrue,
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &potentialTrue, const mfem::Vector &displacementTrue,
|
const physics::RigidRotation &rotation,
|
||||||
const double bernoulliConstant, mfem::Vector &residual) {
|
const mfem::Vector &enthalpyTrue,
|
||||||
|
const mfem::Vector &potentialTrue,
|
||||||
|
const mfem::Vector &displacementTrue,
|
||||||
|
const double bernoulliConstant,
|
||||||
|
mfem::Vector &residual
|
||||||
|
) {
|
||||||
HydrostaticAssemblyRequest request;
|
HydrostaticAssemblyRequest request;
|
||||||
|
|
||||||
request.rotation = &rotation;
|
request.rotation = &rotation;
|
||||||
@@ -531,50 +531,62 @@ void apply_hydrostatic_equilibrium(
|
|||||||
request.bernoulliConstant = bernoulliConstant;
|
request.bernoulliConstant = bernoulliConstant;
|
||||||
request.buildResidual = true;
|
request.buildResidual = true;
|
||||||
|
|
||||||
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request,
|
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, residual);
|
||||||
residual);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void apply_hydrostatic_equilibrium_enthalpy_action(
|
void apply_hydrostatic_equilibrium_enthalpy_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &enthalpyVariationTrue,
|
const mfem::Vector &enthalpyVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &action) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &action
|
||||||
|
) {
|
||||||
HydrostaticAssemblyRequest request;
|
HydrostaticAssemblyRequest request;
|
||||||
|
|
||||||
request.enthalpyVariationTrue = &enthalpyVariationTrue;
|
request.enthalpyVariationTrue = &enthalpyVariationTrue;
|
||||||
|
|
||||||
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_hydrostatic_equilibrium_potential_action(
|
void apply_hydrostatic_equilibrium_potential_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &potentialVariationTrue,
|
const mfem::Vector &potentialVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &action) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &action
|
||||||
|
) {
|
||||||
HydrostaticAssemblyRequest request;
|
HydrostaticAssemblyRequest request;
|
||||||
|
|
||||||
request.potentialVariationTrue = &potentialVariationTrue;
|
request.potentialVariationTrue = &potentialVariationTrue;
|
||||||
|
|
||||||
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_hydrostatic_equilibrium_constant_action(
|
void apply_hydrostatic_equilibrium_constant_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
const double constantVariation, const mfem::Vector &displacementTrue,
|
const mapping::DomainMapper &domainMapper,
|
||||||
mfem::Vector &action) {
|
const double constantVariation,
|
||||||
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &action
|
||||||
|
) {
|
||||||
HydrostaticAssemblyRequest request;
|
HydrostaticAssemblyRequest request;
|
||||||
|
|
||||||
request.constantVariation = constantVariation;
|
request.constantVariation = constantVariation;
|
||||||
|
|
||||||
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
assemble_hydrostatic_form(f, domainMapper, displacementTrue, request, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_hydrostatic_equilibrium_displacement_action(
|
void apply_hydrostatic_equilibrium_displacement_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const physics::RigidRotation &rotation,
|
const physics::RigidRotation &rotation,
|
||||||
const mfem::Vector &baseEnthalpyTrue, const mfem::Vector &basePotentialTrue,
|
const mfem::Vector &baseEnthalpyTrue,
|
||||||
|
const mfem::Vector &basePotentialTrue,
|
||||||
const mfem::Vector &baseDisplacementTrue,
|
const mfem::Vector &baseDisplacementTrue,
|
||||||
const double baseBernoulliConstant,
|
const double baseBernoulliConstant,
|
||||||
const mfem::Vector &displacementVariationTrue, mfem::Vector &action) {
|
const mfem::Vector &displacementVariationTrue,
|
||||||
|
mfem::Vector &action
|
||||||
|
) {
|
||||||
HydrostaticAssemblyRequest request;
|
HydrostaticAssemblyRequest request;
|
||||||
|
|
||||||
request.rotation = &rotation;
|
request.rotation = &rotation;
|
||||||
@@ -583,19 +595,23 @@ void apply_hydrostatic_equilibrium_displacement_action(
|
|||||||
request.displacementVariationTrue = &displacementVariationTrue;
|
request.displacementVariationTrue = &displacementVariationTrue;
|
||||||
request.bernoulliConstant = baseBernoulliConstant;
|
request.bernoulliConstant = baseBernoulliConstant;
|
||||||
|
|
||||||
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request,
|
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request, action);
|
||||||
action);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void apply_hydrostatic_equilibrium_action(
|
void apply_hydrostatic_equilibrium_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const physics::RigidRotation &rotation,
|
const physics::RigidRotation &rotation,
|
||||||
const mfem::Vector &baseEnthalpyTrue, const mfem::Vector &basePotentialTrue,
|
const mfem::Vector &baseEnthalpyTrue,
|
||||||
|
const mfem::Vector &basePotentialTrue,
|
||||||
const mfem::Vector &baseDisplacementTrue,
|
const mfem::Vector &baseDisplacementTrue,
|
||||||
const double baseBernoulliConstant,
|
const double baseBernoulliConstant,
|
||||||
const mfem::Vector &enthalpyVariationTrue,
|
const mfem::Vector &enthalpyVariationTrue,
|
||||||
const mfem::Vector &potentialVariationTrue, const double constantVariation,
|
const mfem::Vector &potentialVariationTrue,
|
||||||
const mfem::Vector &displacementVariationTrue, mfem::Vector &action) {
|
const double constantVariation,
|
||||||
|
const mfem::Vector &displacementVariationTrue,
|
||||||
|
mfem::Vector &action
|
||||||
|
) {
|
||||||
HydrostaticAssemblyRequest request;
|
HydrostaticAssemblyRequest request;
|
||||||
|
|
||||||
request.rotation = &rotation;
|
request.rotation = &rotation;
|
||||||
@@ -607,7 +623,6 @@ void apply_hydrostatic_equilibrium_action(
|
|||||||
request.bernoulliConstant = baseBernoulliConstant;
|
request.bernoulliConstant = baseBernoulliConstant;
|
||||||
request.constantVariation = constantVariation;
|
request.constantVariation = constantVariation;
|
||||||
|
|
||||||
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request,
|
assemble_hydrostatic_form(f, domainMapper, baseDisplacementTrue, request, action);
|
||||||
action);
|
}
|
||||||
}
|
|
||||||
} // namespace mean_field::operators::kernels
|
} // namespace mean_field::operators::kernels
|
||||||
|
|||||||
@@ -12,54 +12,64 @@ module mean_field;
|
|||||||
import :operators.kernels.pressure_force;
|
import :operators.kernels.pressure_force;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
namespace eos = mean_field::eos;
|
||||||
|
|
||||||
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
return DomainSchema::template attribute_belongs_to<
|
|
||||||
mean_field::utils::domain::Vacuum>(attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class PressureForceAction { residual, enthalpy, displacement };
|
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
||||||
|
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
void true_to_local(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
enum class PressureForceAction { residual, enthalpy, displacement };
|
||||||
const mfem::Vector &trueVector, mfem::Vector &localVector) {
|
|
||||||
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
void true_to_local(
|
||||||
"The pressure-force true vector has the wrong size.");
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
|
const mfem::Vector &trueVector,
|
||||||
|
mfem::Vector &localVector
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
trueVector.Size() == finiteElementSpace.GetTrueVSize(), "The pressure-force true vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||||
finiteElementSpace.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->Mult(trueVector, localVector);
|
prolongation->Mult(trueVector, localVector);
|
||||||
} else {
|
} else {
|
||||||
localVector = trueVector;
|
localVector = trueVector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
void local_to_true(
|
||||||
const mfem::Vector &localVector, mfem::Vector &trueVector) {
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(),
|
const mfem::Vector &localVector,
|
||||||
"The pressure-force local vector has the wrong size.");
|
mfem::Vector &trueVector
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
localVector.Size() == finiteElementSpace.GetVSize(), "The pressure-force local vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||||
trueVector = 0.0;
|
trueVector = 0.0;
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||||
finiteElementSpace.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->MultTranspose(localVector, trueVector);
|
prolongation->MultTranspose(localVector, trueVector);
|
||||||
} else {
|
} else {
|
||||||
trueVector = localVector;
|
trueVector = localVector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int vector_dof_index(const mfem::Ordering::Type ordering,
|
[[nodiscard]] int vector_dof_index(
|
||||||
const int scalarDof, const int component,
|
const mfem::Ordering::Type ordering,
|
||||||
|
const int scalarDof,
|
||||||
|
const int component,
|
||||||
const int scalarDofCount,
|
const int scalarDofCount,
|
||||||
const int dimension) {
|
const int dimension
|
||||||
|
) {
|
||||||
if (ordering == mfem::Ordering::byNODES) {
|
if (ordering == mfem::Ordering::byNODES) {
|
||||||
return scalarDof + component * scalarDofCount;
|
return scalarDof + component * scalarDofCount;
|
||||||
}
|
}
|
||||||
@@ -70,10 +80,9 @@ void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
|||||||
|
|
||||||
MFEM_ABORT("The displacement space uses an unsupported ordering.");
|
MFEM_ABORT("The displacement space uses an unsupported ordering.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int
|
[[nodiscard]] int get_pressure_extra_order(const mean_field::eos::Polytrope &barotrope) {
|
||||||
get_pressure_extra_order(const mean_field::eos::Polytrope &barotrope) {
|
|
||||||
/*
|
/*
|
||||||
* Pressure has the enthalpy dependence
|
* Pressure has the enthalpy dependence
|
||||||
*
|
*
|
||||||
@@ -84,92 +93,108 @@ get_pressure_extra_order(const mean_field::eos::Polytrope &barotrope) {
|
|||||||
* contribution is therefore n times that order.
|
* contribution is therefore n times that order.
|
||||||
*/
|
*/
|
||||||
const double extraOrder =
|
const double extraOrder =
|
||||||
barotrope.polytropic_index() *
|
barotrope.polytropic_index() * static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
|
||||||
static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(extraOrder) && extraOrder >= 0.0 &&
|
MFEM_VERIFY(
|
||||||
extraOrder <=
|
std::isfinite(extraOrder) && extraOrder >= 0.0 &&
|
||||||
static_cast<double>(std::numeric_limits<int>::max()),
|
extraOrder <= static_cast<double>(std::numeric_limits<int>::max()),
|
||||||
"The pressure EOS effective polynomial order is invalid.");
|
"The pressure EOS effective polynomial order is invalid."
|
||||||
|
);
|
||||||
|
|
||||||
return static_cast<int>(std::ceil(extraOrder));
|
return static_cast<int>(std::ceil(extraOrder));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const mfem::IntegrationRule &
|
[[nodiscard]] const mfem::IntegrationRule &get_pressure_force_rule(
|
||||||
get_pressure_force_rule(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::eos::Polytrope &barotrope,
|
const mean_field::eos::Polytrope &barotrope,
|
||||||
const mfem::FiniteElement &enthalpyElement,
|
const mfem::FiniteElement &enthalpyElement,
|
||||||
const mfem::FiniteElement &displacementElement,
|
const mfem::FiniteElement &displacementElement,
|
||||||
const mfem::ElementTransformation &transformation) {
|
const mfem::ElementTransformation &transformation
|
||||||
|
) {
|
||||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||||
|
|
||||||
MFEM_VERIFY(enthalpyElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Enthalpy::Scalar::familyOrder,
|
enthalpyElement.GetOrder() == mean_field::field::Enthalpy::Scalar::familyOrder,
|
||||||
"The pressure-force enthalpy element does not match the "
|
"The pressure-force enthalpy element does not match the "
|
||||||
"registered enthalpy field.");
|
"registered enthalpy field."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(displacementElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Displacement::Vector::familyOrder,
|
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
|
||||||
"The pressure-force test element does not match the "
|
"The pressure-force test element does not match the "
|
||||||
"registered displacement field.");
|
"registered displacement field."
|
||||||
|
);
|
||||||
|
|
||||||
const mean_field::quadrature::Query query = EnthalpyField::make_query<
|
const mean_field::quadrature::Query query =
|
||||||
mean_field::field::Enthalpy::Form::PressureForce>(
|
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(),
|
||||||
transformation.OrderW(),
|
std::array<int, 1>{get_pressure_extra_order(barotrope)}, mean_field::utils::DOMAINS::STELLAR,
|
||||||
std::array<int, 1>{get_pressure_extra_order(barotrope)},
|
mean_field::quadrature::MappingKind::general
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
);
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
|
||||||
const mean_field::quadrature::MfemRule rule =
|
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
|
||||||
|
|
||||||
MFEM_VERIFY(rule.integration_rule != nullptr,
|
MFEM_VERIFY(
|
||||||
"The quadrature policy did not return a pressure-force "
|
rule.integration_rule != nullptr, "The quadrature policy did not return a pressure-force "
|
||||||
"integration rule.");
|
"integration rule."
|
||||||
|
);
|
||||||
|
|
||||||
return *rule.integration_rule;
|
return *rule.integration_rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_inputs(
|
void validate_inputs(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper,
|
const mean_field::mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &enthalpyTrue, const mfem::Vector &displacementTrue) {
|
const mfem::Vector &enthalpyTrue,
|
||||||
|
const mfem::Vector &displacementTrue
|
||||||
|
) {
|
||||||
MFEM_VERIFY(f.mesh != nullptr, "The pressure-force kernel requires a mesh.");
|
MFEM_VERIFY(f.mesh != nullptr, "The pressure-force kernel requires a mesh.");
|
||||||
|
|
||||||
MFEM_VERIFY(f.enthalpyFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The pressure-force kernel requires the enthalpy "
|
f.enthalpyFes != nullptr, "The pressure-force kernel requires the enthalpy "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.displacementFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The pressure-force kernel requires the displacement "
|
f.displacementFes != nullptr, "The pressure-force kernel requires the displacement "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.compactificationFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The pressure-force kernel requires the compactification "
|
f.compactificationFes != nullptr, "The pressure-force kernel requires the compactification "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.compactificationCoordinate != nullptr,
|
MFEM_VERIFY(
|
||||||
"The pressure-force kernel requires the compactification "
|
f.compactificationCoordinate != nullptr, "The pressure-force kernel requires the compactification "
|
||||||
"coordinate.");
|
"coordinate."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
MFEM_VERIFY(
|
||||||
"The pressure-force kernel requires the quadrature "
|
f.quadratureFactory != nullptr, "The pressure-force kernel requires the quadrature "
|
||||||
"rule factory.");
|
"rule factory."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
|
MFEM_VERIFY(
|
||||||
"The pressure-force enthalpy vector has the wrong size.");
|
enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
|
||||||
|
"The pressure-force enthalpy vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
MFEM_VERIFY(
|
||||||
"The pressure-force displacement vector has the wrong size.");
|
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||||
|
"The pressure-force displacement vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(domainMapper.GetDimension() == f.mesh->Dimension(),
|
MFEM_VERIFY(
|
||||||
|
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||||
"The pressure-force domain-mapper dimension does not match "
|
"The pressure-force domain-mapper dimension does not match "
|
||||||
"the mesh dimension.");
|
"the mesh dimension."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
MFEM_VERIFY(
|
||||||
"The displacement vector dimension does not match the "
|
f.displacementFes->GetVDim() == f.mesh->Dimension(), "The displacement vector dimension does not match the "
|
||||||
"mesh dimension.");
|
"mesh dimension."
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ElementDisplacementDataFromElementVDofs currently consumes the
|
* ElementDisplacementDataFromElementVDofs currently consumes the
|
||||||
@@ -177,12 +202,14 @@ void validate_inputs(
|
|||||||
* registry change fails immediately rather than silently
|
* registry change fails immediately rather than silently
|
||||||
* corrupting the geometry.
|
* corrupting the geometry.
|
||||||
*/
|
*/
|
||||||
MFEM_VERIFY(f.displacementFes->GetOrdering() == mfem::Ordering::byNODES,
|
MFEM_VERIFY(
|
||||||
|
f.displacementFes->GetOrdering() == mfem::Ordering::byNODES,
|
||||||
"The pressure-force kernel requires the registered byNODES "
|
"The pressure-force kernel requires the registered byNODES "
|
||||||
"displacement ordering.");
|
"displacement ordering."
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void apply_pressure_force_action(
|
void apply_pressure_force_action(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper,
|
const mean_field::mapping::DomainMapper &domainMapper,
|
||||||
const mean_field::eos::Polytrope &barotrope,
|
const mean_field::eos::Polytrope &barotrope,
|
||||||
@@ -190,22 +217,25 @@ void apply_pressure_force_action(
|
|||||||
const mfem::Vector &baseEnthalpyTrue,
|
const mfem::Vector &baseEnthalpyTrue,
|
||||||
const mfem::Vector *enthalpyVariationTrue,
|
const mfem::Vector *enthalpyVariationTrue,
|
||||||
const mfem::Vector *displacementVariationTrue,
|
const mfem::Vector *displacementVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
validate_inputs(f, domainMapper, baseEnthalpyTrue, displacementTrue);
|
validate_inputs(f, domainMapper, baseEnthalpyTrue, displacementTrue);
|
||||||
|
|
||||||
if (pressureForceAction == PressureForceAction::enthalpy) {
|
if (pressureForceAction == PressureForceAction::enthalpy) {
|
||||||
MFEM_VERIFY(enthalpyVariationTrue != nullptr &&
|
MFEM_VERIFY(
|
||||||
enthalpyVariationTrue->Size() ==
|
enthalpyVariationTrue != nullptr && enthalpyVariationTrue->Size() == f.enthalpyFes->GetTrueVSize(),
|
||||||
f.enthalpyFes->GetTrueVSize(),
|
"The pressure-force enthalpy variation has the wrong size."
|
||||||
"The pressure-force enthalpy variation has the wrong size.");
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pressureForceAction == PressureForceAction::displacement) {
|
if (pressureForceAction == PressureForceAction::displacement) {
|
||||||
MFEM_VERIFY(displacementVariationTrue != nullptr &&
|
MFEM_VERIFY(
|
||||||
displacementVariationTrue->Size() ==
|
displacementVariationTrue != nullptr &&
|
||||||
f.displacementFes->GetTrueVSize(),
|
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
|
||||||
"The pressure-force displacement variation has the wrong "
|
"The pressure-force displacement variation has the wrong "
|
||||||
"size.");
|
"size."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
mfem::Vector baseEnthalpyLocal;
|
mfem::Vector baseEnthalpyLocal;
|
||||||
@@ -216,22 +246,19 @@ void apply_pressure_force_action(
|
|||||||
true_to_local(*f.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
|
true_to_local(*f.enthalpyFes, baseEnthalpyTrue, baseEnthalpyLocal);
|
||||||
|
|
||||||
if (enthalpyVariationTrue != nullptr) {
|
if (enthalpyVariationTrue != nullptr) {
|
||||||
true_to_local(*f.enthalpyFes, *enthalpyVariationTrue,
|
true_to_local(*f.enthalpyFes, *enthalpyVariationTrue, enthalpyVariationLocal);
|
||||||
enthalpyVariationLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||||
|
|
||||||
if (displacementVariationTrue != nullptr) {
|
if (displacementVariationTrue != nullptr) {
|
||||||
true_to_local(*f.displacementFes, *displacementVariationTrue,
|
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
|
||||||
displacementVariationLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mfem::Vector localAction(f.displacementFes->GetVSize());
|
mfem::Vector localAction(f.displacementFes->GetVSize());
|
||||||
localAction = 0.0;
|
localAction = 0.0;
|
||||||
|
|
||||||
mean_field::mapping::DomainMapper::Workspace workspace(
|
mean_field::mapping::DomainMapper::Workspace workspace(f.mesh->Dimension());
|
||||||
f.mesh->Dimension());
|
|
||||||
|
|
||||||
mfem::Array<int> enthalpyDofsofs;
|
mfem::Array<int> enthalpyDofsofs;
|
||||||
mfem::Array<int> displacementDofs;
|
mfem::Array<int> displacementDofs;
|
||||||
@@ -255,16 +282,15 @@ void apply_pressure_force_action(
|
|||||||
|
|
||||||
const int dimension = f.mesh->Dimension();
|
const int dimension = f.mesh->Dimension();
|
||||||
|
|
||||||
const mfem::Ordering::Type displacementOrdering =
|
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
|
||||||
f.displacementFes->GetOrdering();
|
|
||||||
|
|
||||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||||
mfem::ElementTransformation *transformation =
|
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||||
f.mesh->GetElementTransformation(elementId);
|
|
||||||
|
|
||||||
MFEM_VERIFY(transformation != nullptr,
|
MFEM_VERIFY(
|
||||||
"The pressure-force kernel received a null element "
|
transformation != nullptr, "The pressure-force kernel received a null element "
|
||||||
"transformation.");
|
"transformation."
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Skip vacuum before constructing or evaluating any mapping
|
* Skip vacuum before constructing or evaluating any mapping
|
||||||
@@ -274,17 +300,13 @@ void apply_pressure_force_action(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::FiniteElement &enthalpyElement =
|
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
|
||||||
*f.enthalpyFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &displacementElement =
|
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||||
*f.displacementFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &compactificationElement =
|
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||||
*f.compactificationFes->GetFE(elementId);
|
|
||||||
|
|
||||||
mfem::DofTransformation *enthalpyDofTransformation =
|
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
|
||||||
|
|
||||||
mfem::DofTransformation *displacementDofTransformation =
|
mfem::DofTransformation *displacementDofTransformation =
|
||||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||||
@@ -295,19 +317,16 @@ void apply_pressure_force_action(
|
|||||||
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
baseEnthalpyLocal.GetSubVector(enthalpyDofs, elementBaseEnthalpy);
|
||||||
|
|
||||||
if (enthalpyVariationTrue != nullptr) {
|
if (enthalpyVariationTrue != nullptr) {
|
||||||
enthalpyVariationLocal.GetSubVector(enthalpyDofs,
|
enthalpyVariationLocal.GetSubVector(enthalpyDofs, elementEnthalpyVariation);
|
||||||
elementEnthalpyVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||||
|
|
||||||
if (displacementVariationTrue != nullptr) {
|
if (displacementVariationTrue != nullptr) {
|
||||||
displacementVariationLocal.GetSubVector(displacementDofs,
|
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||||
elementDisplacementVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.compactificationCoordinate->GetSubVector(compactificationDofs,
|
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
|
|
||||||
if (enthalpyDofTransformation != nullptr) {
|
if (enthalpyDofTransformation != nullptr) {
|
||||||
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
|
enthalpyDofTransformation->InvTransformPrimal(elementBaseEnthalpy);
|
||||||
@@ -321,42 +340,42 @@ void apply_pressure_force_action(
|
|||||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||||
|
|
||||||
if (displacementVariationTrue != nullptr) {
|
if (displacementVariationTrue != nullptr) {
|
||||||
displacementDofTransformation->InvTransformPrimal(
|
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||||
elementDisplacementVariation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compactificationDofTransformation != nullptr) {
|
if (compactificationDofTransformation != nullptr) {
|
||||||
compactificationDofTransformation->InvTransformPrimal(
|
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||||
displacementElement, elementDisplacement);
|
|
||||||
|
|
||||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||||
compactificationElement, elementCompactification);
|
compactificationElement, elementCompactification
|
||||||
|
);
|
||||||
|
|
||||||
const mean_field::mapping::ElementMappingData mappingData{
|
const mean_field::mapping::ElementMappingData mappingData{
|
||||||
.displacement = displacementData,
|
.displacement = displacementData, .compactification = compactificationData
|
||||||
.compactification = compactificationData};
|
};
|
||||||
|
|
||||||
std::optional<mean_field::mapping::ElementDisplacementData>
|
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
|
||||||
displacementVariationData;
|
|
||||||
|
|
||||||
if (displacementVariationTrue != nullptr) {
|
if (displacementVariationTrue != nullptr) {
|
||||||
displacementVariationData.emplace(
|
displacementVariationData.emplace(
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||||
displacementElement, elementDisplacementVariation));
|
displacementElement, elementDisplacementVariation
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
||||||
|
|
||||||
MFEM_VERIFY(displacementDofs.Size() ==
|
MFEM_VERIFY(
|
||||||
scalarDisplacementDofCount * dimension,
|
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
|
||||||
"The pressure-force element displacement vector has "
|
"The pressure-force element displacement vector has "
|
||||||
"the wrong size.");
|
"the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||||
|
|
||||||
@@ -364,34 +383,30 @@ void apply_pressure_force_action(
|
|||||||
|
|
||||||
displacementDShapePhysical.SetSize(scalarDisplacementDofCount, dimension);
|
displacementDShapePhysical.SetSize(scalarDisplacementDofCount, dimension);
|
||||||
|
|
||||||
displacementDShapePhysicalVariation.SetSize(scalarDisplacementDofCount,
|
displacementDShapePhysicalVariation.SetSize(scalarDisplacementDofCount, dimension);
|
||||||
dimension);
|
|
||||||
|
|
||||||
elementAction.SetSize(displacementDofs.Size());
|
elementAction.SetSize(displacementDofs.Size());
|
||||||
elementAction = 0.0;
|
elementAction = 0.0;
|
||||||
|
|
||||||
const mfem::IntegrationRule &integrationRule = get_pressure_force_rule(
|
const mfem::IntegrationRule &integrationRule =
|
||||||
f, barotrope, enthalpyElement, displacementElement, *transformation);
|
get_pressure_force_rule(f, barotrope, enthalpyElement, displacementElement, *transformation);
|
||||||
|
|
||||||
for (int quadratureIndex = 0;
|
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
||||||
quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
|
||||||
const mfem::IntegrationPoint &integrationPoint =
|
|
||||||
integrationRule.IntPoint(quadratureIndex);
|
|
||||||
|
|
||||||
transformation->SetIntPoint(&integrationPoint);
|
transformation->SetIntPoint(&integrationPoint);
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus mappingStatus =
|
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||||
domainMapper.EvaluateVolume(mappingData, *transformation,
|
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||||
integrationPoint, workspace,
|
);
|
||||||
mappingContext);
|
|
||||||
|
|
||||||
MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
|
MFEM_VERIFY(
|
||||||
|
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"Stateless mapping failed in the pressure-force "
|
"Stateless mapping failed in the pressure-force "
|
||||||
"kernel. Element: "
|
"kernel. Element: "
|
||||||
<< elementId
|
<< elementId << ", attribute: " << transformation->Attribute
|
||||||
<< ", attribute: " << transformation->Attribute
|
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||||
<< ", quadrature point: " << quadratureIndex
|
);
|
||||||
<< ", status: " << static_cast<int>(mappingStatus));
|
|
||||||
|
|
||||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||||
|
|
||||||
@@ -401,18 +416,20 @@ void apply_pressure_force_action(
|
|||||||
|
|
||||||
if (pressureForceAction == PressureForceAction::residual ||
|
if (pressureForceAction == PressureForceAction::residual ||
|
||||||
pressureForceAction == PressureForceAction::displacement) {
|
pressureForceAction == PressureForceAction::displacement) {
|
||||||
pressureFactor = barotrope.pressure_from_enthalpy(enthalpyValue);
|
|
||||||
} else {
|
|
||||||
const double enthalpyVariationValue =
|
|
||||||
elementEnthalpyVariation * enthalpyShape;
|
|
||||||
|
|
||||||
pressureFactor =
|
pressureFactor =
|
||||||
barotrope.pressure_derivative_from_enthalpy(enthalpyValue) *
|
eos::evaluate<eos::quantity::Pressure>(barotrope, eos::SpecificEnthalpyValue{enthalpyValue})
|
||||||
|
.value();
|
||||||
|
} else {
|
||||||
|
const double enthalpyVariationValue = elementEnthalpyVariation * enthalpyShape;
|
||||||
|
|
||||||
|
pressureFactor = eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{enthalpyValue}
|
||||||
|
)
|
||||||
|
.value() *
|
||||||
enthalpyVariationValue;
|
enthalpyVariationValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
displacementElement.CalcDShape(integrationPoint,
|
displacementElement.CalcDShape(integrationPoint, displacementDShapeReference);
|
||||||
displacementDShapeReference);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Row i of DShape is grad_reference(N_i). Multiplication
|
* Row i of DShape is grad_reference(N_i). Multiplication
|
||||||
@@ -421,27 +438,25 @@ void apply_pressure_force_action(
|
|||||||
* grad_physical(N_i)
|
* grad_physical(N_i)
|
||||||
* = grad_reference(N_i) J^{-1}.
|
* = grad_reference(N_i) J^{-1}.
|
||||||
*/
|
*/
|
||||||
mfem::Mult(displacementDShapeReference, mappingContext.quadrature.J_inv,
|
mfem::Mult(displacementDShapeReference, mappingContext.quadrature.J_inv, displacementDShapePhysical);
|
||||||
displacementDShapePhysical);
|
|
||||||
|
|
||||||
std::optional<mean_field::mapping::VolumeMappingVariation>
|
std::optional<mean_field::mapping::VolumeMappingVariation> mappingVariation;
|
||||||
mappingVariation;
|
|
||||||
|
|
||||||
if (pressureForceAction == PressureForceAction::displacement) {
|
if (pressureForceAction == PressureForceAction::displacement) {
|
||||||
mappingVariation.emplace();
|
mappingVariation.emplace();
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus variationStatus =
|
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||||
domainMapper.EvaluateVolumeVariation(
|
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||||
mappingData, *displacementVariationData, *transformation,
|
workspace, *mappingVariation
|
||||||
integrationPoint, mappingContext, workspace, *mappingVariation);
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
variationStatus == mean_field::mapping::MappingStatus::valid,
|
variationStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"Stateless mapping variation failed in the "
|
"Stateless mapping variation failed in the "
|
||||||
"pressure-force kernel. Element: "
|
"pressure-force kernel. Element: "
|
||||||
<< elementId << ", attribute: " << transformation->Attribute
|
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
|
||||||
<< ", quadrature point: " << quadratureIndex
|
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
|
||||||
<< ", status: " << static_cast<int>(variationStatus));
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Differentiating
|
* Differentiating
|
||||||
@@ -452,18 +467,19 @@ void apply_pressure_force_action(
|
|||||||
* test-gradient variation used by the geometric
|
* test-gradient variation used by the geometric
|
||||||
* pressure block.
|
* pressure block.
|
||||||
*/
|
*/
|
||||||
mfem::Mult(displacementDShapeReference,
|
mfem::Mult(
|
||||||
mappingVariation->inverse_element_jacobian_variation,
|
displacementDShapeReference, mappingVariation->inverse_element_jacobian_variation,
|
||||||
displacementDShapePhysicalVariation);
|
displacementDShapePhysicalVariation
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const double weightedPressureFactor =
|
const double weightedPressureFactor = pressureFactor * mappingContext.quadrature.weight;
|
||||||
pressureFactor * mappingContext.quadrature.weight;
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(pressureFactor) &&
|
MFEM_VERIFY(
|
||||||
std::isfinite(weightedPressureFactor),
|
std::isfinite(pressureFactor) && std::isfinite(weightedPressureFactor),
|
||||||
"The pressure-force kernel encountered a non-finite "
|
"The pressure-force kernel encountered a non-finite "
|
||||||
"quadrature value.");
|
"quadrature value."
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For the vector basis N_i e_c,
|
* For the vector basis N_i e_c,
|
||||||
@@ -475,12 +491,11 @@ void apply_pressure_force_action(
|
|||||||
* R_(i,c)
|
* R_(i,c)
|
||||||
* = -integral P partial_c N_i dV.
|
* = -integral P partial_c N_i dV.
|
||||||
*/
|
*/
|
||||||
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount;
|
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
|
||||||
++scalarDof) {
|
|
||||||
for (int component = 0; component < dimension; ++component) {
|
for (int component = 0; component < dimension; ++component) {
|
||||||
const int vectorDof =
|
const int vectorDof = vector_dof_index(
|
||||||
vector_dof_index(displacementOrdering, scalarDof, component,
|
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
|
||||||
scalarDisplacementDofCount, dimension);
|
);
|
||||||
|
|
||||||
if (pressureForceAction == PressureForceAction::displacement) {
|
if (pressureForceAction == PressureForceAction::displacement) {
|
||||||
/*
|
/*
|
||||||
@@ -494,22 +509,20 @@ void apply_pressure_force_action(
|
|||||||
const double gradientWeightVariation =
|
const double gradientWeightVariation =
|
||||||
mappingContext.quadrature.weight *
|
mappingContext.quadrature.weight *
|
||||||
displacementDShapePhysicalVariation(scalarDof, component) +
|
displacementDShapePhysicalVariation(scalarDof, component) +
|
||||||
mappingVariation->weight_variation *
|
mappingVariation->weight_variation * displacementDShapePhysical(scalarDof, component);
|
||||||
displacementDShapePhysical(scalarDof, component);
|
|
||||||
|
|
||||||
const double contribution =
|
const double contribution = pressureFactor * gradientWeightVariation;
|
||||||
pressureFactor * gradientWeightVariation;
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(gradientWeightVariation) &&
|
MFEM_VERIFY(
|
||||||
std::isfinite(contribution),
|
std::isfinite(gradientWeightVariation) && std::isfinite(contribution),
|
||||||
"The pressure-force geometry action "
|
"The pressure-force geometry action "
|
||||||
"encountered a non-finite contribution.");
|
"encountered a non-finite contribution."
|
||||||
|
);
|
||||||
|
|
||||||
elementAction(vectorDof) -= contribution;
|
elementAction(vectorDof) -= contribution;
|
||||||
} else {
|
} else {
|
||||||
elementAction(vectorDof) -=
|
elementAction(vectorDof) -=
|
||||||
weightedPressureFactor *
|
weightedPressureFactor * displacementDShapePhysical(scalarDof, component);
|
||||||
displacementDShapePhysical(scalarDof, component);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -523,38 +536,51 @@ void apply_pressure_force_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
local_to_true(*f.displacementFes, localAction, actionTrue);
|
local_to_true(*f.displacementFes, localAction, actionTrue);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace mean_field::operators::kernels {
|
namespace mean_field::operators::kernels {
|
||||||
void apply_pressure_force_residual(
|
void apply_pressure_force_residual(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
const eos::Polytrope &barotrope, const mfem::Vector &enthalpyTrue,
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &residualTrue) {
|
const eos::Polytrope &barotrope,
|
||||||
apply_pressure_force_action(f, domainMapper, barotrope,
|
const mfem::Vector &enthalpyTrue,
|
||||||
PressureForceAction::residual, enthalpyTrue,
|
const mfem::Vector &displacementTrue,
|
||||||
nullptr, nullptr, displacementTrue, residualTrue);
|
mfem::Vector &residualTrue
|
||||||
}
|
) {
|
||||||
|
|
||||||
void apply_pressure_force_enthalpy_action(
|
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
|
||||||
const eos::Polytrope &barotrope, const mfem::Vector &baseEnthalpyTrue,
|
|
||||||
const mfem::Vector &enthalpyVariationTrue,
|
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
|
||||||
apply_pressure_force_action(f, domainMapper, barotrope,
|
|
||||||
PressureForceAction::enthalpy, baseEnthalpyTrue,
|
|
||||||
&enthalpyVariationTrue, nullptr, displacementTrue,
|
|
||||||
actionTrue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply_pressure_force_displacement_action(
|
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
|
||||||
const eos::Polytrope &barotrope, const mfem::Vector &baseEnthalpyTrue,
|
|
||||||
const mfem::Vector &displacementVariationTrue,
|
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
|
||||||
apply_pressure_force_action(
|
apply_pressure_force_action(
|
||||||
f, domainMapper, barotrope, PressureForceAction::displacement,
|
f, domainMapper, barotrope, PressureForceAction::residual, enthalpyTrue, nullptr, nullptr, displacementTrue,
|
||||||
baseEnthalpyTrue, nullptr, &displacementVariationTrue, displacementTrue,
|
residualTrue
|
||||||
actionTrue);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void apply_pressure_force_enthalpy_action(
|
||||||
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
|
const eos::Polytrope &barotrope,
|
||||||
|
const mfem::Vector &baseEnthalpyTrue,
|
||||||
|
const mfem::Vector &enthalpyVariationTrue,
|
||||||
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
|
apply_pressure_force_action(
|
||||||
|
f, domainMapper, barotrope, PressureForceAction::enthalpy, baseEnthalpyTrue, &enthalpyVariationTrue,
|
||||||
|
nullptr, displacementTrue, actionTrue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_pressure_force_displacement_action(
|
||||||
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
|
const eos::Polytrope &barotrope,
|
||||||
|
const mfem::Vector &baseEnthalpyTrue,
|
||||||
|
const mfem::Vector &displacementVariationTrue,
|
||||||
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
|
apply_pressure_force_action(
|
||||||
|
f, domainMapper, barotrope, PressureForceAction::displacement, baseEnthalpyTrue, nullptr,
|
||||||
|
&displacementVariationTrue, displacementTrue, actionTrue
|
||||||
|
);
|
||||||
|
}
|
||||||
} // namespace mean_field::operators::kernels
|
} // namespace mean_field::operators::kernels
|
||||||
|
|||||||
@@ -11,61 +11,66 @@ module mean_field;
|
|||||||
import :operators.kernels.rotational_displacement_force;
|
import :operators.kernels.rotational_displacement_force;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
||||||
return DomainSchema::template attribute_belongs_to<
|
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
|
||||||
mean_field::utils::domain::Vacuum>(attribute);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
enum class RotationalDisplacementForceAction {
|
enum class RotationalDisplacementForceAction { residual, density, displacement, complete };
|
||||||
residual,
|
|
||||||
density,
|
|
||||||
displacement,
|
|
||||||
complete
|
|
||||||
};
|
|
||||||
|
|
||||||
void true_to_local(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
void true_to_local(
|
||||||
const mfem::Vector &trueVector, mfem::Vector &localVector) {
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
const mfem::Vector &trueVector,
|
||||||
|
mfem::Vector &localVector
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
||||||
"The rotational-displacement-force true vector has the wrong "
|
"The rotational-displacement-force true vector has the wrong "
|
||||||
"size.");
|
"size."
|
||||||
|
);
|
||||||
|
|
||||||
localVector.SetSize(finiteElementSpace.GetVSize());
|
localVector.SetSize(finiteElementSpace.GetVSize());
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||||
finiteElementSpace.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->Mult(trueVector, localVector);
|
prolongation->Mult(trueVector, localVector);
|
||||||
} else {
|
} else {
|
||||||
localVector = trueVector;
|
localVector = trueVector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
void local_to_true(
|
||||||
const mfem::Vector &localVector, mfem::Vector &trueVector) {
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(),
|
const mfem::Vector &localVector,
|
||||||
|
mfem::Vector &trueVector
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
localVector.Size() == finiteElementSpace.GetVSize(),
|
||||||
"The rotational-displacement-force local vector has the wrong "
|
"The rotational-displacement-force local vector has the wrong "
|
||||||
"size.");
|
"size."
|
||||||
|
);
|
||||||
|
|
||||||
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
||||||
trueVector = 0.0;
|
trueVector = 0.0;
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
||||||
finiteElementSpace.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->MultTranspose(localVector, trueVector);
|
prolongation->MultTranspose(localVector, trueVector);
|
||||||
} else {
|
} else {
|
||||||
trueVector = localVector;
|
trueVector = localVector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int vector_dof_index(const mfem::Ordering::Type ordering,
|
[[nodiscard]] int vector_dof_index(
|
||||||
const int scalarDof, const int component,
|
const mfem::Ordering::Type ordering,
|
||||||
|
const int scalarDof,
|
||||||
|
const int component,
|
||||||
const int scalarDofCount,
|
const int scalarDofCount,
|
||||||
const int dimension) {
|
const int dimension
|
||||||
|
) {
|
||||||
if (ordering == mfem::Ordering::byNODES) {
|
if (ordering == mfem::Ordering::byNODES) {
|
||||||
return scalarDof + component * scalarDofCount;
|
return scalarDof + component * scalarDofCount;
|
||||||
}
|
}
|
||||||
@@ -74,110 +79,130 @@ void local_to_true(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
|||||||
return scalarDof * dimension + component;
|
return scalarDof * dimension + component;
|
||||||
}
|
}
|
||||||
|
|
||||||
MFEM_ABORT("The rotational-displacement-force test space uses an "
|
MFEM_ABORT(
|
||||||
"unsupported ordering.");
|
"The rotational-displacement-force test space uses an "
|
||||||
|
"unsupported ordering."
|
||||||
|
);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const mfem::IntegrationRule &
|
[[nodiscard]] const mfem::IntegrationRule &get_rotation_force_rule(
|
||||||
get_rotation_force_rule(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mfem::FiniteElement &densityElement,
|
const mfem::FiniteElement &densityElement,
|
||||||
const mfem::FiniteElement &displacementElement,
|
const mfem::FiniteElement &displacementElement,
|
||||||
const mfem::ElementTransformation &transformation) {
|
const mfem::ElementTransformation &transformation
|
||||||
using DisplacementField =
|
) {
|
||||||
mean_field::field::Field<mean_field::field::Displacement>;
|
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
||||||
|
|
||||||
MFEM_VERIFY(densityElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Density::Scalar::familyOrder,
|
densityElement.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||||
"The rotational-displacement-force density element does not "
|
"The rotational-displacement-force density element does not "
|
||||||
"match the registered density field.");
|
"match the registered density field."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(displacementElement.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Displacement::Vector::familyOrder,
|
displacementElement.GetOrder() == mean_field::field::Displacement::Vector::familyOrder,
|
||||||
"The rotational-displacement-force test element does not match "
|
"The rotational-displacement-force test element does not match "
|
||||||
"the registered displacement field.");
|
"the registered displacement field."
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* grad(Psi_rotation) is linear in physical position, so it adds one
|
* grad(Psi_rotation) is linear in physical position, so it adds one
|
||||||
* dynamic polynomial-order contribution.
|
* dynamic polynomial-order contribution.
|
||||||
*/
|
*/
|
||||||
const mean_field::quadrature::Query query = DisplacementField::make_query<
|
const mean_field::quadrature::Query query =
|
||||||
mean_field::field::Displacement::Form::CentrifugalForce>(
|
DisplacementField::make_query<mean_field::field::Displacement::Form::CentrifugalForce>(
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{1},
|
||||||
transformation.OrderW(), std::array<int, 1>{1},
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
);
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
|
||||||
const mean_field::quadrature::MfemRule rule =
|
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
||||||
f.quadratureFactory->get(query, transformation.GetGeometryType());
|
|
||||||
|
|
||||||
MFEM_VERIFY(rule.integration_rule != nullptr,
|
MFEM_VERIFY(
|
||||||
"The quadrature policy did not return a rotational-"
|
rule.integration_rule != nullptr, "The quadrature policy did not return a rotational-"
|
||||||
"displacement-force integration rule.");
|
"displacement-force integration rule."
|
||||||
|
);
|
||||||
|
|
||||||
return *rule.integration_rule;
|
return *rule.integration_rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_finite_vector(const mfem::Vector &vector, const char *message) {
|
void validate_finite_vector(
|
||||||
|
const mfem::Vector &vector,
|
||||||
|
const char *message
|
||||||
|
) {
|
||||||
for (int index = 0; index < vector.Size(); ++index) {
|
for (int index = 0; index < vector.Size(); ++index) {
|
||||||
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
MFEM_VERIFY(std::isfinite(vector(index)), message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_common_inputs(
|
void validate_common_inputs(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper,
|
const mean_field::mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &displacementTrue) {
|
const mfem::Vector &displacementTrue
|
||||||
MFEM_VERIFY(f.mesh != nullptr,
|
) {
|
||||||
"The rotational-displacement-force kernel requires a mesh.");
|
MFEM_VERIFY(f.mesh != nullptr, "The rotational-displacement-force kernel requires a mesh.");
|
||||||
|
|
||||||
MFEM_VERIFY(f.mesh->Dimension() == 3,
|
MFEM_VERIFY(
|
||||||
"The rotational-displacement-force kernel requires a "
|
f.mesh->Dimension() == 3, "The rotational-displacement-force kernel requires a "
|
||||||
"three-dimensional mesh.");
|
"three-dimensional mesh."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.densityFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"The rotational-displacement-force kernel requires the density "
|
f.densityFes != nullptr, "The rotational-displacement-force kernel requires the density "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.displacementFes != nullptr,
|
MFEM_VERIFY(
|
||||||
|
f.displacementFes != nullptr, "The rotational-displacement-force kernel requires the "
|
||||||
|
"displacement finite-element space."
|
||||||
|
);
|
||||||
|
|
||||||
|
MFEM_VERIFY(
|
||||||
|
f.compactificationFes != nullptr && f.compactificationCoordinate != nullptr,
|
||||||
"The rotational-displacement-force kernel requires the "
|
"The rotational-displacement-force kernel requires the "
|
||||||
"displacement finite-element space.");
|
"compactification coordinate."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.compactificationFes != nullptr &&
|
MFEM_VERIFY(
|
||||||
f.compactificationCoordinate != nullptr,
|
f.quadratureFactory != nullptr, "The rotational-displacement-force kernel requires the "
|
||||||
"The rotational-displacement-force kernel requires the "
|
"quadrature-rule factory."
|
||||||
"compactification coordinate.");
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
MFEM_VERIFY(
|
||||||
"The rotational-displacement-force kernel requires the "
|
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||||
"quadrature-rule factory.");
|
|
||||||
|
|
||||||
MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
|
||||||
"The rotational-displacement-force displacement vector has the "
|
"The rotational-displacement-force displacement vector has the "
|
||||||
"wrong size.");
|
"wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(domainMapper.GetDimension() == f.mesh->Dimension(),
|
MFEM_VERIFY(
|
||||||
|
domainMapper.GetDimension() == f.mesh->Dimension(),
|
||||||
"The rotational-displacement-force mapper dimension does not "
|
"The rotational-displacement-force mapper dimension does not "
|
||||||
"match the mesh dimension.");
|
"match the mesh dimension."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
MFEM_VERIFY(
|
||||||
|
f.displacementFes->GetVDim() == f.mesh->Dimension(),
|
||||||
"The rotational-displacement-force displacement dimension does "
|
"The rotational-displacement-force displacement dimension does "
|
||||||
"not match the mesh dimension.");
|
"not match the mesh dimension."
|
||||||
|
);
|
||||||
|
|
||||||
validate_finite_vector(
|
validate_finite_vector(
|
||||||
displacementTrue,
|
displacementTrue, "The rotational-displacement-force displacement contains a "
|
||||||
"The rotational-displacement-force displacement contains a "
|
"non-finite value."
|
||||||
"non-finite value.");
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_density(const mean_field::fem::FEM &f,
|
void validate_density(
|
||||||
const mfem::Vector &density, const char *message) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const mfem::Vector &density,
|
||||||
|
const char *message
|
||||||
|
) {
|
||||||
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
|
MFEM_VERIFY(density.Size() == f.densityFes->GetTrueVSize(), message);
|
||||||
validate_finite_vector(density, message);
|
validate_finite_vector(density, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_rotational_displacement_force_action(
|
void apply_rotational_displacement_force_action(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper,
|
const mean_field::mapping::DomainMapper &domainMapper,
|
||||||
const mean_field::physics::RigidRotation &rotation,
|
const mean_field::physics::RigidRotation &rotation,
|
||||||
@@ -185,53 +210,55 @@ void apply_rotational_displacement_force_action(
|
|||||||
const mfem::Vector *baseDensityTrue,
|
const mfem::Vector *baseDensityTrue,
|
||||||
const mfem::Vector *densityVariationTrue,
|
const mfem::Vector *densityVariationTrue,
|
||||||
const mfem::Vector *displacementVariationTrue,
|
const mfem::Vector *displacementVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
validate_common_inputs(f, domainMapper, displacementTrue);
|
validate_common_inputs(f, domainMapper, displacementTrue);
|
||||||
|
|
||||||
const bool needsBaseDensity =
|
const bool needsBaseDensity = requestedAction == RotationalDisplacementForceAction::residual ||
|
||||||
requestedAction == RotationalDisplacementForceAction::residual ||
|
|
||||||
requestedAction == RotationalDisplacementForceAction::displacement ||
|
requestedAction == RotationalDisplacementForceAction::displacement ||
|
||||||
requestedAction == RotationalDisplacementForceAction::complete;
|
requestedAction == RotationalDisplacementForceAction::complete;
|
||||||
|
|
||||||
const bool needsDensityVariation =
|
const bool needsDensityVariation = requestedAction == RotationalDisplacementForceAction::density ||
|
||||||
requestedAction == RotationalDisplacementForceAction::density ||
|
|
||||||
requestedAction == RotationalDisplacementForceAction::complete;
|
requestedAction == RotationalDisplacementForceAction::complete;
|
||||||
|
|
||||||
const bool needsDisplacementVariation =
|
const bool needsDisplacementVariation = requestedAction == RotationalDisplacementForceAction::displacement ||
|
||||||
requestedAction == RotationalDisplacementForceAction::displacement ||
|
|
||||||
requestedAction == RotationalDisplacementForceAction::complete;
|
requestedAction == RotationalDisplacementForceAction::complete;
|
||||||
|
|
||||||
if (needsBaseDensity) {
|
if (needsBaseDensity) {
|
||||||
MFEM_VERIFY(baseDensityTrue != nullptr,
|
MFEM_VERIFY(
|
||||||
"The rotational-displacement-force action requires a base "
|
baseDensityTrue != nullptr, "The rotational-displacement-force action requires a base "
|
||||||
"density.");
|
"density."
|
||||||
|
);
|
||||||
|
|
||||||
validate_density(
|
validate_density(f, *baseDensityTrue, "The rotational-displacement-force base density is invalid.");
|
||||||
f, *baseDensityTrue,
|
|
||||||
"The rotational-displacement-force base density is invalid.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsDensityVariation) {
|
if (needsDensityVariation) {
|
||||||
MFEM_VERIFY(densityVariationTrue != nullptr,
|
MFEM_VERIFY(
|
||||||
"The rotational-displacement-force action requires a "
|
densityVariationTrue != nullptr, "The rotational-displacement-force action requires a "
|
||||||
"density variation.");
|
"density variation."
|
||||||
|
);
|
||||||
|
|
||||||
validate_density(f, *densityVariationTrue,
|
validate_density(
|
||||||
|
f, *densityVariationTrue,
|
||||||
"The rotational-displacement-force density variation is "
|
"The rotational-displacement-force density variation is "
|
||||||
"invalid.");
|
"invalid."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
MFEM_VERIFY(displacementVariationTrue != nullptr &&
|
MFEM_VERIFY(
|
||||||
displacementVariationTrue->Size() ==
|
displacementVariationTrue != nullptr &&
|
||||||
f.displacementFes->GetTrueVSize(),
|
displacementVariationTrue->Size() == f.displacementFes->GetTrueVSize(),
|
||||||
"The rotational-displacement-force displacement variation "
|
"The rotational-displacement-force displacement variation "
|
||||||
"is invalid.");
|
"is invalid."
|
||||||
|
);
|
||||||
|
|
||||||
validate_finite_vector(
|
validate_finite_vector(
|
||||||
*displacementVariationTrue,
|
*displacementVariationTrue, "The rotational-displacement-force displacement variation "
|
||||||
"The rotational-displacement-force displacement variation "
|
"contains a non-finite value."
|
||||||
"contains a non-finite value.");
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
mfem::Vector baseDensityLocal;
|
mfem::Vector baseDensityLocal;
|
||||||
@@ -250,15 +277,13 @@ void apply_rotational_displacement_force_action(
|
|||||||
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
true_to_local(*f.displacementFes, displacementTrue, displacementLocal);
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
true_to_local(*f.displacementFes, *displacementVariationTrue,
|
true_to_local(*f.displacementFes, *displacementVariationTrue, displacementVariationLocal);
|
||||||
displacementVariationLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mfem::Vector localAction(f.displacementFes->GetVSize());
|
mfem::Vector localAction(f.displacementFes->GetVSize());
|
||||||
localAction = 0.0;
|
localAction = 0.0;
|
||||||
|
|
||||||
mean_field::mapping::DomainMapper::Workspace workspace(
|
mean_field::mapping::DomainMapper::Workspace workspace(f.mesh->Dimension());
|
||||||
f.mesh->Dimension());
|
|
||||||
|
|
||||||
mfem::Array<int> densityDofs;
|
mfem::Array<int> densityDofs;
|
||||||
mfem::Array<int> displacementDofs;
|
mfem::Array<int> displacementDofs;
|
||||||
@@ -284,16 +309,15 @@ void apply_rotational_displacement_force_action(
|
|||||||
|
|
||||||
const int dimension = f.mesh->Dimension();
|
const int dimension = f.mesh->Dimension();
|
||||||
|
|
||||||
const mfem::Ordering::Type displacementOrdering =
|
const mfem::Ordering::Type displacementOrdering = f.displacementFes->GetOrdering();
|
||||||
f.displacementFes->GetOrdering();
|
|
||||||
|
|
||||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||||
mfem::ElementTransformation *transformation =
|
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||||
f.mesh->GetElementTransformation(elementId);
|
|
||||||
|
|
||||||
MFEM_VERIFY(transformation != nullptr,
|
MFEM_VERIFY(
|
||||||
"The rotational-displacement-force kernel received a null "
|
transformation != nullptr, "The rotational-displacement-force kernel received a null "
|
||||||
"element transformation.");
|
"element transformation."
|
||||||
|
);
|
||||||
|
|
||||||
if (is_vacuum_attribute(transformation->Attribute)) {
|
if (is_vacuum_attribute(transformation->Attribute)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -301,14 +325,11 @@ void apply_rotational_displacement_force_action(
|
|||||||
|
|
||||||
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
|
const mfem::FiniteElement &densityElement = *f.densityFes->GetFE(elementId);
|
||||||
|
|
||||||
const mfem::FiniteElement &displacementElement =
|
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||||
*f.displacementFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &compactificationElement =
|
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||||
*f.compactificationFes->GetFE(elementId);
|
|
||||||
|
|
||||||
mfem::DofTransformation *densityDofTransformation =
|
mfem::DofTransformation *densityDofTransformation = f.densityFes->GetElementDofs(elementId, densityDofs);
|
||||||
f.densityFes->GetElementDofs(elementId, densityDofs);
|
|
||||||
|
|
||||||
mfem::DofTransformation *displacementDofTransformation =
|
mfem::DofTransformation *displacementDofTransformation =
|
||||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||||
@@ -327,12 +348,10 @@ void apply_rotational_displacement_force_action(
|
|||||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
displacementVariationLocal.GetSubVector(displacementDofs,
|
displacementVariationLocal.GetSubVector(displacementDofs, elementDisplacementVariation);
|
||||||
elementDisplacementVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.compactificationCoordinate->GetSubVector(compactificationDofs,
|
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
|
|
||||||
if (densityDofTransformation != nullptr) {
|
if (densityDofTransformation != nullptr) {
|
||||||
if (needsBaseDensity) {
|
if (needsBaseDensity) {
|
||||||
@@ -348,42 +367,42 @@ void apply_rotational_displacement_force_action(
|
|||||||
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
displacementDofTransformation->InvTransformPrimal(elementDisplacement);
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
displacementDofTransformation->InvTransformPrimal(
|
displacementDofTransformation->InvTransformPrimal(elementDisplacementVariation);
|
||||||
elementDisplacementVariation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compactificationDofTransformation != nullptr) {
|
if (compactificationDofTransformation != nullptr) {
|
||||||
compactificationDofTransformation->InvTransformPrimal(
|
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||||
displacementElement, elementDisplacement);
|
|
||||||
|
|
||||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||||
compactificationElement, elementCompactification);
|
compactificationElement, elementCompactification
|
||||||
|
);
|
||||||
|
|
||||||
const mean_field::mapping::ElementMappingData mappingData{
|
const mean_field::mapping::ElementMappingData mappingData{
|
||||||
.displacement = displacementData,
|
.displacement = displacementData, .compactification = compactificationData
|
||||||
.compactification = compactificationData};
|
};
|
||||||
|
|
||||||
std::optional<mean_field::mapping::ElementDisplacementData>
|
std::optional<mean_field::mapping::ElementDisplacementData> displacementVariationData;
|
||||||
displacementVariationData;
|
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
displacementVariationData.emplace(
|
displacementVariationData.emplace(
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||||
displacementElement, elementDisplacementVariation));
|
displacementElement, elementDisplacementVariation
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
const int scalarDisplacementDofCount = displacementElement.GetDof();
|
||||||
|
|
||||||
MFEM_VERIFY(displacementDofs.Size() ==
|
MFEM_VERIFY(
|
||||||
scalarDisplacementDofCount * dimension,
|
displacementDofs.Size() == scalarDisplacementDofCount * dimension,
|
||||||
"The rotational-displacement-force element displacement "
|
"The rotational-displacement-force element displacement "
|
||||||
"vector has the wrong size.");
|
"vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
densityShape.SetSize(densityElement.GetDof());
|
densityShape.SetSize(densityElement.GetDof());
|
||||||
displacementShape.SetSize(scalarDisplacementDofCount);
|
displacementShape.SetSize(scalarDisplacementDofCount);
|
||||||
@@ -396,42 +415,39 @@ void apply_rotational_displacement_force_action(
|
|||||||
elementAction.SetSize(displacementDofs.Size());
|
elementAction.SetSize(displacementDofs.Size());
|
||||||
elementAction = 0.0;
|
elementAction = 0.0;
|
||||||
|
|
||||||
const mfem::IntegrationRule &integrationRule = get_rotation_force_rule(
|
const mfem::IntegrationRule &integrationRule =
|
||||||
f, densityElement, displacementElement, *transformation);
|
get_rotation_force_rule(f, densityElement, displacementElement, *transformation);
|
||||||
|
|
||||||
for (int quadratureIndex = 0;
|
for (int quadratureIndex = 0; quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
||||||
quadratureIndex < integrationRule.GetNPoints(); ++quadratureIndex) {
|
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(quadratureIndex);
|
||||||
const mfem::IntegrationPoint &integrationPoint =
|
|
||||||
integrationRule.IntPoint(quadratureIndex);
|
|
||||||
|
|
||||||
transformation->SetIntPoint(&integrationPoint);
|
transformation->SetIntPoint(&integrationPoint);
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus mappingStatus =
|
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||||
domainMapper.EvaluateVolume(mappingData, *transformation,
|
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||||
integrationPoint, workspace,
|
);
|
||||||
mappingContext);
|
|
||||||
|
|
||||||
MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
|
MFEM_VERIFY(
|
||||||
|
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"Stateless mapping failed in the rotational-"
|
"Stateless mapping failed in the rotational-"
|
||||||
"displacement-force kernel. Element: "
|
"displacement-force kernel. Element: "
|
||||||
<< elementId
|
<< elementId << ", attribute: " << transformation->Attribute
|
||||||
<< ", attribute: " << transformation->Attribute
|
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||||
<< ", quadrature point: " << quadratureIndex
|
);
|
||||||
<< ", status: " << static_cast<int>(mappingStatus));
|
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
const mean_field::mapping::MappingStatus variationStatus =
|
const mean_field::mapping::MappingStatus variationStatus = domainMapper.EvaluateVolumeVariation(
|
||||||
domainMapper.EvaluateVolumeVariation(
|
mappingData, *displacementVariationData, *transformation, integrationPoint, mappingContext,
|
||||||
mappingData, *displacementVariationData, *transformation,
|
workspace, mappingVariation
|
||||||
integrationPoint, mappingContext, workspace, mappingVariation);
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
variationStatus == mean_field::mapping::MappingStatus::valid,
|
variationStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"Stateless mapping variation failed in the "
|
"Stateless mapping variation failed in the "
|
||||||
"rotational-displacement-force kernel. Element: "
|
"rotational-displacement-force kernel. Element: "
|
||||||
<< elementId << ", attribute: " << transformation->Attribute
|
<< elementId << ", attribute: " << transformation->Attribute << ", quadrature point: "
|
||||||
<< ", quadrature point: " << quadratureIndex
|
<< quadratureIndex << ", status: " << static_cast<int>(variationStatus)
|
||||||
<< ", status: " << static_cast<int>(variationStatus));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
densityElement.CalcShape(integrationPoint, densityShape);
|
densityElement.CalcShape(integrationPoint, densityShape);
|
||||||
@@ -449,16 +465,15 @@ void apply_rotational_displacement_force_action(
|
|||||||
densityVariationValue = elementDensityVariation * densityShape;
|
densityVariationValue = elementDensityVariation * densityShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
rotation.potential_gradient(mappingContext.mapping.physical_position,
|
rotation.potential_gradient(mappingContext.mapping.physical_position, potentialGradient);
|
||||||
potentialGradient);
|
|
||||||
|
|
||||||
centrifugalAcceleration = potentialGradient;
|
centrifugalAcceleration = potentialGradient;
|
||||||
centrifugalAcceleration *= -1.0;
|
centrifugalAcceleration *= -1.0;
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
rotation.potential_gradient_directional_derivative(
|
rotation.potential_gradient_directional_derivative(
|
||||||
mappingVariation.mapping.physical_position_variation,
|
mappingVariation.mapping.physical_position_variation, potentialGradientVariation
|
||||||
potentialGradientVariation);
|
);
|
||||||
|
|
||||||
centrifugalAccelerationVariation = potentialGradientVariation;
|
centrifugalAccelerationVariation = potentialGradientVariation;
|
||||||
|
|
||||||
@@ -470,38 +485,37 @@ void apply_rotational_displacement_force_action(
|
|||||||
weightedForce = 0.0;
|
weightedForce = 0.0;
|
||||||
|
|
||||||
if (requestedAction == RotationalDisplacementForceAction::residual) {
|
if (requestedAction == RotationalDisplacementForceAction::residual) {
|
||||||
weightedForce.Add(baseDensityValue * mappingContext.quadrature.weight,
|
weightedForce.Add(baseDensityValue * mappingContext.quadrature.weight, centrifugalAcceleration);
|
||||||
centrifugalAcceleration);
|
|
||||||
} else {
|
} else {
|
||||||
if (needsDensityVariation) {
|
if (needsDensityVariation) {
|
||||||
weightedForce.Add(densityVariationValue *
|
weightedForce.Add(
|
||||||
mappingContext.quadrature.weight,
|
densityVariationValue * mappingContext.quadrature.weight, centrifugalAcceleration
|
||||||
centrifugalAcceleration);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsDisplacementVariation) {
|
if (needsDisplacementVariation) {
|
||||||
weightedForce.Add(baseDensityValue * mappingContext.quadrature.weight,
|
weightedForce.Add(
|
||||||
centrifugalAccelerationVariation);
|
baseDensityValue * mappingContext.quadrature.weight, centrifugalAccelerationVariation
|
||||||
|
);
|
||||||
|
|
||||||
weightedForce.Add(baseDensityValue *
|
weightedForce.Add(
|
||||||
mappingVariation.weight_variation,
|
baseDensityValue * mappingVariation.weight_variation, centrifugalAcceleration
|
||||||
centrifugalAcceleration);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount;
|
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
|
||||||
++scalarDof) {
|
|
||||||
for (int component = 0; component < dimension; ++component) {
|
for (int component = 0; component < dimension; ++component) {
|
||||||
const int vectorDof =
|
const int vectorDof = vector_dof_index(
|
||||||
vector_dof_index(displacementOrdering, scalarDof, component,
|
displacementOrdering, scalarDof, component, scalarDisplacementDofCount, dimension
|
||||||
scalarDisplacementDofCount, dimension);
|
);
|
||||||
|
|
||||||
const double contribution =
|
const double contribution = displacementShape(scalarDof) * weightedForce(component);
|
||||||
displacementShape(scalarDof) * weightedForce(component);
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(contribution),
|
MFEM_VERIFY(
|
||||||
"The rotational-displacement-force kernel "
|
std::isfinite(contribution), "The rotational-displacement-force kernel "
|
||||||
"encountered a non-finite contribution.");
|
"encountered a non-finite contribution."
|
||||||
|
);
|
||||||
|
|
||||||
elementAction(vectorDof) += contribution;
|
elementAction(vectorDof) += contribution;
|
||||||
}
|
}
|
||||||
@@ -516,49 +530,66 @@ void apply_rotational_displacement_force_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
local_to_true(*f.displacementFes, localAction, actionTrue);
|
local_to_true(*f.displacementFes, localAction, actionTrue);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace mean_field::operators::kernels {
|
namespace mean_field::operators::kernels {
|
||||||
void apply_rotational_displacement_force_residual(
|
void apply_rotational_displacement_force_residual(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
const physics::RigidRotation &rotation, const mfem::Vector &densityTrue,
|
const mapping::DomainMapper &domainMapper,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &residualTrue) {
|
const physics::RigidRotation &rotation,
|
||||||
|
const mfem::Vector &densityTrue,
|
||||||
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &residualTrue
|
||||||
|
) {
|
||||||
apply_rotational_displacement_force_action(
|
apply_rotational_displacement_force_action(
|
||||||
f, domainMapper, rotation, RotationalDisplacementForceAction::residual,
|
f, domainMapper, rotation, RotationalDisplacementForceAction::residual, &densityTrue, nullptr, nullptr,
|
||||||
&densityTrue, nullptr, nullptr, displacementTrue, residualTrue);
|
displacementTrue, residualTrue
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void apply_rotational_displacement_force_density_action(
|
void apply_rotational_displacement_force_density_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
const physics::RigidRotation &rotation,
|
const physics::RigidRotation &rotation,
|
||||||
const mfem::Vector &densityVariationTrue,
|
const mfem::Vector &densityVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
apply_rotational_displacement_force_action(
|
apply_rotational_displacement_force_action(
|
||||||
f, domainMapper, rotation, RotationalDisplacementForceAction::density,
|
f, domainMapper, rotation, RotationalDisplacementForceAction::density, nullptr, &densityVariationTrue,
|
||||||
nullptr, &densityVariationTrue, nullptr, displacementTrue, actionTrue);
|
nullptr, displacementTrue, actionTrue
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void apply_rotational_displacement_force_displacement_action(
|
void apply_rotational_displacement_force_displacement_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
const physics::RigidRotation &rotation, const mfem::Vector &baseDensityTrue,
|
const mapping::DomainMapper &domainMapper,
|
||||||
|
const physics::RigidRotation &rotation,
|
||||||
|
const mfem::Vector &baseDensityTrue,
|
||||||
const mfem::Vector &displacementVariationTrue,
|
const mfem::Vector &displacementVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
apply_rotational_displacement_force_action(
|
apply_rotational_displacement_force_action(
|
||||||
f, domainMapper, rotation,
|
f, domainMapper, rotation, RotationalDisplacementForceAction::displacement, &baseDensityTrue, nullptr,
|
||||||
RotationalDisplacementForceAction::displacement, &baseDensityTrue,
|
&displacementVariationTrue, displacementTrue, actionTrue
|
||||||
nullptr, &displacementVariationTrue, displacementTrue, actionTrue);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_rotational_displacement_force_complete_action(
|
void apply_rotational_displacement_force_complete_action(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domainMapper,
|
const fem::FEM &f,
|
||||||
const physics::RigidRotation &rotation, const mfem::Vector &baseDensityTrue,
|
const mapping::DomainMapper &domainMapper,
|
||||||
|
const physics::RigidRotation &rotation,
|
||||||
|
const mfem::Vector &baseDensityTrue,
|
||||||
const mfem::Vector &densityVariationTrue,
|
const mfem::Vector &densityVariationTrue,
|
||||||
const mfem::Vector &displacementVariationTrue,
|
const mfem::Vector &displacementVariationTrue,
|
||||||
const mfem::Vector &displacementTrue, mfem::Vector &actionTrue) {
|
const mfem::Vector &displacementTrue,
|
||||||
|
mfem::Vector &actionTrue
|
||||||
|
) {
|
||||||
apply_rotational_displacement_force_action(
|
apply_rotational_displacement_force_action(
|
||||||
f, domainMapper, rotation, RotationalDisplacementForceAction::complete,
|
f, domainMapper, rotation, RotationalDisplacementForceAction::complete, &baseDensityTrue,
|
||||||
&baseDensityTrue, &densityVariationTrue, &displacementVariationTrue,
|
&densityVariationTrue, &displacementVariationTrue, displacementTrue, actionTrue
|
||||||
displacementTrue, actionTrue);
|
);
|
||||||
}
|
}
|
||||||
} // namespace mean_field::operators::kernels
|
} // namespace mean_field::operators::kernels
|
||||||
|
|||||||
@@ -401,8 +401,14 @@ namespace mean_field::operators {
|
|||||||
const double density = elementBaseDensity * densityShape;
|
const double density = elementBaseDensity * densityShape;
|
||||||
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
|
const double enthalpy = elementBaseEnthalpy * enthalpyShape;
|
||||||
const double quadratureWeight = mappingContext.quadrature.weight;
|
const double quadratureWeight = mappingContext.quadrature.weight;
|
||||||
const double eosDensity = m_equationOfState.density_from_enthalpy(enthalpy);
|
const eos::SpecificEnthalpyValue specificEnthalpy{enthalpy};
|
||||||
const double enthalpyDerivative = m_equationOfState.density_derivative_from_enthalpy(enthalpy);
|
const double eosDensity =
|
||||||
|
eos::evaluate<eos::quantity::Density>(m_equationOfState, specificEnthalpy).value();
|
||||||
|
const double enthalpyDerivative =
|
||||||
|
eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
m_equationOfState, specificEnthalpy
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
std::isfinite(quadratureWeight) && quadratureWeight > 0.0 && std::isfinite(eosDensity) &&
|
std::isfinite(quadratureWeight) && quadratureWeight > 0.0 && std::isfinite(eosDensity) &&
|
||||||
|
|||||||
@@ -9,128 +9,133 @@ module mean_field;
|
|||||||
import :operators.prepared_gravity_source;
|
import :operators.prepared_gravity_source;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
int get_operator_height(const mean_field::fem::FEM &f) {
|
int get_operator_height(const mean_field::fem::FEM &f) {
|
||||||
MFEM_VERIFY(f.gravityPotentialFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"PreparedMappedGravitySourceOperator requires the "
|
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
|
||||||
"gravity-potential "
|
"gravity-potential "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
return mean_field::field::make_field_dof_map<mean_field::field::Gravity,
|
);
|
||||||
DomainSchema>(
|
return mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes)
|
||||||
*f.gravityPotentialFes)
|
|
||||||
.reduced_size();
|
.reduced_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_operator_width(const mean_field::fem::FEM &f) {
|
int get_operator_width(const mean_field::fem::FEM &f) {
|
||||||
MFEM_VERIFY(f.densityFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"PreparedMappedGravitySourceOperator requires the density "
|
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
|
||||||
"finite-element space.");
|
"finite-element space."
|
||||||
return mean_field::field::make_field_dof_map<mean_field::field::Density,
|
);
|
||||||
DomainSchema>(*f.densityFes)
|
return mean_field::field::make_field_dof_map<mean_field::field::Density, DomainSchema>(*f.densityFes)
|
||||||
.reduced_size();
|
.reduced_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void true_to_local(const mfem::ParFiniteElementSpace &finite_element_space,
|
void true_to_local(
|
||||||
|
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||||
const mfem::Vector &true_vector,
|
const mfem::Vector &true_vector,
|
||||||
mfem::Vector &local_vector) {
|
mfem::Vector &local_vector
|
||||||
|
) {
|
||||||
local_vector.SetSize(finite_element_space.GetVSize());
|
local_vector.SetSize(finite_element_space.GetVSize());
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||||
finite_element_space.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->Mult(true_vector, local_vector);
|
prolongation->Mult(true_vector, local_vector);
|
||||||
} else {
|
} else {
|
||||||
local_vector = true_vector;
|
local_vector = true_vector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void local_to_true(const mfem::ParFiniteElementSpace &finite_element_space,
|
void local_to_true(
|
||||||
|
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||||
const mfem::Vector &local_vector,
|
const mfem::Vector &local_vector,
|
||||||
mfem::Vector &true_vector) {
|
mfem::Vector &true_vector
|
||||||
MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(),
|
) {
|
||||||
"Local vector has the wrong size.");
|
MFEM_VERIFY(local_vector.Size() == finite_element_space.GetVSize(), "Local vector has the wrong size.");
|
||||||
|
|
||||||
true_vector.SetSize(finite_element_space.GetTrueVSize());
|
true_vector.SetSize(finite_element_space.GetTrueVSize());
|
||||||
true_vector = 0.0;
|
true_vector = 0.0;
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||||
finite_element_space.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->MultTranspose(local_vector, true_vector);
|
prolongation->MultTranspose(local_vector, true_vector);
|
||||||
} else {
|
} else {
|
||||||
true_vector = local_vector;
|
true_vector = local_vector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::IntegrationRule &
|
const mfem::IntegrationRule &get_source_rule(
|
||||||
get_source_rule(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mfem::FiniteElement &density_element,
|
const mfem::FiniteElement &density_element,
|
||||||
const mfem::FiniteElement &potential_element,
|
const mfem::FiniteElement &potential_element,
|
||||||
const mfem::ElementTransformation &transformation) {
|
const mfem::ElementTransformation &transformation
|
||||||
|
) {
|
||||||
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
|
using GravityField = mean_field::field::Field<mean_field::field::Gravity>;
|
||||||
MFEM_VERIFY(density_element.GetOrder() ==
|
MFEM_VERIFY(
|
||||||
mean_field::field::Density::Scalar::familyOrder,
|
density_element.GetOrder() == mean_field::field::Density::Scalar::familyOrder,
|
||||||
"The prepared source trial element does not match the registered "
|
"The prepared source trial element does not match the registered "
|
||||||
"density field.");
|
"density field."
|
||||||
MFEM_VERIFY(potential_element.GetOrder() ==
|
);
|
||||||
mean_field::field::Gravity::Potential::familyOrder,
|
MFEM_VERIFY(
|
||||||
|
potential_element.GetOrder() == mean_field::field::Gravity::Potential::familyOrder,
|
||||||
"The prepared source test element does not match the registered "
|
"The prepared source test element does not match the registered "
|
||||||
"gravity potential.");
|
"gravity potential."
|
||||||
const mean_field::quadrature::Query query = GravityField::make_query<
|
);
|
||||||
mean_field::field::Gravity::Form::SourceProjection>(
|
const mean_field::quadrature::Query query =
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
GravityField::make_query<mean_field::field::Gravity::Form::SourceProjection>(
|
||||||
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
||||||
mean_field::quadrature::MappingKind::general);
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
|
);
|
||||||
|
|
||||||
return *f.quadratureFactory->get(query, transformation.GetGeometryType())
|
return *f.quadratureFactory->get(query, transformation.GetGeometryType()).integration_rule;
|
||||||
.integration_rule;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class FrozenMappedGravitySourceCoefficient final : public mfem::Coefficient {
|
class FrozenMappedGravitySourceCoefficient final : public mfem::Coefficient {
|
||||||
public:
|
public:
|
||||||
FrozenMappedGravitySourceCoefficient(
|
FrozenMappedGravitySourceCoefficient(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domain_mapper,
|
const mean_field::mapping::DomainMapper &domain_mapper,
|
||||||
const mfem::Vector &displacement_true)
|
const mfem::Vector &displacement_true
|
||||||
: m_fem(f), m_domain_mapper(domain_mapper),
|
)
|
||||||
|
: m_fem(f),
|
||||||
|
m_domain_mapper(domain_mapper),
|
||||||
m_workspace(domain_mapper.GetDimension()) {
|
m_workspace(domain_mapper.GetDimension()) {
|
||||||
true_to_local(*m_fem.displacementFes, displacement_true,
|
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
|
||||||
m_displacement_local);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double Eval(mfem::ElementTransformation &transformation,
|
double Eval(
|
||||||
const mfem::IntegrationPoint &integration_point) override {
|
mfem::ElementTransformation &transformation,
|
||||||
|
const mfem::IntegrationPoint &integration_point
|
||||||
|
) override {
|
||||||
transformation.SetIntPoint(&integration_point);
|
transformation.SetIntPoint(&integration_point);
|
||||||
|
|
||||||
const int element_id = transformation.ElementNo;
|
const int element_id = transformation.ElementNo;
|
||||||
MFEM_VERIFY(element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
MFEM_VERIFY(
|
||||||
|
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
||||||
"Mapped gravity source coefficient received an invalid element "
|
"Mapped gravity source coefficient received an invalid element "
|
||||||
"ID.");
|
"ID."
|
||||||
if (DomainSchema::template attribute_belongs_to<
|
);
|
||||||
mean_field::utils::domain::Vacuum>(transformation.Attribute)) {
|
if (DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(
|
||||||
|
transformation.Attribute
|
||||||
|
)) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadElement(element_id);
|
LoadElement(element_id);
|
||||||
const mean_field::mapping::ElementMappingData mapping_data{
|
const mean_field::mapping::ElementMappingData mapping_data{
|
||||||
.displacement = *m_displacement_data,
|
.displacement = *m_displacement_data, .compactification = *m_compactification_data
|
||||||
.compactification = *m_compactification_data};
|
};
|
||||||
|
|
||||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus status =
|
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
|
||||||
m_domain_mapper.EvaluateVolume(mapping_data, transformation,
|
mapping_data, transformation, integration_point, m_workspace, mapping_context
|
||||||
integration_point, m_workspace,
|
);
|
||||||
mapping_context);
|
|
||||||
|
|
||||||
if (status != mean_field::mapping::MappingStatus::valid) {
|
if (status != mean_field::mapping::MappingStatus::valid) {
|
||||||
const mfem::FiniteElement &displacement_element =
|
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||||
*m_fem.displacementFes->GetFE(element_id);
|
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||||
const mfem::FiniteElement &compactification_element =
|
|
||||||
*m_fem.compactificationFes->GetFE(element_id);
|
|
||||||
|
|
||||||
mfem::Vector displacement_shape(displacement_element.GetDof());
|
mfem::Vector displacement_shape(displacement_element.GetDof());
|
||||||
mfem::Vector compactification_shape(compactification_element.GetDof());
|
mfem::Vector compactification_shape(compactification_element.GetDof());
|
||||||
@@ -138,85 +143,74 @@ public:
|
|||||||
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
|
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
|
||||||
|
|
||||||
displacement_element.CalcShape(integration_point, displacement_shape);
|
displacement_element.CalcShape(integration_point, displacement_shape);
|
||||||
compactification_element.CalcShape(integration_point,
|
compactification_element.CalcShape(integration_point, compactification_shape);
|
||||||
compactification_shape);
|
|
||||||
transformation.Transform(integration_point, reference_position);
|
transformation.Transform(integration_point, reference_position);
|
||||||
m_displacement_data->GetDofMatrix().MultTranspose(displacement_shape,
|
m_displacement_data->GetDofMatrix().MultTranspose(displacement_shape, displacement_value);
|
||||||
displacement_value);
|
|
||||||
|
|
||||||
const double compactification_coordinate =
|
const double compactification_coordinate = m_compactification_data->GetDofs() * compactification_shape;
|
||||||
m_compactification_data->GetDofs() * compactification_shape;
|
|
||||||
|
|
||||||
MFEM_ABORT(
|
MFEM_ABORT(
|
||||||
"Stateless domain mapping failed while preparing the "
|
"Stateless domain mapping failed while preparing the "
|
||||||
"gravity "
|
"gravity "
|
||||||
"source operator."
|
"source operator."
|
||||||
<< "\nMapping status = " << static_cast<int>(status)
|
<< "\nMapping status = " << static_cast<int>(status) << "\nElement ID = " << element_id
|
||||||
<< "\nElement ID = " << element_id
|
|
||||||
<< "\nElement attribute = " << transformation.Attribute
|
<< "\nElement attribute = " << transformation.Attribute
|
||||||
<< "\nIntegration-point index = " << integration_point.index
|
<< "\nIntegration-point index = " << integration_point.index << "\nIntegration point = <"
|
||||||
<< "\nIntegration point = <" << integration_point.x << ", "
|
<< integration_point.x << ", " << integration_point.y << ", " << integration_point.z << ">"
|
||||||
<< integration_point.y << ", " << integration_point.z << ">"
|
<< "\nReference position = <" << reference_position(0) << ", " << reference_position(1) << ", "
|
||||||
<< "\nReference position = <" << reference_position(0) << ", "
|
<< reference_position(2) << ">"
|
||||||
<< reference_position(1) << ", " << reference_position(2) << ">"
|
<< "\nReference radius = " << reference_position.Norml2() << "\nDisplacement value = <"
|
||||||
<< "\nReference radius = " << reference_position.Norml2()
|
<< displacement_value(0) << ", " << displacement_value(1) << ", " << displacement_value(2) << ">"
|
||||||
<< "\nDisplacement value = <" << displacement_value(0) << ", "
|
|
||||||
<< displacement_value(1) << ", " << displacement_value(2) << ">"
|
|
||||||
<< "\nDisplacement magnitude = " << displacement_value.Norml2()
|
<< "\nDisplacement magnitude = " << displacement_value.Norml2()
|
||||||
<< "\nCompactification coordinate = " << compactification_coordinate
|
<< "\nCompactification coordinate = " << compactification_coordinate
|
||||||
<< "\nDisplacement ordering = "
|
<< "\nDisplacement ordering = " << static_cast<int>(m_fem.displacementFes->GetOrdering())
|
||||||
<< static_cast<int>(m_fem.displacementFes->GetOrdering()));
|
);
|
||||||
}
|
}
|
||||||
const double mapping_determinant =
|
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
|
||||||
mapping_context.mapping.mapping_determinant;
|
MFEM_VERIFY(
|
||||||
MFEM_VERIFY(std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||||
"Prepared gravity source operator encountered a non-positive "
|
"Prepared gravity source operator encountered a non-positive "
|
||||||
"or "
|
"or "
|
||||||
"non-finite mapping determinant.");
|
"non-finite mapping determinant."
|
||||||
|
);
|
||||||
|
|
||||||
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
|
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadElement(const int element_id) {
|
void LoadElement(const int element_id) {
|
||||||
if (element_id == m_cached_element_id) {
|
if (element_id == m_cached_element_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::FiniteElement &displacement_element =
|
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||||
*m_fem.displacementFes->GetFE(element_id);
|
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||||
const mfem::FiniteElement &compactification_element =
|
|
||||||
*m_fem.compactificationFes->GetFE(element_id);
|
|
||||||
|
|
||||||
mfem::DofTransformation *displacement_dof_transformation =
|
mfem::DofTransformation *displacement_dof_transformation =
|
||||||
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
||||||
mfem::DofTransformation *compactification_dof_transformation =
|
mfem::DofTransformation *compactification_dof_transformation =
|
||||||
m_fem.compactificationFes->GetElementDofs(element_id,
|
m_fem.compactificationFes->GetElementDofs(element_id, m_compactification_dofs);
|
||||||
m_compactification_dofs);
|
|
||||||
|
|
||||||
m_displacement_local.GetSubVector(m_displacement_dofs,
|
m_displacement_local.GetSubVector(m_displacement_dofs, m_element_displacement);
|
||||||
m_element_displacement);
|
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification);
|
||||||
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs,
|
|
||||||
m_element_compactification);
|
|
||||||
|
|
||||||
if (displacement_dof_transformation != nullptr) {
|
if (displacement_dof_transformation != nullptr) {
|
||||||
displacement_dof_transformation->InvTransformPrimal(
|
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
|
||||||
m_element_displacement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compactification_dof_transformation != nullptr) {
|
if (compactification_dof_transformation != nullptr) {
|
||||||
compactification_dof_transformation->InvTransformPrimal(
|
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
|
||||||
m_element_compactification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_displacement_data =
|
m_displacement_data = std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
||||||
std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||||
displacement_element, m_element_displacement));
|
displacement_element, m_element_displacement
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
m_compactification_data =
|
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
||||||
std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
compactification_element, m_element_compactification
|
||||||
compactification_element, m_element_compactification);
|
);
|
||||||
|
|
||||||
m_cached_element_id = element_id;
|
m_cached_element_id = element_id;
|
||||||
}
|
}
|
||||||
@@ -232,71 +226,89 @@ private:
|
|||||||
mfem::Vector m_element_displacement;
|
mfem::Vector m_element_displacement;
|
||||||
mfem::Vector m_element_compactification;
|
mfem::Vector m_element_compactification;
|
||||||
|
|
||||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
|
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data;
|
||||||
m_displacement_data;
|
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
|
||||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
|
|
||||||
m_compactification_data;
|
|
||||||
|
|
||||||
mean_field::mapping::DomainMapper::Workspace m_workspace;
|
mean_field::mapping::DomainMapper::Workspace m_workspace;
|
||||||
int m_cached_element_id{-1};
|
int m_cached_element_id{-1};
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace mean_field::operators {
|
namespace mean_field::operators {
|
||||||
PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator(
|
PreparedMappedGravitySourceOperator::PreparedMappedGravitySourceOperator(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domain_mapper)
|
const fem::FEM &f,
|
||||||
: Operator(get_operator_height(f), get_operator_width(f)), m_fem(f),
|
const mapping::DomainMapper &domain_mapper
|
||||||
|
)
|
||||||
|
: Operator(
|
||||||
|
get_operator_height(f),
|
||||||
|
get_operator_width(f)
|
||||||
|
),
|
||||||
|
m_fem(f),
|
||||||
m_domain_mapper(domain_mapper),
|
m_domain_mapper(domain_mapper),
|
||||||
m_density_map(field::make_field_dof_map<field::Density, DomainSchema>(
|
m_density_map(
|
||||||
*f.densityFes)),
|
field::make_field_dof_map<
|
||||||
m_potential_map(field::make_field_dof_map<field::Gravity, DomainSchema>(
|
field::Density,
|
||||||
*f.gravityPotentialFes)),
|
DomainSchema>(*f.densityFes)
|
||||||
|
),
|
||||||
|
m_potential_map(
|
||||||
|
field::make_field_dof_map<
|
||||||
|
field::Gravity,
|
||||||
|
DomainSchema>(*f.gravityPotentialFes)
|
||||||
|
),
|
||||||
m_displacement_map(
|
m_displacement_map(
|
||||||
field::make_field_dof_map<field::Displacement, DomainSchema>(
|
field::make_field_dof_map<
|
||||||
*f.displacementFes)) {
|
field::Displacement,
|
||||||
MFEM_VERIFY(f.mesh != nullptr,
|
DomainSchema>(*f.displacementFes)
|
||||||
"PreparedMappedGravitySourceOperator requires a mesh.");
|
) {
|
||||||
MFEM_VERIFY(f.densityFes != nullptr,
|
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedGravitySourceOperator requires a mesh.");
|
||||||
"PreparedMappedGravitySourceOperator requires the density "
|
|
||||||
"finite-element space.");
|
|
||||||
MFEM_VERIFY(f.gravityPotentialFes != nullptr,
|
|
||||||
"PreparedMappedGravitySourceOperator requires the "
|
|
||||||
"gravity-potential "
|
|
||||||
"finite-element space.");
|
|
||||||
MFEM_VERIFY(f.displacementFes != nullptr,
|
|
||||||
"PreparedMappedGravitySourceOperator requires "
|
|
||||||
"the displacement finite-element space.");
|
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
f.compactificationFes != nullptr,
|
f.densityFes != nullptr, "PreparedMappedGravitySourceOperator requires the density "
|
||||||
"PreparedMappedGravitySourceOperator requires the compactification "
|
"finite-element space."
|
||||||
"finite-element space.");
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
|
||||||
|
"gravity-potential "
|
||||||
|
"finite-element space."
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
f.displacementFes != nullptr, "PreparedMappedGravitySourceOperator requires "
|
||||||
|
"the displacement finite-element space."
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
f.compactificationFes != nullptr, "PreparedMappedGravitySourceOperator requires the compactification "
|
||||||
|
"finite-element space."
|
||||||
|
);
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
f.compactificationCoordinate != nullptr,
|
f.compactificationCoordinate != nullptr,
|
||||||
"PreparedMappedGravitySourceOperator requires the compactification "
|
"PreparedMappedGravitySourceOperator requires the compactification "
|
||||||
"coordinate.");
|
"coordinate."
|
||||||
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
);
|
||||||
"PreparedMappedGravitySourceOperator "
|
MFEM_VERIFY(
|
||||||
"requires the quadrature-rule factory.");
|
f.quadratureFactory != nullptr, "PreparedMappedGravitySourceOperator "
|
||||||
MFEM_VERIFY(domain_mapper.GetDimension() == f.mesh->Dimension(),
|
"requires the quadrature-rule factory."
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||||
"The stateless domain-mapper dimension does not match the mesh "
|
"The stateless domain-mapper dimension does not match the mesh "
|
||||||
"dimension.");
|
"dimension."
|
||||||
|
);
|
||||||
|
|
||||||
m_stellar_marker =
|
m_stellar_marker = utils::domain::make_attribute_marker<utils::domain::Stellar, DomainSchema>(*f.mesh);
|
||||||
utils::domain::make_attribute_marker<utils::domain::Stellar,
|
}
|
||||||
DomainSchema>(*f.mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PreparedMappedGravitySourceOperator::Prepare(
|
void PreparedMappedGravitySourceOperator::Prepare(const mfem::Vector &displacement) {
|
||||||
const mfem::Vector &displacement) {
|
MFEM_VERIFY(
|
||||||
MFEM_VERIFY(displacement.Size() == m_displacement_map.reduced_size(),
|
displacement.Size() == m_displacement_map.reduced_size(),
|
||||||
"PreparedMappedGravitySourceOperator received a displacement "
|
"PreparedMappedGravitySourceOperator received a displacement "
|
||||||
"vector "
|
"vector "
|
||||||
"with the wrong size.");
|
"with the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
for (int i = 0; i < displacement.Size(); ++i) {
|
for (int i = 0; i < displacement.Size(); ++i) {
|
||||||
MFEM_VERIFY(std::isfinite(displacement(i)),
|
MFEM_VERIFY(
|
||||||
"PreparedMappedGravitySourceOperator received a non-finite "
|
std::isfinite(displacement(i)), "PreparedMappedGravitySourceOperator received a non-finite "
|
||||||
"displacement value.");
|
"displacement value."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_is_prepared = false;
|
m_is_prepared = false;
|
||||||
@@ -305,14 +317,12 @@ void PreparedMappedGravitySourceOperator::Prepare(
|
|||||||
m_elements.clear();
|
m_elements.clear();
|
||||||
m_elements.reserve(m_fem.mesh->GetNE());
|
m_elements.reserve(m_fem.mesh->GetNE());
|
||||||
|
|
||||||
FrozenMappedGravitySourceCoefficient source_coefficient(
|
FrozenMappedGravitySourceCoefficient source_coefficient(m_fem, m_domain_mapper, m_displacement_true);
|
||||||
m_fem, m_domain_mapper, m_displacement_true);
|
|
||||||
|
|
||||||
for (int element_id = 0; element_id < m_fem.mesh->GetNE(); ++element_id) {
|
for (int element_id = 0; element_id < m_fem.mesh->GetNE(); ++element_id) {
|
||||||
const int attribute = m_fem.mesh->GetAttribute(element_id);
|
const int attribute = m_fem.mesh->GetAttribute(element_id);
|
||||||
|
|
||||||
if (attribute <= 0 || attribute > m_stellar_marker.Size() ||
|
if (attribute <= 0 || attribute > m_stellar_marker.Size() || m_stellar_marker[attribute - 1] == 0) {
|
||||||
m_stellar_marker[attribute - 1] == 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,24 +331,19 @@ void PreparedMappedGravitySourceOperator::Prepare(
|
|||||||
|
|
||||||
data.element_id = element_id;
|
data.element_id = element_id;
|
||||||
|
|
||||||
data.density_dof_transformation =
|
data.density_dof_transformation = m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
|
||||||
m_fem.densityFes->GetElementDofs(element_id, data.density_dofs);
|
|
||||||
|
|
||||||
data.potential_dof_transformation =
|
data.potential_dof_transformation =
|
||||||
m_fem.gravityPotentialFes->GetElementDofs(element_id,
|
m_fem.gravityPotentialFes->GetElementDofs(element_id, data.potential_dofs);
|
||||||
data.potential_dofs);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &density_element =
|
const mfem::FiniteElement &density_element = *m_fem.densityFes->GetFE(element_id);
|
||||||
*m_fem.densityFes->GetFE(element_id);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &potential_element =
|
const mfem::FiniteElement &potential_element = *m_fem.gravityPotentialFes->GetFE(element_id);
|
||||||
*m_fem.gravityPotentialFes->GetFE(element_id);
|
|
||||||
|
|
||||||
mfem::ElementTransformation &transformation =
|
mfem::ElementTransformation &transformation = *m_fem.mesh->GetElementTransformation(element_id);
|
||||||
*m_fem.mesh->GetElementTransformation(element_id);
|
|
||||||
|
|
||||||
const mfem::IntegrationRule &integration_rule = get_source_rule(
|
const mfem::IntegrationRule &integration_rule =
|
||||||
m_fem, density_element, potential_element, transformation);
|
get_source_rule(m_fem, density_element, potential_element, transformation);
|
||||||
|
|
||||||
const int quadrature_point_count = integration_rule.GetNPoints();
|
const int quadrature_point_count = integration_rule.GetNPoints();
|
||||||
|
|
||||||
@@ -355,10 +360,8 @@ void PreparedMappedGravitySourceOperator::Prepare(
|
|||||||
mfem::Vector density_shape(density_dof_count);
|
mfem::Vector density_shape(density_dof_count);
|
||||||
mfem::Vector potential_shape(potential_dof_count);
|
mfem::Vector potential_shape(potential_dof_count);
|
||||||
|
|
||||||
for (int quadrature_point = 0; quadrature_point < quadrature_point_count;
|
for (int quadrature_point = 0; quadrature_point < quadrature_point_count; ++quadrature_point) {
|
||||||
++quadrature_point) {
|
const mfem::IntegrationPoint &integration_point = integration_rule.IntPoint(quadrature_point);
|
||||||
const mfem::IntegrationPoint &integration_point =
|
|
||||||
integration_rule.IntPoint(quadrature_point);
|
|
||||||
|
|
||||||
transformation.SetIntPoint(&integration_point);
|
transformation.SetIntPoint(&integration_point);
|
||||||
|
|
||||||
@@ -376,40 +379,41 @@ void PreparedMappedGravitySourceOperator::Prepare(
|
|||||||
data.potential_basis(quadrature_point, i) = potential_shape(i);
|
data.potential_basis(quadrature_point, i) = potential_shape(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
const double coefficient_value =
|
const double coefficient_value = source_coefficient.Eval(transformation, integration_point);
|
||||||
source_coefficient.Eval(transformation, integration_point);
|
|
||||||
|
|
||||||
transformation.SetIntPoint(&integration_point);
|
transformation.SetIntPoint(&integration_point);
|
||||||
|
|
||||||
const double quadrature_value = integration_point.weight *
|
const double quadrature_value = integration_point.weight * transformation.Weight() * coefficient_value;
|
||||||
transformation.Weight() *
|
|
||||||
coefficient_value;
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(quadrature_value) && quadrature_value > 0.0,
|
MFEM_VERIFY(
|
||||||
|
std::isfinite(quadrature_value) && quadrature_value > 0.0,
|
||||||
"Prepared gravity source operator encountered invalid "
|
"Prepared gravity source operator encountered invalid "
|
||||||
"quadrature data on element "
|
"quadrature data on element "
|
||||||
<< element_id << ", quadrature point " << quadrature_point
|
<< element_id << ", quadrature point " << quadrature_point << "."
|
||||||
<< ".");
|
);
|
||||||
|
|
||||||
data.quadrature_data(quadrature_point) = quadrature_value;
|
data.quadrature_data(quadrature_point) = quadrature_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MFEM_VERIFY(!m_elements.empty(),
|
MFEM_VERIFY(!m_elements.empty(), "PreparedMappedGravitySourceOperator found no stellar elements.");
|
||||||
"PreparedMappedGravitySourceOperator found no stellar elements.");
|
|
||||||
|
|
||||||
m_is_prepared = true;
|
m_is_prepared = true;
|
||||||
++m_preparation_count;
|
++m_preparation_count;
|
||||||
}
|
}
|
||||||
void PreparedMappedGravitySourceOperator::Mult(const mfem::Vector &density,
|
void PreparedMappedGravitySourceOperator::Mult(
|
||||||
mfem::Vector &action) const {
|
const mfem::Vector &density,
|
||||||
MFEM_VERIFY(m_is_prepared,
|
mfem::Vector &action
|
||||||
"PreparedMappedGravitySourceOperator must be prepared before "
|
) const {
|
||||||
"Mult is called.");
|
MFEM_VERIFY(
|
||||||
|
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
|
||||||
|
"Mult is called."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(density.Size() == Width(),
|
MFEM_VERIFY(
|
||||||
"PreparedMappedGravitySourceOperator received a density vector "
|
density.Size() == Width(), "PreparedMappedGravitySourceOperator received a density vector "
|
||||||
"with the wrong size.");
|
"with the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
m_density_true.SetSize(m_density_map.full_size());
|
m_density_true.SetSize(m_density_map.full_size());
|
||||||
m_density_map.scatter(density, m_density_true);
|
m_density_map.scatter(density, m_density_true);
|
||||||
@@ -457,17 +461,21 @@ void PreparedMappedGravitySourceOperator::Mult(const mfem::Vector &density,
|
|||||||
local_to_true(*m_fem.gravityPotentialFes, local_action, m_action_true);
|
local_to_true(*m_fem.gravityPotentialFes, local_action, m_action_true);
|
||||||
action.SetSize(Height());
|
action.SetSize(Height());
|
||||||
m_potential_map.gather(m_action_true, action);
|
m_potential_map.gather(m_action_true, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreparedMappedGravitySourceOperator::MultTranspose(
|
void PreparedMappedGravitySourceOperator::MultTranspose(
|
||||||
const mfem::Vector &potential, mfem::Vector &action) const {
|
const mfem::Vector &potential,
|
||||||
MFEM_VERIFY(m_is_prepared,
|
mfem::Vector &action
|
||||||
"PreparedMappedGravitySourceOperator must be prepared before "
|
) const {
|
||||||
"MultTranspose is called.");
|
MFEM_VERIFY(
|
||||||
|
m_is_prepared, "PreparedMappedGravitySourceOperator must be prepared before "
|
||||||
|
"MultTranspose is called."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(potential.Size() == Height(),
|
MFEM_VERIFY(
|
||||||
"PreparedMappedGravitySourceOperator received a potential vector "
|
potential.Size() == Height(), "PreparedMappedGravitySourceOperator received a potential vector "
|
||||||
"with the wrong size.");
|
"with the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
m_potential_true.SetSize(m_potential_map.full_size());
|
m_potential_true.SetSize(m_potential_map.full_size());
|
||||||
m_potential_map.scatter(potential, m_potential_true);
|
m_potential_map.scatter(potential, m_potential_true);
|
||||||
@@ -512,28 +520,24 @@ void PreparedMappedGravitySourceOperator::MultTranspose(
|
|||||||
local_to_true(*m_fem.densityFes, local_action, m_action_true);
|
local_to_true(*m_fem.densityFes, local_action, m_action_true);
|
||||||
action.SetSize(Width());
|
action.SetSize(Width());
|
||||||
m_density_map.gather(m_action_true, action);
|
m_density_map.gather(m_action_true, action);
|
||||||
}
|
}
|
||||||
bool PreparedMappedGravitySourceOperator::IsPrepared() const noexcept {
|
bool PreparedMappedGravitySourceOperator::IsPrepared() const noexcept {
|
||||||
return m_is_prepared;
|
return m_is_prepared;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t
|
std::uint64_t PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
|
||||||
PreparedMappedGravitySourceOperator::GetPreparationCount() const noexcept {
|
|
||||||
return m_preparation_count;
|
return m_preparation_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
const field::FieldDofMap &
|
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetDensityMap() const noexcept {
|
||||||
PreparedMappedGravitySourceOperator::GetDensityMap() const noexcept {
|
|
||||||
return m_density_map;
|
return m_density_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
const field::FieldDofMap &
|
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetPotentialMap() const noexcept {
|
||||||
PreparedMappedGravitySourceOperator::GetPotentialMap() const noexcept {
|
|
||||||
return m_potential_map;
|
return m_potential_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
const field::FieldDofMap &
|
const field::FieldDofMap &PreparedMappedGravitySourceOperator::GetDisplacementMap() const noexcept {
|
||||||
PreparedMappedGravitySourceOperator::GetDisplacementMap() const noexcept {
|
|
||||||
return m_displacement_map;
|
return m_displacement_map;
|
||||||
}
|
}
|
||||||
} // namespace mean_field::operators
|
} // namespace mean_field::operators
|
||||||
|
|||||||
@@ -8,106 +8,119 @@ module mean_field;
|
|||||||
import :operators.prepared_hdiv_mass;
|
import :operators.prepared_hdiv_mass;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
int get_operator_size(const mean_field::fem::FEM &f) {
|
int get_operator_size(const mean_field::fem::FEM &f) {
|
||||||
MFEM_VERIFY(f.gravityFluxFes != nullptr,
|
MFEM_VERIFY(
|
||||||
"PreparedMappedHDivMassOperator requires the "
|
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||||
"gravity-gradient finite-element space.");
|
"gravity-gradient finite-element space."
|
||||||
return mean_field::field::make_field_dof_map<mean_field::field::Gravity,
|
);
|
||||||
DomainSchema>(*f.gravityFluxFes)
|
return mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes)
|
||||||
.reduced_size();
|
.reduced_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void true_to_local(const mfem::ParFiniteElementSpace &finite_element_space,
|
void true_to_local(
|
||||||
|
const mfem::ParFiniteElementSpace &finite_element_space,
|
||||||
const mfem::Vector &true_vector,
|
const mfem::Vector &true_vector,
|
||||||
mfem::Vector &local_vector) {
|
mfem::Vector &local_vector
|
||||||
|
) {
|
||||||
local_vector.SetSize(finite_element_space.GetVSize());
|
local_vector.SetSize(finite_element_space.GetVSize());
|
||||||
|
|
||||||
const mfem::Operator *prolongation =
|
const mfem::Operator *prolongation = finite_element_space.GetProlongationMatrix();
|
||||||
finite_element_space.GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (prolongation != nullptr) {
|
if (prolongation != nullptr) {
|
||||||
prolongation->Mult(true_vector, local_vector);
|
prolongation->Mult(true_vector, local_vector);
|
||||||
} else {
|
} else {
|
||||||
local_vector = true_vector;
|
local_vector = true_vector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int find_representative_element(const mean_field::fem::FEM &f,
|
int find_representative_element(
|
||||||
const mfem::Array<int> &marker) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const mfem::Array<int> &marker
|
||||||
|
) {
|
||||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||||
const int attribute = f.mesh->GetAttribute(element_id);
|
const int attribute = f.mesh->GetAttribute(element_id);
|
||||||
|
|
||||||
if (attribute > 0 && attribute <= marker.Size() &&
|
if (attribute > 0 && attribute <= marker.Size() && marker[attribute - 1] != 0) {
|
||||||
marker[attribute - 1] != 0) {
|
|
||||||
return element_id;
|
return element_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_uniform_domain_discretization(
|
void validate_uniform_domain_discretization(
|
||||||
const mean_field::fem::FEM &f, const mfem::Array<int> &marker,
|
const mean_field::fem::FEM &f,
|
||||||
const int representative_element_id) {
|
const mfem::Array<int> &marker,
|
||||||
const mfem::FiniteElement &representative_element =
|
const int representative_element_id
|
||||||
*f.gravityFluxFes->GetFE(representative_element_id);
|
) {
|
||||||
|
const mfem::FiniteElement &representative_element = *f.gravityFluxFes->GetFE(representative_element_id);
|
||||||
const mfem::ElementTransformation &representative_transformation =
|
const mfem::ElementTransformation &representative_transformation =
|
||||||
*f.mesh->GetElementTransformation(representative_element_id);
|
*f.mesh->GetElementTransformation(representative_element_id);
|
||||||
|
|
||||||
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
for (int element_id = 0; element_id < f.mesh->GetNE(); ++element_id) {
|
||||||
const int attribute = f.mesh->GetAttribute(element_id);
|
const int attribute = f.mesh->GetAttribute(element_id);
|
||||||
|
|
||||||
if (attribute <= 0 || attribute > marker.Size() ||
|
if (attribute <= 0 || attribute > marker.Size() || marker[attribute - 1] == 0) {
|
||||||
marker[attribute - 1] == 0) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::FiniteElement &element = *f.gravityFluxFes->GetFE(element_id);
|
const mfem::FiniteElement &element = *f.gravityFluxFes->GetFE(element_id);
|
||||||
const mfem::ElementTransformation &transformation =
|
const mfem::ElementTransformation &transformation = *f.mesh->GetElementTransformation(element_id);
|
||||||
*f.mesh->GetElementTransformation(element_id);
|
|
||||||
|
|
||||||
MFEM_VERIFY(element.GetGeomType() == representative_element.GetGeomType(),
|
MFEM_VERIFY(
|
||||||
|
element.GetGeomType() == representative_element.GetGeomType(),
|
||||||
"Prepared H(div) mass domains currently require a uniform "
|
"Prepared H(div) mass domains currently require a uniform "
|
||||||
"element "
|
"element "
|
||||||
"geometry.");
|
"geometry."
|
||||||
MFEM_VERIFY(element.GetOrder() == representative_element.GetOrder(),
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
element.GetOrder() == representative_element.GetOrder(),
|
||||||
"Prepared H(div) mass domains currently require a uniform "
|
"Prepared H(div) mass domains currently require a uniform "
|
||||||
"finite-element order.");
|
"finite-element order."
|
||||||
MFEM_VERIFY(transformation.OrderW() ==
|
);
|
||||||
representative_transformation.OrderW(),
|
MFEM_VERIFY(
|
||||||
|
transformation.OrderW() == representative_transformation.OrderW(),
|
||||||
"Prepared H(div) mass domains currently require a uniform "
|
"Prepared H(div) mass domains currently require a uniform "
|
||||||
"geometry-weight order.");
|
"geometry-weight order."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
|
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
|
||||||
public:
|
public:
|
||||||
FrozenMappedHDivMassCoefficient(
|
FrozenMappedHDivMassCoefficient(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domain_mapper,
|
const mean_field::mapping::DomainMapper &domain_mapper,
|
||||||
const mfem::Vector &displacement_true, bool elevates_vacuum)
|
const mfem::Vector &displacement_true,
|
||||||
: MatrixCoefficient(domain_mapper.GetDimension()), m_fem(f),
|
bool elevates_vacuum
|
||||||
|
)
|
||||||
|
: MatrixCoefficient(domain_mapper.GetDimension()),
|
||||||
|
m_fem(f),
|
||||||
m_domain_mapper(domain_mapper),
|
m_domain_mapper(domain_mapper),
|
||||||
m_workspace(domain_mapper.GetDimension()),
|
m_workspace(domain_mapper.GetDimension()),
|
||||||
m_elevates_vacuum(elevates_vacuum) {
|
m_elevates_vacuum(elevates_vacuum) {
|
||||||
true_to_local(*m_fem.displacementFes, displacement_true,
|
true_to_local(*m_fem.displacementFes, displacement_true, m_displacement_local);
|
||||||
m_displacement_local);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Eval(mfem::DenseMatrix &mass_tensor,
|
void Eval(
|
||||||
|
mfem::DenseMatrix &mass_tensor,
|
||||||
mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point) override {
|
const mfem::IntegrationPoint &integration_point
|
||||||
|
) override {
|
||||||
transformation.SetIntPoint(&integration_point);
|
transformation.SetIntPoint(&integration_point);
|
||||||
|
|
||||||
const int element_id = transformation.ElementNo;
|
const int element_id = transformation.ElementNo;
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
element_id >= 0 && element_id < m_fem.mesh->GetNE(),
|
||||||
"Mapped H(div) mass coefficient received an invalid element ID.");
|
"Mapped H(div) mass coefficient received an invalid element ID."
|
||||||
|
);
|
||||||
|
|
||||||
const bool element_is_vacuum = DomainSchema::template attribute_belongs_to<
|
const bool element_is_vacuum =
|
||||||
mean_field::utils::domain::Vacuum>(transformation.Attribute);
|
DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(
|
||||||
|
transformation.Attribute
|
||||||
|
);
|
||||||
|
|
||||||
if (element_is_vacuum != m_elevates_vacuum) {
|
if (element_is_vacuum != m_elevates_vacuum) {
|
||||||
mass_tensor.SetSize(m_domain_mapper.GetDimension());
|
mass_tensor.SetSize(m_domain_mapper.GetDimension());
|
||||||
@@ -118,79 +131,72 @@ public:
|
|||||||
LoadElement(element_id);
|
LoadElement(element_id);
|
||||||
|
|
||||||
const mean_field::mapping::ElementMappingData mapping_data{
|
const mean_field::mapping::ElementMappingData mapping_data{
|
||||||
.displacement = *m_displacement_data,
|
.displacement = *m_displacement_data, .compactification = *m_compactification_data
|
||||||
.compactification = *m_compactification_data};
|
};
|
||||||
|
|
||||||
mean_field::mapping::VolumeMappingContext mapping_context;
|
mean_field::mapping::VolumeMappingContext mapping_context;
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus status =
|
const mean_field::mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
|
||||||
m_domain_mapper.EvaluateVolume(mapping_data, transformation,
|
mapping_data, transformation, integration_point, m_workspace, mapping_context
|
||||||
integration_point, m_workspace,
|
);
|
||||||
mapping_context);
|
|
||||||
|
|
||||||
MFEM_VERIFY(status == mean_field::mapping::MappingStatus::valid,
|
MFEM_VERIFY(
|
||||||
|
status == mean_field::mapping::MappingStatus::valid,
|
||||||
"Stateless domain mapping failed while preparing the H(div) "
|
"Stateless domain mapping failed while preparing the H(div) "
|
||||||
"mass "
|
"mass "
|
||||||
"operator. Mapping status = "
|
"operator. Mapping status = "
|
||||||
<< static_cast<int>(status)
|
<< static_cast<int>(status) << ", element ID = " << element_id
|
||||||
<< ", element ID = " << element_id
|
|
||||||
<< ", element attribute = " << transformation.Attribute
|
<< ", element attribute = " << transformation.Attribute
|
||||||
<< ", coefficient domain = "
|
<< ", coefficient domain = " << (m_elevates_vacuum ? "vacuum" : "stellar")
|
||||||
<< (m_elevates_vacuum ? "vacuum" : "stellar"));
|
);
|
||||||
|
|
||||||
const mfem::DenseMatrix &mapping_jacobian =
|
const mfem::DenseMatrix &mapping_jacobian = mapping_context.mapping.mapping_jacobian;
|
||||||
mapping_context.mapping.mapping_jacobian;
|
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
|
||||||
const double mapping_determinant =
|
|
||||||
mapping_context.mapping.mapping_determinant;
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
MFEM_VERIFY(
|
||||||
|
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
|
||||||
"Prepared H(div) mass operator encountered a non-positive or "
|
"Prepared H(div) mass operator encountered a non-positive or "
|
||||||
"non-finite mapping determinant.");
|
"non-finite mapping determinant."
|
||||||
|
);
|
||||||
|
|
||||||
mfem::MultAtB(mapping_jacobian, mapping_jacobian, mass_tensor);
|
mfem::MultAtB(mapping_jacobian, mapping_jacobian, mass_tensor);
|
||||||
mass_tensor *= 1.0 / mapping_determinant;
|
mass_tensor *= 1.0 / mapping_determinant;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadElement(const int element_id) {
|
void LoadElement(const int element_id) {
|
||||||
if (element_id == m_cached_element_id) {
|
if (element_id == m_cached_element_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::FiniteElement &displacement_element =
|
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
|
||||||
*m_fem.displacementFes->GetFE(element_id);
|
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
|
||||||
const mfem::FiniteElement &compactification_element =
|
|
||||||
*m_fem.compactificationFes->GetFE(element_id);
|
|
||||||
|
|
||||||
mfem::DofTransformation *displacement_dof_transformation =
|
mfem::DofTransformation *displacement_dof_transformation =
|
||||||
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
m_fem.displacementFes->GetElementVDofs(element_id, m_displacement_dofs);
|
||||||
mfem::DofTransformation *compactification_dof_transformation =
|
mfem::DofTransformation *compactification_dof_transformation =
|
||||||
m_fem.compactificationFes->GetElementDofs(element_id,
|
m_fem.compactificationFes->GetElementDofs(element_id, m_compactification_dofs);
|
||||||
m_compactification_dofs);
|
|
||||||
|
|
||||||
m_displacement_local.GetSubVector(m_displacement_dofs,
|
m_displacement_local.GetSubVector(m_displacement_dofs, m_element_displacement);
|
||||||
m_element_displacement);
|
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs, m_element_compactification);
|
||||||
m_fem.compactificationCoordinate->GetSubVector(m_compactification_dofs,
|
|
||||||
m_element_compactification);
|
|
||||||
|
|
||||||
if (displacement_dof_transformation != nullptr) {
|
if (displacement_dof_transformation != nullptr) {
|
||||||
displacement_dof_transformation->InvTransformPrimal(
|
displacement_dof_transformation->InvTransformPrimal(m_element_displacement);
|
||||||
m_element_displacement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compactification_dof_transformation != nullptr) {
|
if (compactification_dof_transformation != nullptr) {
|
||||||
compactification_dof_transformation->InvTransformPrimal(
|
compactification_dof_transformation->InvTransformPrimal(m_element_compactification);
|
||||||
m_element_compactification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_displacement_data =
|
m_displacement_data = std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
||||||
std::make_unique<mean_field::mapping::ElementDisplacementData>(
|
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
||||||
displacement_element, m_element_displacement));
|
displacement_element, m_element_displacement
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
m_compactification_data =
|
m_compactification_data = std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
||||||
std::make_unique<mean_field::mapping::ElementCompactificationData>(
|
compactification_element, m_element_compactification
|
||||||
compactification_element, m_element_compactification);
|
);
|
||||||
|
|
||||||
m_cached_element_id = element_id;
|
m_cached_element_id = element_id;
|
||||||
}
|
}
|
||||||
@@ -206,100 +212,106 @@ private:
|
|||||||
mfem::Vector m_element_displacement;
|
mfem::Vector m_element_displacement;
|
||||||
mfem::Vector m_element_compactification;
|
mfem::Vector m_element_compactification;
|
||||||
|
|
||||||
std::unique_ptr<mean_field::mapping::ElementDisplacementData>
|
std::unique_ptr<mean_field::mapping::ElementDisplacementData> m_displacement_data;
|
||||||
m_displacement_data;
|
std::unique_ptr<mean_field::mapping::ElementCompactificationData> m_compactification_data;
|
||||||
std::unique_ptr<mean_field::mapping::ElementCompactificationData>
|
|
||||||
m_compactification_data;
|
|
||||||
|
|
||||||
mean_field::mapping::DomainMapper::Workspace m_workspace;
|
mean_field::mapping::DomainMapper::Workspace m_workspace;
|
||||||
int m_cached_element_id{-1};
|
int m_cached_element_id{-1};
|
||||||
bool m_elevates_vacuum;
|
bool m_elevates_vacuum;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace mean_field::operators {
|
namespace mean_field::operators {
|
||||||
PreparedMappedHDivMassOperator::PreparedMappedHDivMassOperator(
|
PreparedMappedHDivMassOperator::PreparedMappedHDivMassOperator(
|
||||||
const fem::FEM &f, const mapping::DomainMapper &domain_mapper)
|
const fem::FEM &f,
|
||||||
: Operator(get_operator_size(f)), m_fem(f), m_domain_mapper(domain_mapper),
|
const mapping::DomainMapper &domain_mapper
|
||||||
m_flux_map(field::make_field_dof_map<field::Gravity, DomainSchema>(
|
)
|
||||||
*f.gravityFluxFes)),
|
: Operator(get_operator_size(f)),
|
||||||
|
m_fem(f),
|
||||||
|
m_domain_mapper(domain_mapper),
|
||||||
|
m_flux_map(
|
||||||
|
field::make_field_dof_map<
|
||||||
|
field::Gravity,
|
||||||
|
DomainSchema>(*f.gravityFluxFes)
|
||||||
|
),
|
||||||
m_displacement_map(
|
m_displacement_map(
|
||||||
field::make_field_dof_map<field::Displacement, DomainSchema>(
|
field::make_field_dof_map<
|
||||||
*f.displacementFes)) {
|
field::Displacement,
|
||||||
MFEM_VERIFY(f.mesh != nullptr,
|
DomainSchema>(*f.displacementFes)
|
||||||
"PreparedMappedHDivMassOperator requires a mesh.");
|
) {
|
||||||
MFEM_VERIFY(f.gravityFluxFes != nullptr,
|
MFEM_VERIFY(f.mesh != nullptr, "PreparedMappedHDivMassOperator requires a mesh.");
|
||||||
"PreparedMappedHDivMassOperator requires the "
|
MFEM_VERIFY(
|
||||||
"gravity-gradient finite-element space.");
|
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||||
MFEM_VERIFY(f.displacementFes != nullptr,
|
"gravity-gradient finite-element space."
|
||||||
"PreparedMappedHDivMassOperator requires the "
|
);
|
||||||
"displacement finite-element space.");
|
MFEM_VERIFY(
|
||||||
MFEM_VERIFY(f.compactificationFes != nullptr,
|
f.displacementFes != nullptr, "PreparedMappedHDivMassOperator requires the "
|
||||||
"PreparedMappedHDivMassOperator requires the compactification "
|
"displacement finite-element space."
|
||||||
"finite-element space.");
|
);
|
||||||
MFEM_VERIFY(f.compactificationCoordinate != nullptr,
|
MFEM_VERIFY(
|
||||||
"PreparedMappedHDivMassOperator requires the compactification "
|
f.compactificationFes != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
|
||||||
"coordinate.");
|
"finite-element space."
|
||||||
MFEM_VERIFY(f.quadratureFactory != nullptr,
|
);
|
||||||
"PreparedMappedHDivMassOperator requires the quadrature-rule "
|
MFEM_VERIFY(
|
||||||
"factory.");
|
f.compactificationCoordinate != nullptr, "PreparedMappedHDivMassOperator requires the compactification "
|
||||||
MFEM_VERIFY(domain_mapper.GetDimension() == f.mesh->Dimension(),
|
"coordinate."
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
f.quadratureFactory != nullptr, "PreparedMappedHDivMassOperator requires the quadrature-rule "
|
||||||
|
"factory."
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
domain_mapper.GetDimension() == f.mesh->Dimension(),
|
||||||
"The stateless domain-mapper dimension does not match the mesh "
|
"The stateless domain-mapper dimension does not match the mesh "
|
||||||
"dimension.");
|
"dimension."
|
||||||
|
);
|
||||||
|
|
||||||
m_stellar_marker =
|
m_stellar_marker = utils::domain::make_attribute_marker<utils::domain::Stellar, DomainSchema>(*f.mesh);
|
||||||
utils::domain::make_attribute_marker<utils::domain::Stellar,
|
m_vacuum_marker = utils::domain::make_attribute_marker<utils::domain::Vacuum, DomainSchema>(*f.mesh);
|
||||||
DomainSchema>(*f.mesh);
|
|
||||||
m_vacuum_marker =
|
|
||||||
utils::domain::make_attribute_marker<utils::domain::Vacuum, DomainSchema>(
|
|
||||||
*f.mesh);
|
|
||||||
|
|
||||||
const int stellar_element_id =
|
const int stellar_element_id = find_representative_element(f, m_stellar_marker);
|
||||||
find_representative_element(f, m_stellar_marker);
|
|
||||||
const int vacuum_element_id = find_representative_element(f, m_vacuum_marker);
|
const int vacuum_element_id = find_representative_element(f, m_vacuum_marker);
|
||||||
|
|
||||||
MFEM_VERIFY(stellar_element_id >= 0,
|
MFEM_VERIFY(
|
||||||
"PreparedMappedHDivMassOperator requires "
|
stellar_element_id >= 0, "PreparedMappedHDivMassOperator requires "
|
||||||
"at least one stellar element.");
|
"at least one stellar element."
|
||||||
MFEM_VERIFY(vacuum_element_id >= 0,
|
);
|
||||||
"PreparedMappedHDivMassOperator requires at "
|
MFEM_VERIFY(
|
||||||
"least one compactified vacuum element.");
|
vacuum_element_id >= 0, "PreparedMappedHDivMassOperator requires at "
|
||||||
|
"least one compactified vacuum element."
|
||||||
|
);
|
||||||
|
|
||||||
validate_uniform_domain_discretization(f, m_stellar_marker,
|
validate_uniform_domain_discretization(f, m_stellar_marker, stellar_element_id);
|
||||||
stellar_element_id);
|
|
||||||
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
|
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
|
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
|
||||||
MFEM_VERIFY(displacement.Size() == m_displacement_map.reduced_size(),
|
MFEM_VERIFY(
|
||||||
|
displacement.Size() == m_displacement_map.reduced_size(),
|
||||||
"PreparedMappedHDivMassOperator received a displacement vector "
|
"PreparedMappedHDivMassOperator received a displacement vector "
|
||||||
"with "
|
"with "
|
||||||
"the wrong size.");
|
"the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
for (int i = 0; i < displacement.Size(); ++i) {
|
for (int i = 0; i < displacement.Size(); ++i) {
|
||||||
MFEM_VERIFY(std::isfinite(displacement(i)),
|
MFEM_VERIFY(
|
||||||
"PreparedMappedHDivMassOperator received a non-finite "
|
std::isfinite(displacement(i)), "PreparedMappedHDivMassOperator received a non-finite "
|
||||||
"displacement "
|
"displacement "
|
||||||
"value.");
|
"value."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_displacement_true.SetSize(m_displacement_map.full_size());
|
m_displacement_true.SetSize(m_displacement_map.full_size());
|
||||||
m_displacement_map.scatter(displacement, m_displacement_true);
|
m_displacement_map.scatter(displacement, m_displacement_true);
|
||||||
|
|
||||||
const int stellar_element_id =
|
const int stellar_element_id = find_representative_element(m_fem, m_stellar_marker);
|
||||||
find_representative_element(m_fem, m_stellar_marker);
|
const int vacuum_element_id = find_representative_element(m_fem, m_vacuum_marker);
|
||||||
const int vacuum_element_id =
|
|
||||||
find_representative_element(m_fem, m_vacuum_marker);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &stellar_element =
|
const mfem::FiniteElement &stellar_element = *m_fem.gravityFluxFes->GetFE(stellar_element_id);
|
||||||
*m_fem.gravityFluxFes->GetFE(stellar_element_id);
|
const mfem::FiniteElement &vacuum_element = *m_fem.gravityFluxFes->GetFE(vacuum_element_id);
|
||||||
const mfem::FiniteElement &vacuum_element =
|
|
||||||
*m_fem.gravityFluxFes->GetFE(vacuum_element_id);
|
|
||||||
|
|
||||||
mfem::ElementTransformation &stellar_transformation =
|
mfem::ElementTransformation &stellar_transformation = *m_fem.mesh->GetElementTransformation(stellar_element_id);
|
||||||
*m_fem.mesh->GetElementTransformation(stellar_element_id);
|
mfem::ElementTransformation &vacuum_transformation = *m_fem.mesh->GetElementTransformation(vacuum_element_id);
|
||||||
mfem::ElementTransformation &vacuum_transformation =
|
|
||||||
*m_fem.mesh->GetElementTransformation(vacuum_element_id);
|
|
||||||
|
|
||||||
m_stellar_mass_form.reset();
|
m_stellar_mass_form.reset();
|
||||||
m_vacuum_mass_form.reset();
|
m_vacuum_mass_form.reset();
|
||||||
@@ -307,55 +319,53 @@ void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
|
|||||||
m_vacuum_mass_coefficient.reset();
|
m_vacuum_mass_coefficient.reset();
|
||||||
|
|
||||||
m_stellar_mass_coefficient =
|
m_stellar_mass_coefficient =
|
||||||
std::make_unique<FrozenMappedHDivMassCoefficient>(
|
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, m_displacement_true, false);
|
||||||
m_fem, m_domain_mapper, m_displacement_true, false);
|
m_vacuum_mass_coefficient =
|
||||||
m_vacuum_mass_coefficient = std::make_unique<FrozenMappedHDivMassCoefficient>(
|
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, m_displacement_true, true);
|
||||||
m_fem, m_domain_mapper, m_displacement_true, true);
|
|
||||||
|
|
||||||
m_stellar_mass_form =
|
m_stellar_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||||
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
m_vacuum_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
||||||
m_vacuum_mass_form =
|
|
||||||
std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
|
|
||||||
m_stellar_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
m_stellar_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||||
m_vacuum_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
m_vacuum_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||||
|
|
||||||
auto stellar_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(
|
auto stellar_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_stellar_mass_coefficient);
|
||||||
*m_stellar_mass_coefficient);
|
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_vacuum_mass_coefficient);
|
||||||
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(
|
|
||||||
*m_vacuum_mass_coefficient);
|
|
||||||
|
|
||||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||||
*stellar_integrator, quadrature::QuadratureRole::discretization,
|
*stellar_integrator, quadrature::QuadratureRole::discretization, stellar_element, stellar_transformation,
|
||||||
stellar_element, stellar_transformation, utils::DOMAINS::STELLAR,
|
utils::DOMAINS::STELLAR, quadrature::MappingKind::general
|
||||||
quadrature::MappingKind::general);
|
);
|
||||||
|
|
||||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||||
*vacuum_integrator, quadrature::QuadratureRole::discretization,
|
*vacuum_integrator, quadrature::QuadratureRole::discretization, vacuum_element, vacuum_transformation,
|
||||||
vacuum_element, vacuum_transformation, utils::DOMAINS::VACUUM,
|
utils::DOMAINS::VACUUM, quadrature::MappingKind::kelvin
|
||||||
quadrature::MappingKind::kelvin);
|
);
|
||||||
|
|
||||||
m_stellar_mass_form->AddDomainIntegrator(stellar_integrator.release(),
|
m_stellar_mass_form->AddDomainIntegrator(stellar_integrator.release(), m_stellar_marker);
|
||||||
m_stellar_marker);
|
m_vacuum_mass_form->AddDomainIntegrator(vacuum_integrator.release(), m_vacuum_marker);
|
||||||
m_vacuum_mass_form->AddDomainIntegrator(vacuum_integrator.release(),
|
|
||||||
m_vacuum_marker);
|
|
||||||
m_stellar_mass_form->Assemble();
|
m_stellar_mass_form->Assemble();
|
||||||
m_vacuum_mass_form->Assemble();
|
m_vacuum_mass_form->Assemble();
|
||||||
|
|
||||||
m_is_prepared = true;
|
m_is_prepared = true;
|
||||||
++m_preparation_count;
|
++m_preparation_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreparedMappedHDivMassOperator::Mult(const mfem::Vector &gravity_gradient,
|
void PreparedMappedHDivMassOperator::Mult(
|
||||||
mfem::Vector &action) const {
|
const mfem::Vector &gravity_gradient,
|
||||||
MFEM_VERIFY(m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
mfem::Vector &action
|
||||||
"before Mult is called.");
|
) const {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
||||||
|
"before Mult is called."
|
||||||
|
);
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
|
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
|
||||||
"PreparedMappedHDivMassOperator has incomplete domain mass forms.");
|
"PreparedMappedHDivMassOperator has incomplete domain mass forms."
|
||||||
|
);
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
gravity_gradient.Size() == Width(),
|
gravity_gradient.Size() == Width(), "PreparedMappedHDivMassOperator received a gravity-gradient vector "
|
||||||
"PreparedMappedHDivMassOperator received a gravity-gradient vector "
|
"with the wrong size."
|
||||||
"with the wrong size.");
|
);
|
||||||
|
|
||||||
m_flux_true.SetSize(m_flux_map.full_size());
|
m_flux_true.SetSize(m_flux_map.full_size());
|
||||||
m_action_true.SetSize(m_flux_map.full_size());
|
m_action_true.SetSize(m_flux_map.full_size());
|
||||||
@@ -366,47 +376,45 @@ void PreparedMappedHDivMassOperator::Mult(const mfem::Vector &gravity_gradient,
|
|||||||
m_action_true += m_domain_action_true;
|
m_action_true += m_domain_action_true;
|
||||||
action.SetSize(Height());
|
action.SetSize(Height());
|
||||||
m_flux_map.gather(m_action_true, action);
|
m_flux_map.gather(m_action_true, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreparedMappedHDivMassOperator::AssembleDiagonal(
|
void PreparedMappedHDivMassOperator::AssembleDiagonal(mfem::Vector &diagonal) const {
|
||||||
mfem::Vector &diagonal) const {
|
|
||||||
mfem::Vector true_diagonal;
|
mfem::Vector true_diagonal;
|
||||||
AssembleTrueDiagonal(true_diagonal);
|
AssembleTrueDiagonal(true_diagonal);
|
||||||
diagonal.SetSize(Height());
|
diagonal.SetSize(Height());
|
||||||
m_flux_map.gather(true_diagonal, diagonal);
|
m_flux_map.gather(true_diagonal, diagonal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreparedMappedHDivMassOperator::AssembleTrueDiagonal(
|
void PreparedMappedHDivMassOperator::AssembleTrueDiagonal(mfem::Vector &diagonal) const {
|
||||||
mfem::Vector &diagonal) const {
|
MFEM_VERIFY(
|
||||||
MFEM_VERIFY(m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
m_is_prepared, "PreparedMappedHDivMassOperator must be prepared "
|
||||||
"before assembling its diagonal.");
|
"before assembling its diagonal."
|
||||||
|
);
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
|
m_stellar_mass_form != nullptr && m_vacuum_mass_form != nullptr,
|
||||||
"PreparedMappedHDivMassOperator has incomplete domain mass forms.");
|
"PreparedMappedHDivMassOperator has incomplete domain mass forms."
|
||||||
|
);
|
||||||
|
|
||||||
diagonal.SetSize(m_flux_map.full_size());
|
diagonal.SetSize(m_flux_map.full_size());
|
||||||
mfem::Vector domain_diagonal(m_flux_map.full_size());
|
mfem::Vector domain_diagonal(m_flux_map.full_size());
|
||||||
m_stellar_mass_form->AssembleDiagonal(diagonal);
|
m_stellar_mass_form->AssembleDiagonal(diagonal);
|
||||||
m_vacuum_mass_form->AssembleDiagonal(domain_diagonal);
|
m_vacuum_mass_form->AssembleDiagonal(domain_diagonal);
|
||||||
diagonal += domain_diagonal;
|
diagonal += domain_diagonal;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PreparedMappedHDivMassOperator::IsPrepared() const noexcept {
|
bool PreparedMappedHDivMassOperator::IsPrepared() const noexcept {
|
||||||
return m_is_prepared;
|
return m_is_prepared;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t
|
std::uint64_t PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
|
||||||
PreparedMappedHDivMassOperator::GetPreparationCount() const noexcept {
|
|
||||||
return m_preparation_count;
|
return m_preparation_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
const field::FieldDofMap &
|
const field::FieldDofMap &PreparedMappedHDivMassOperator::GetFluxMap() const noexcept {
|
||||||
PreparedMappedHDivMassOperator::GetFluxMap() const noexcept {
|
|
||||||
return m_flux_map;
|
return m_flux_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
const field::FieldDofMap &
|
const field::FieldDofMap &PreparedMappedHDivMassOperator::GetDisplacementMap() const noexcept {
|
||||||
PreparedMappedHDivMassOperator::GetDisplacementMap() const noexcept {
|
|
||||||
return m_displacement_map;
|
return m_displacement_map;
|
||||||
}
|
}
|
||||||
} // namespace mean_field::operators
|
} // namespace mean_field::operators
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -619,9 +619,15 @@ namespace mean_field::operators {
|
|||||||
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||||
const double enthalpy = quadratureEnthalpy(quadraturePoint);
|
const double enthalpy = quadratureEnthalpy(quadraturePoint);
|
||||||
|
|
||||||
const double pressure = m_equationOfState.pressure_from_enthalpy(enthalpy);
|
const eos::SpecificEnthalpyValue specificEnthalpy{enthalpy};
|
||||||
|
const double pressure =
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(m_equationOfState, specificEnthalpy).value();
|
||||||
|
|
||||||
const double pressureDerivative = m_equationOfState.pressure_derivative_from_enthalpy(enthalpy);
|
const double pressureDerivative =
|
||||||
|
eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
m_equationOfState, specificEnthalpy
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
const double quadratureWeight = data.quadratureWeights(quadraturePoint);
|
const double quadratureWeight = data.quadratureWeights(quadraturePoint);
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ namespace {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] mfem::Vector make_computational_origin(const mfem::ParMesh &mesh) {
|
||||||
|
mfem::Vector origin(mesh.SpaceDimension());
|
||||||
|
origin = 0.0;
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] mean_field::operators::StellarEquilibriumLayout make_layout(
|
[[nodiscard]] mean_field::operators::StellarEquilibriumLayout make_layout(
|
||||||
const mean_field::field::FieldDofMap &densityMap,
|
const mean_field::field::FieldDofMap &densityMap,
|
||||||
const mean_field::field::FieldDofMap &displacementMap,
|
const mean_field::field::FieldDofMap &displacementMap,
|
||||||
@@ -253,6 +259,8 @@ namespace mean_field::operators {
|
|||||||
field::FieldDofMap gravityFluxMap;
|
field::FieldDofMap gravityFluxMap;
|
||||||
field::FieldDofMap gravityPotentialMap;
|
field::FieldDofMap gravityPotentialMap;
|
||||||
field::FieldDofMap enthalpyMap;
|
field::FieldDofMap enthalpyMap;
|
||||||
|
field::FieldBoundaryDofMap pressureSurfaceRows;
|
||||||
|
field::FieldPointDofMap centerDisplacementRows;
|
||||||
|
|
||||||
StellarEquilibriumLayout layout;
|
StellarEquilibriumLayout layout;
|
||||||
mfem::Array<int> gravityStateOffsets;
|
mfem::Array<int> gravityStateOffsets;
|
||||||
@@ -284,6 +292,23 @@ namespace mean_field::operators {
|
|||||||
field::Enthalpy,
|
field::Enthalpy,
|
||||||
DomainSchema>(*f.enthalpyFes)
|
DomainSchema>(*f.enthalpyFes)
|
||||||
),
|
),
|
||||||
|
pressureSurfaceRows(
|
||||||
|
field::make_field_boundary_dof_map<
|
||||||
|
field::Enthalpy,
|
||||||
|
utils::domain::StellarSurface,
|
||||||
|
DomainSchema>(
|
||||||
|
*f.enthalpyFes,
|
||||||
|
enthalpyMap
|
||||||
|
)
|
||||||
|
),
|
||||||
|
centerDisplacementRows(
|
||||||
|
field::make_field_point_dof_map<field::Displacement>(
|
||||||
|
*f.displacementFes,
|
||||||
|
displacementMap,
|
||||||
|
make_computational_origin(*f.mesh),
|
||||||
|
1.0e-12
|
||||||
|
)
|
||||||
|
),
|
||||||
layout(make_layout(
|
layout(make_layout(
|
||||||
densityMap,
|
densityMap,
|
||||||
displacementMap,
|
displacementMap,
|
||||||
@@ -314,27 +339,15 @@ namespace mean_field::operators {
|
|||||||
fem::FEM &f,
|
fem::FEM &f,
|
||||||
const mapping::DomainMapper &domainMapper,
|
const mapping::DomainMapper &domainMapper,
|
||||||
const eos::Polytrope &equationOfState,
|
const eos::Polytrope &equationOfState,
|
||||||
const models::StellarModel &stellarModel
|
const double targetMass,
|
||||||
)
|
const PressureSurfaceConstraintView surfaceConstraint
|
||||||
: PreparedStellarEquilibriumOperator(
|
|
||||||
f,
|
|
||||||
domainMapper,
|
|
||||||
equationOfState,
|
|
||||||
stellarModel.targetMass()
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PreparedStellarEquilibriumOperator::PreparedStellarEquilibriumOperator(
|
|
||||||
fem::FEM &f,
|
|
||||||
const mapping::DomainMapper &domainMapper,
|
|
||||||
const eos::Polytrope &equationOfState,
|
|
||||||
const double targetMass
|
|
||||||
)
|
)
|
||||||
: PreparedStellarEquilibriumOperator(
|
: PreparedStellarEquilibriumOperator(
|
||||||
f,
|
f,
|
||||||
domainMapper,
|
domainMapper,
|
||||||
equationOfState,
|
equationOfState,
|
||||||
targetMass,
|
targetMass,
|
||||||
|
surfaceConstraint,
|
||||||
MakeConstructionData(f)
|
MakeConstructionData(f)
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@@ -344,6 +357,7 @@ namespace mean_field::operators {
|
|||||||
const mapping::DomainMapper &domainMapper,
|
const mapping::DomainMapper &domainMapper,
|
||||||
const eos::Polytrope &equationOfState,
|
const eos::Polytrope &equationOfState,
|
||||||
const double targetMass,
|
const double targetMass,
|
||||||
|
const PressureSurfaceConstraintView surfaceConstraint,
|
||||||
ConstructionData constructionData
|
ConstructionData constructionData
|
||||||
)
|
)
|
||||||
: mfem::Operator(
|
: mfem::Operator(
|
||||||
@@ -390,6 +404,11 @@ namespace mean_field::operators {
|
|||||||
domainMapper,
|
domainMapper,
|
||||||
m_gravityContext
|
m_gravityContext
|
||||||
),
|
),
|
||||||
|
m_surfaceConstraintOperator(
|
||||||
|
constructionData.pressureSurfaceRows,
|
||||||
|
surfaceConstraint
|
||||||
|
),
|
||||||
|
m_centeringConstraintOperator(constructionData.centerDisplacementRows),
|
||||||
m_targetMass(targetMass) {
|
m_targetMass(targetMass) {
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
std::isfinite(m_targetMass) && m_targetMass > 0.0,
|
std::isfinite(m_targetMass) && m_targetMass > 0.0,
|
||||||
@@ -506,6 +525,14 @@ namespace mean_field::operators {
|
|||||||
report.massNormalization =
|
report.massNormalization =
|
||||||
m_massNormalizationOperator.Prepare({.targetMass = m_targetMass}, make_mass_dependencies(dependencies));
|
m_massNormalizationOperator.Prepare({.targetMass = m_targetMass}, make_mass_dependencies(dependencies));
|
||||||
|
|
||||||
|
report.surfaceConstraint = m_surfaceConstraintOperator.Prepare(
|
||||||
|
reducedEnthalpy, !wasPrepared || dependencies.enthalpy != m_preparedDependencies.enthalpy
|
||||||
|
);
|
||||||
|
|
||||||
|
report.centeringConstraint = m_centeringConstraintOperator.Prepare(
|
||||||
|
displacement, !wasPrepared || dependencies.displacement != m_preparedDependencies.displacement
|
||||||
|
);
|
||||||
|
|
||||||
const bool dependenciesChanged = !wasPrepared || dependencies != m_preparedDependencies;
|
const bool dependenciesChanged = !wasPrepared || dependencies != m_preparedDependencies;
|
||||||
if (dependenciesChanged || report.DidAnyChildWork()) {
|
if (dependenciesChanged || report.DidAnyChildWork()) {
|
||||||
AssembleResidual();
|
AssembleResidual();
|
||||||
@@ -542,7 +569,9 @@ namespace mean_field::operators {
|
|||||||
m_gravityOperator.Mult(m_gravityState, gravity);
|
m_gravityOperator.Mult(m_gravityState, gravity);
|
||||||
m_barotropicClosureOperator.BuildResidual(closure);
|
m_barotropicClosureOperator.BuildResidual(closure);
|
||||||
m_displacementOperator.BuildResidual(displacement);
|
m_displacementOperator.BuildResidual(displacement);
|
||||||
|
m_centeringConstraintOperator.ApplyResidualRows(displacement);
|
||||||
m_hydrostaticOperator.BuildResidual(hydrostatic);
|
m_hydrostaticOperator.BuildResidual(hydrostatic);
|
||||||
|
m_surfaceConstraintOperator.ApplyResidualRows(hydrostatic);
|
||||||
m_massNormalizationOperator.BuildResidual(mass);
|
m_massNormalizationOperator.BuildResidual(mass);
|
||||||
|
|
||||||
m_cachedResidual.SetSize(Height());
|
m_cachedResidual.SetSize(Height());
|
||||||
@@ -659,11 +688,13 @@ namespace mean_field::operators {
|
|||||||
reducedDensityDirection, displacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
|
reducedDensityDirection, displacementDirection, gravityGradientDirection, reducedEnthalpyDirection,
|
||||||
displacementAction
|
displacementAction
|
||||||
);
|
);
|
||||||
|
m_centeringConstraintOperator.ApplyJacobianRows(displacementDirection, displacementAction);
|
||||||
|
|
||||||
m_hydrostaticOperator.ApplyCompleteJacobianAction(
|
m_hydrostaticOperator.ApplyCompleteJacobianAction(
|
||||||
reducedEnthalpyDirection, gravityPotentialDirection, bernoulliDirection(0), displacementDirection,
|
reducedEnthalpyDirection, gravityPotentialDirection, bernoulliDirection(0), displacementDirection,
|
||||||
hydrostaticAction
|
hydrostaticAction
|
||||||
);
|
);
|
||||||
|
m_surfaceConstraintOperator.ApplyJacobianRows(reducedEnthalpyDirection, hydrostaticAction);
|
||||||
|
|
||||||
m_massNormalizationOperator.ApplyCompleteJacobianAction(
|
m_massNormalizationOperator.ApplyCompleteJacobianAction(
|
||||||
reducedDensityDirection, displacementDirection, massAction
|
reducedDensityDirection, displacementDirection, massAction
|
||||||
@@ -712,7 +743,8 @@ namespace mean_field::operators {
|
|||||||
bool PreparedStellarEquilibriumOperator::IsPrepared() const noexcept {
|
bool PreparedStellarEquilibriumOperator::IsPrepared() const noexcept {
|
||||||
return m_isPrepared && m_gravityContext.IsPrepared() && m_barotropicClosureOperator.IsPrepared() &&
|
return m_isPrepared && m_gravityContext.IsPrepared() && m_barotropicClosureOperator.IsPrepared() &&
|
||||||
m_hydrostaticOperator.IsPrepared() && m_displacementOperator.IsPrepared() &&
|
m_hydrostaticOperator.IsPrepared() && m_displacementOperator.IsPrepared() &&
|
||||||
m_massNormalizationOperator.IsPrepared();
|
m_massNormalizationOperator.IsPrepared() && m_surfaceConstraintOperator.IsPrepared() &&
|
||||||
|
m_centeringConstraintOperator.IsPrepared();
|
||||||
}
|
}
|
||||||
|
|
||||||
double PreparedStellarEquilibriumOperator::GetTargetMass() const noexcept {
|
double PreparedStellarEquilibriumOperator::GetTargetMass() const noexcept {
|
||||||
@@ -771,6 +803,16 @@ namespace mean_field::operators {
|
|||||||
return m_massNormalizationOperator;
|
return m_massNormalizationOperator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PreparedPressureSurfaceConstraint &
|
||||||
|
PreparedStellarEquilibriumOperator::GetSurfaceConstraintOperator() const noexcept {
|
||||||
|
return m_surfaceConstraintOperator;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreparedCenteringConstraint &
|
||||||
|
PreparedStellarEquilibriumOperator::GetCenteringConstraintOperator() const noexcept {
|
||||||
|
return m_centeringConstraintOperator;
|
||||||
|
}
|
||||||
|
|
||||||
void PreparedStellarEquilibriumOperator::VerifyPrepared() const {
|
void PreparedStellarEquilibriumOperator::VerifyPrepared() const {
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
IsPrepared(), "PreparedStellarEquilibriumOperator must be prepared before residual or Jacobian application."
|
IsPrepared(), "PreparedStellarEquilibriumOperator must be prepared before residual or Jacobian application."
|
||||||
|
|||||||
@@ -16,13 +16,11 @@ namespace mean_field::physics {
|
|||||||
local_Q = 0.0;
|
local_Q = 0.0;
|
||||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||||
*fem.domainMapperStateless, *fem.displacement,
|
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||||
*fem.compactificationCoordinate
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
|
for (int i = 0; i < fem.mesh->GetNE(); ++i) {
|
||||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
|
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(fem.mesh->GetAttribute(i)))
|
||||||
fem.mesh->GetAttribute(i)))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
mfem::ElementTransformation *trans = fem.mesh->GetElementTransformation(i);
|
||||||
@@ -40,8 +38,7 @@ namespace mean_field::physics {
|
|||||||
|
|
||||||
mapping::VolumeMappingContext mapping_context;
|
mapping::VolumeMappingContext mapping_context;
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) ==
|
mapping_evaluator.EvaluateVolume(*trans, ip, mapping_context) == mapping::MappingStatus::valid,
|
||||||
mapping::MappingStatus::valid,
|
|
||||||
"Quadrupole integration encountered an invalid mapping."
|
"Quadrupole integration encountered an invalid mapping."
|
||||||
);
|
);
|
||||||
const double weight = mapping_context.quadrature.weight;
|
const double weight = mapping_context.quadrature.weight;
|
||||||
@@ -220,12 +217,8 @@ namespace mean_field::physics {
|
|||||||
|
|
||||||
GravitySolution solution(f);
|
GravitySolution solution(f);
|
||||||
|
|
||||||
gravity_flux_adapter.scatter(
|
gravity_flux_adapter.scatter(gravity_state.GetBlock(gravity_gradient_residual_block), solution.gradPhi);
|
||||||
gravity_state.GetBlock(gravity_gradient_residual_block), solution.gradPhi
|
gravity_potential_adapter.scatter(gravity_state.GetBlock(gravity_poisson_residual_block), solution.phi);
|
||||||
);
|
|
||||||
gravity_potential_adapter.scatter(
|
|
||||||
gravity_state.GetBlock(gravity_poisson_residual_block), solution.phi
|
|
||||||
);
|
|
||||||
|
|
||||||
return solution;
|
return solution;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,11 @@ namespace mean_field::physics {
|
|||||||
double local_I = 0.0;
|
double local_I = 0.0;
|
||||||
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||||
*fem.domainMapperStateless, *fem.displacement,
|
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||||
*fem.compactificationCoordinate
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for (int i = 0; i < fem.mesh->GetNE(); i++) {
|
for (int i = 0; i < fem.mesh->GetNE(); i++) {
|
||||||
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(
|
if (!DomainSchema::template attribute_belongs_to<utils::domain::Stellar>(fem.mesh->GetAttribute(i)))
|
||||||
fem.mesh->GetAttribute(i)))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(i);
|
mfem::ElementTransformation *T = fem.mesh->GetElementTransformation(i);
|
||||||
@@ -37,8 +35,7 @@ namespace mean_field::physics {
|
|||||||
|
|
||||||
mapping::VolumeMappingContext mapping_context;
|
mapping::VolumeMappingContext mapping_context;
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
mapping_evaluator.EvaluateVolume(*T, ip, mapping_context) ==
|
mapping_evaluator.EvaluateVolume(*T, ip, mapping_context) == mapping::MappingStatus::valid,
|
||||||
mapping::MappingStatus::valid,
|
|
||||||
"Moment-of-inertia integration encountered an invalid mapping."
|
"Moment-of-inertia integration encountered an invalid mapping."
|
||||||
);
|
);
|
||||||
const mfem::Vector &x_phys = mapping_context.mapping.physical_position;
|
const mfem::Vector &x_phys = mapping_context.mapping.physical_position;
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ namespace mean_field::utils {
|
|||||||
const int dim = fem.mesh->Dimension();
|
const int dim = fem.mesh->Dimension();
|
||||||
x_ref = x_phys_target;
|
x_ref = x_phys_target;
|
||||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
||||||
*fem.domainMapperStateless, *fem.displacement,
|
*fem.domainMapperStateless, *fem.displacement, *fem.compactificationCoordinate
|
||||||
*fem.compactificationCoordinate
|
|
||||||
);
|
);
|
||||||
|
|
||||||
mfem::Array<int> init_elem;
|
mfem::Array<int> init_elem;
|
||||||
@@ -39,8 +38,7 @@ namespace mean_field::utils {
|
|||||||
|
|
||||||
mapping::MappingPointContext context;
|
mapping::MappingPointContext context;
|
||||||
MFEM_VERIFY(
|
MFEM_VERIFY(
|
||||||
mapping_evaluator.EvaluatePoint(*T0, origin_ip[0], context) ==
|
mapping_evaluator.EvaluatePoint(*T0, origin_ip[0], context) == mapping::MappingStatus::valid,
|
||||||
mapping::MappingStatus::valid,
|
|
||||||
"Reference-point initialization encountered an invalid mapping."
|
"Reference-point initialization encountered an invalid mapping."
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -104,8 +102,7 @@ namespace mean_field::utils {
|
|||||||
T->SetIntPoint(&ip);
|
T->SetIntPoint(&ip);
|
||||||
|
|
||||||
mapping::MappingPointContext context;
|
mapping::MappingPointContext context;
|
||||||
if (mapping_evaluator.EvaluatePoint(*T, ip, context) !=
|
if (mapping_evaluator.EvaluatePoint(*T, ip, context) != mapping::MappingStatus::valid) {
|
||||||
mapping::MappingStatus::valid) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const mfem::Vector ¤t_x_phys = context.physical_position;
|
const mfem::Vector ¤t_x_phys = context.physical_position;
|
||||||
|
|||||||
@@ -4,21 +4,25 @@ module;
|
|||||||
module mean_field;
|
module mean_field;
|
||||||
|
|
||||||
namespace mean_field::utils {
|
namespace mean_field::utils {
|
||||||
DOMAINS operator|(DOMAINS lhs, DOMAINS rhs) {
|
DOMAINS operator|(
|
||||||
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) |
|
DOMAINS lhs,
|
||||||
static_cast<uint8_t>(rhs));
|
DOMAINS rhs
|
||||||
}
|
) {
|
||||||
|
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
DOMAINS operator&(DOMAINS lhs, DOMAINS rhs) {
|
DOMAINS operator&(
|
||||||
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) &
|
DOMAINS lhs,
|
||||||
static_cast<uint8_t>(rhs));
|
DOMAINS rhs
|
||||||
}
|
) {
|
||||||
|
return static_cast<DOMAINS>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
int get_mesh_order(const mfem::Mesh &mesh) {
|
int get_mesh_order(const mfem::Mesh &mesh) {
|
||||||
if (mesh.GetNodes() != nullptr) {
|
if (mesh.GetNodes() != nullptr) {
|
||||||
return mesh.GetNodes()->FESpace()->GetMaxElementOrder();
|
return mesh.GetNodes()->FESpace()->GetMaxElementOrder();
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mean_field::utils
|
} // namespace mean_field::utils
|
||||||
|
|||||||
100
libmeanfield/interface/eos/concepts.cppm
Normal file
100
libmeanfield/interface/eos/concepts.cppm
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
export module mean_field:eos.concepts;
|
||||||
|
export import :eos.relations;
|
||||||
|
|
||||||
|
export namespace mean_field::eos {
|
||||||
|
namespace detail {
|
||||||
|
template <typename EquationOfState, typename RelationType> struct ImplementsRelation : std::false_type { };
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename EquationOfState,
|
||||||
|
typename Output,
|
||||||
|
typename... Inputs>
|
||||||
|
struct ImplementsRelation<
|
||||||
|
EquationOfState,
|
||||||
|
Relation<
|
||||||
|
Output,
|
||||||
|
Inputs...>> : std::bool_constant <
|
||||||
|
requires(
|
||||||
|
const std::remove_cvref_t<EquationOfState> &equationOfState,
|
||||||
|
QuantityValue<Inputs>... inputValues
|
||||||
|
) {
|
||||||
|
{equationOfState.evaluate(Relation<Output, Inputs...>{}, inputValues...)}
|
||||||
|
->std::same_as<QuantityValue<Output>>;
|
||||||
|
}>{};
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename Catalog> struct ImplementsRelationCatalog : std::false_type { };
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename... Relations>
|
||||||
|
struct ImplementsRelationCatalog<EquationOfState, RelationCatalog<Relations...>>
|
||||||
|
: std::bool_constant<(ImplementsRelation<EquationOfState, Relations>::value && ...)> { };
|
||||||
|
|
||||||
|
template <typename Candidate, typename = void> struct IsEquationOfStateModel : std::false_type { };
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
struct IsEquationOfStateModel<Candidate, std::void_t<typename std::remove_cvref_t<Candidate>::Relations>>
|
||||||
|
: std::bool_constant<
|
||||||
|
ValidRelationCatalog<typename std::remove_cvref_t<Candidate>::Relations> &&
|
||||||
|
ImplementsRelationCatalog<
|
||||||
|
std::remove_cvref_t<Candidate>,
|
||||||
|
typename std::remove_cvref_t<Candidate>::Relations>::value> { };
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename RelationType, typename InputQuantity>
|
||||||
|
struct ImplementsPartialDerivative : std::false_type { };
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename EquationOfState,
|
||||||
|
typename Output,
|
||||||
|
typename... Inputs,
|
||||||
|
typename InputQuantity>
|
||||||
|
struct ImplementsPartialDerivative<
|
||||||
|
EquationOfState,
|
||||||
|
Relation<
|
||||||
|
Output,
|
||||||
|
Inputs...>,
|
||||||
|
InputQuantity> : std::bool_constant <
|
||||||
|
(std::same_as<
|
||||||
|
InputQuantity,
|
||||||
|
Inputs> ||
|
||||||
|
...) &&
|
||||||
|
requires(
|
||||||
|
const std::remove_cvref_t<EquationOfState> &equationOfState,
|
||||||
|
QuantityValue<Inputs>... inputValues
|
||||||
|
) {
|
||||||
|
{equationOfState
|
||||||
|
.partialDerivative(Relation<Output, Inputs...>{}, WithRespectTo<InputQuantity>{}, inputValues...)}
|
||||||
|
->std::same_as<PartialDerivative<Output, InputQuantity>>;
|
||||||
|
}>{};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept EquationOfStateModel = detail::IsEquationOfStateModel<Candidate>::value;
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename RelationType>
|
||||||
|
concept SupportsRelation =
|
||||||
|
EquationOfStateModel<EquationOfState> && ThermodynamicRelationType<RelationType> &&
|
||||||
|
relationCatalogContains<typename std::remove_cvref_t<EquationOfState>::Relations, RelationType>;
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename RelationType, typename InputQuantity>
|
||||||
|
concept SupportsPartialDerivative =
|
||||||
|
SupportsRelation<EquationOfState, RelationType> && ThermodynamicQuantityType<InputQuantity> &&
|
||||||
|
detail::ImplementsPartialDerivative<EquationOfState, RelationType, InputQuantity>::value;
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept StructureSeedEquationOfState =
|
||||||
|
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, SpecificEnthalpyFromDensity>;
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept BarotropicClosureEquationOfState =
|
||||||
|
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, DensityFromSpecificEnthalpy> &&
|
||||||
|
SupportsPartialDerivative<Candidate, DensityFromSpecificEnthalpy, quantity::SpecificEnthalpy>;
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept PressureForceEquationOfState =
|
||||||
|
EquationOfStateModel<Candidate> && SupportsRelation<Candidate, PressureFromSpecificEnthalpy> &&
|
||||||
|
SupportsPartialDerivative<Candidate, PressureFromSpecificEnthalpy, quantity::SpecificEnthalpy>;
|
||||||
|
} // namespace mean_field::eos
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
export module mean_field:eos.base;
|
|
||||||
|
|
||||||
export namespace mean_field::eos {
|
|
||||||
class EquationOfState {
|
|
||||||
public:
|
|
||||||
virtual ~EquationOfState() = default;
|
|
||||||
[[nodiscard]] virtual double pressure_from_density(double density) const = 0;
|
|
||||||
[[nodiscard]] virtual double pressure_from_enthalpy(double enthalpy) const = 0;
|
|
||||||
[[nodiscard]] virtual double enthalpy_from_density(double density) const = 0;
|
|
||||||
[[nodiscard]] virtual double enthalpy_from_pressure(double pressure) const = 0;
|
|
||||||
[[nodiscard]] virtual double density_from_enthalpy(double enthalpy) const = 0;
|
|
||||||
[[nodiscard]] virtual double density_derivative_from_enthalpy(double enthalpy) const = 0;
|
|
||||||
[[nodiscard]] virtual double pressure_derivative_from_enthalpy(double enthalpy) const = 0;
|
|
||||||
[[nodiscard]] virtual double pressure_derivative_from_density(double density) const = 0;
|
|
||||||
};
|
|
||||||
} // namespace mean_field::eos
|
|
||||||
90
libmeanfield/interface/eos/evaluation.cppm
Normal file
90
libmeanfield/interface/eos/evaluation.cppm
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
export module mean_field:eos.evaluation;
|
||||||
|
export import :eos.concepts;
|
||||||
|
|
||||||
|
export namespace mean_field::eos {
|
||||||
|
enum class EvaluationErrorCode {
|
||||||
|
unsupported_relation,
|
||||||
|
unsupported_derivative,
|
||||||
|
wrong_input_count,
|
||||||
|
wrong_input_quantity,
|
||||||
|
nonfinite_input,
|
||||||
|
outside_domain,
|
||||||
|
nonfinite_result
|
||||||
|
};
|
||||||
|
|
||||||
|
class EvaluationError final : public std::domain_error {
|
||||||
|
public:
|
||||||
|
explicit EvaluationError(
|
||||||
|
const EvaluationErrorCode code,
|
||||||
|
std::string message
|
||||||
|
)
|
||||||
|
: std::domain_error(std::move(message)),
|
||||||
|
m_code(code) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] EvaluationErrorCode code() const noexcept {
|
||||||
|
return m_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
EvaluationErrorCode m_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <
|
||||||
|
ThermodynamicQuantityType OutputQuantity,
|
||||||
|
EquationOfStateModel EquationOfState,
|
||||||
|
QuantityValueType... InputValues>
|
||||||
|
requires SupportsRelation<
|
||||||
|
EquationOfState,
|
||||||
|
Relation<
|
||||||
|
OutputQuantity,
|
||||||
|
QuantityOfT<InputValues>...>>
|
||||||
|
[[nodiscard]] constexpr QuantityValue<OutputQuantity> evaluate(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
const InputValues... inputValues
|
||||||
|
) noexcept(noexcept(equationOfState
|
||||||
|
.evaluate(
|
||||||
|
Relation<
|
||||||
|
OutputQuantity,
|
||||||
|
QuantityOfT<InputValues>...>{},
|
||||||
|
inputValues...
|
||||||
|
))) {
|
||||||
|
return equationOfState.evaluate(Relation<OutputQuantity, QuantityOfT<InputValues>...>{}, inputValues...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
ThermodynamicQuantityType OutputQuantity,
|
||||||
|
ThermodynamicQuantityType InputQuantity,
|
||||||
|
EquationOfStateModel EquationOfState,
|
||||||
|
QuantityValueType... InputValues>
|
||||||
|
requires SupportsPartialDerivative<
|
||||||
|
EquationOfState,
|
||||||
|
Relation<
|
||||||
|
OutputQuantity,
|
||||||
|
QuantityOfT<InputValues>...>,
|
||||||
|
InputQuantity>
|
||||||
|
[[nodiscard]] constexpr PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity>
|
||||||
|
partialDerivative(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
const InputValues... inputValues
|
||||||
|
) noexcept(noexcept(equationOfState
|
||||||
|
.partialDerivative(
|
||||||
|
Relation<
|
||||||
|
OutputQuantity,
|
||||||
|
QuantityOfT<InputValues>...>{},
|
||||||
|
WithRespectTo<InputQuantity>{},
|
||||||
|
inputValues...
|
||||||
|
))) {
|
||||||
|
return equationOfState.partialDerivative(
|
||||||
|
Relation<OutputQuantity, QuantityOfT<InputValues>...>{}, WithRespectTo<InputQuantity>{}, inputValues...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} // namespace mean_field::eos
|
||||||
@@ -3,11 +3,18 @@ module;
|
|||||||
#include <format>
|
#include <format>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
export module mean_field:eos.polytrope;
|
export module mean_field:eos.polytrope;
|
||||||
export import :eos.base;
|
export import :eos.evaluation;
|
||||||
|
|
||||||
export namespace mean_field::eos {
|
export namespace mean_field::eos {
|
||||||
class Polytrope final : public EquationOfState {
|
class Polytrope final {
|
||||||
public:
|
public:
|
||||||
|
using Relations = RelationCatalog<
|
||||||
|
PressureFromDensity,
|
||||||
|
PressureFromSpecificEnthalpy,
|
||||||
|
SpecificEnthalpyFromDensity,
|
||||||
|
SpecificEnthalpyFromPressure,
|
||||||
|
DensityFromSpecificEnthalpy>;
|
||||||
|
|
||||||
Polytrope(
|
Polytrope(
|
||||||
const double polytropic_index,
|
const double polytropic_index,
|
||||||
const double polytropic_constant
|
const double polytropic_constant
|
||||||
@@ -49,82 +56,128 @@ export namespace mean_field::eos {
|
|||||||
return m_enthalpy_scale;
|
return m_enthalpy_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double pressure_from_density(const double density) const override {
|
[[nodiscard]] PressureValue evaluate(
|
||||||
validate_nonnegativity(density, "density");
|
PressureFromDensity,
|
||||||
if (density == 0.0) {
|
const DensityValue density
|
||||||
return 0.0;
|
) const {
|
||||||
|
validate_nonnegativity(density.value(), "density");
|
||||||
|
if (density.value() == 0.0) {
|
||||||
|
return PressureValue{0.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_polytropic_constant * std::pow(density, 1.0 + 1.0 / m_polytropic_index);
|
return PressureValue{m_polytropic_constant * std::pow(density.value(), 1.0 + 1.0 / m_polytropic_index)};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double enthalpy_from_density(const double density) const override {
|
[[nodiscard]] SpecificEnthalpyValue evaluate(
|
||||||
validate_nonnegativity(density, "density");
|
SpecificEnthalpyFromDensity,
|
||||||
if (density == 0.0) {
|
const DensityValue density
|
||||||
return 0.0;
|
) const {
|
||||||
|
validate_nonnegativity(density.value(), "density");
|
||||||
|
if (density.value() == 0.0) {
|
||||||
|
return SpecificEnthalpyValue{0.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_enthalpy_scale * std::pow(density, 1.0 / m_polytropic_index);
|
return SpecificEnthalpyValue{m_enthalpy_scale * std::pow(density.value(), 1.0 / m_polytropic_index)};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double density_from_enthalpy(const double enthalpy) const override {
|
[[nodiscard]] DensityValue evaluate(
|
||||||
validate_finite(enthalpy, "enthalpy");
|
DensityFromSpecificEnthalpy,
|
||||||
|
const SpecificEnthalpyValue specificEnthalpy
|
||||||
|
) const {
|
||||||
|
validate_finite(specificEnthalpy.value(), "specific enthalpy");
|
||||||
|
|
||||||
if (enthalpy <= 0.0) {
|
if (specificEnthalpy.value() <= 0.0) {
|
||||||
return 0.0;
|
return DensityValue{0.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index);
|
return DensityValue{std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index)};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double pressure_from_enthalpy(const double enthalpy) const override {
|
[[nodiscard]] PressureValue evaluate(
|
||||||
validate_finite(enthalpy, "enthalpy");
|
PressureFromSpecificEnthalpy,
|
||||||
|
const SpecificEnthalpyValue specificEnthalpy
|
||||||
|
) const {
|
||||||
|
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||||
|
|
||||||
if (enthalpy <= 0.0) {
|
if (specificEnthalpy.value() <= 0.0) {
|
||||||
return 0.0;
|
return PressureValue{0.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
return density_from_enthalpy(enthalpy) * enthalpy / (m_polytropic_index + 1.0);
|
return PressureValue{density.value() * specificEnthalpy.value() / (m_polytropic_index + 1.0)};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double density_derivative_from_enthalpy(const double enthalpy) const override {
|
[[nodiscard]] SpecificEnthalpyValue evaluate(
|
||||||
validate_finite(enthalpy, "enthalpy");
|
SpecificEnthalpyFromPressure,
|
||||||
if (enthalpy < 0.0) {
|
const PressureValue pressure
|
||||||
return 0.0;
|
) const {
|
||||||
|
validate_nonnegativity(pressure.value(), "pressure");
|
||||||
|
if (pressure.value() == 0.0) {
|
||||||
|
return SpecificEnthalpyValue{0.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enthalpy == 0.0) {
|
const double indexPlusOne = m_polytropic_index + 1.0;
|
||||||
return m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0;
|
|
||||||
|
return SpecificEnthalpyValue{
|
||||||
|
indexPlusOne * std::pow(m_polytropic_constant, m_polytropic_index / indexPlusOne) *
|
||||||
|
std::pow(pressure.value(), 1.0 / indexPlusOne)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_polytropic_index / m_enthalpy_scale *
|
[[nodiscard]] PartialDerivative<
|
||||||
std::pow(enthalpy / m_enthalpy_scale, m_polytropic_index - 1.0);
|
quantity::Density,
|
||||||
|
quantity::SpecificEnthalpy>
|
||||||
|
partialDerivative(
|
||||||
|
DensityFromSpecificEnthalpy,
|
||||||
|
WithRespectTo<quantity::SpecificEnthalpy>,
|
||||||
|
const SpecificEnthalpyValue specificEnthalpy
|
||||||
|
) const {
|
||||||
|
validate_finite(specificEnthalpy.value(), "specific enthalpy");
|
||||||
|
if (specificEnthalpy.value() < 0.0) {
|
||||||
|
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{0.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double pressure_derivative_from_enthalpy(const double enthalpy) const override {
|
if (specificEnthalpy.value() == 0.0) {
|
||||||
validate_finite(enthalpy, "enthalpy");
|
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{
|
||||||
|
m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0
|
||||||
if (enthalpy <= 0.0) {
|
};
|
||||||
return 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return density_from_enthalpy(enthalpy);
|
return PartialDerivative<quantity::Density, quantity::SpecificEnthalpy>{
|
||||||
|
m_polytropic_index / m_enthalpy_scale *
|
||||||
|
std::pow(specificEnthalpy.value() / m_enthalpy_scale, m_polytropic_index - 1.0)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double pressure_derivative_from_density(const double density) const override {
|
[[nodiscard]] PartialDerivative<
|
||||||
validate_nonnegativity(density, "density");
|
quantity::Pressure,
|
||||||
if (density == 0.0) {
|
quantity::SpecificEnthalpy>
|
||||||
return 0.0;
|
partialDerivative(
|
||||||
|
PressureFromSpecificEnthalpy,
|
||||||
|
WithRespectTo<quantity::SpecificEnthalpy>,
|
||||||
|
const SpecificEnthalpyValue specificEnthalpy
|
||||||
|
) const {
|
||||||
|
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specificEnthalpy);
|
||||||
|
|
||||||
|
return PartialDerivative<quantity::Pressure, quantity::SpecificEnthalpy>{density.value()};
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
|
[[nodiscard]] PartialDerivative<
|
||||||
std::pow(density, 1.0 / m_polytropic_index);
|
quantity::Pressure,
|
||||||
|
quantity::Density>
|
||||||
|
partialDerivative(
|
||||||
|
PressureFromDensity,
|
||||||
|
WithRespectTo<quantity::Density>,
|
||||||
|
const DensityValue density
|
||||||
|
) const {
|
||||||
|
validate_nonnegativity(density.value(), "density");
|
||||||
|
if (density.value() == 0.0) {
|
||||||
|
return PartialDerivative<quantity::Pressure, quantity::Density>{0.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double enthalpy_from_pressure(const double pressure) const override {
|
return PartialDerivative<quantity::Pressure, quantity::Density>{
|
||||||
validate_nonnegativity(pressure, "pressure");
|
m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) *
|
||||||
const double np1 = m_polytropic_index + 1;
|
std::pow(density.value(), 1.0 / m_polytropic_index)
|
||||||
return np1 * std::pow(m_polytropic_constant, m_polytropic_index / np1) * std::pow(pressure, 1.0 / np1);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -133,8 +186,8 @@ export namespace mean_field::eos {
|
|||||||
const char *quantity
|
const char *quantity
|
||||||
) {
|
) {
|
||||||
if (!std::isfinite(value)) {
|
if (!std::isfinite(value)) {
|
||||||
throw std::domain_error(
|
throw EvaluationError(
|
||||||
std::format(
|
EvaluationErrorCode::nonfinite_input, std::format(
|
||||||
"The {} must be finite. Instead a value of {} has been "
|
"The {} must be finite. Instead a value of {} has been "
|
||||||
"provided",
|
"provided",
|
||||||
quantity, value
|
quantity, value
|
||||||
@@ -149,8 +202,8 @@ export namespace mean_field::eos {
|
|||||||
) {
|
) {
|
||||||
validate_finite(value, quantity);
|
validate_finite(value, quantity);
|
||||||
if (value < 0.0) {
|
if (value < 0.0) {
|
||||||
throw std::domain_error(
|
throw EvaluationError(
|
||||||
std::format(
|
EvaluationErrorCode::outside_domain, std::format(
|
||||||
"The {} must be non-negative. Instead a value of {} "
|
"The {} must be non-negative. Instead a value of {} "
|
||||||
"has been "
|
"has been "
|
||||||
"provided",
|
"provided",
|
||||||
|
|||||||
128
libmeanfield/interface/eos/pressure_surface.cppm
Normal file
128
libmeanfield/interface/eos/pressure_surface.cppm
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
export module mean_field:eos.pressure_surface;
|
||||||
|
|
||||||
|
export import :eos.evaluation;
|
||||||
|
|
||||||
|
export namespace mean_field::eos {
|
||||||
|
namespace detail {
|
||||||
|
template <
|
||||||
|
ThermodynamicQuantityType InputQuantity,
|
||||||
|
typename SurfaceState>
|
||||||
|
[[nodiscard]] constexpr auto pressureSurfaceRelationInput(
|
||||||
|
const PressureValue targetPressure,
|
||||||
|
const SurfaceState &state
|
||||||
|
) {
|
||||||
|
if constexpr (std::same_as<InputQuantity, quantity::Pressure>) {
|
||||||
|
return targetPressure;
|
||||||
|
} else {
|
||||||
|
return state.value(InputQuantity{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename RelationType> struct PressureSurfaceRelationOperations;
|
||||||
|
|
||||||
|
template <typename CarrierQuantity, typename... InputQuantities>
|
||||||
|
struct PressureSurfaceRelationOperations<Relation<CarrierQuantity, InputQuantities...>> {
|
||||||
|
template <
|
||||||
|
typename EquationOfState,
|
||||||
|
typename SurfaceState>
|
||||||
|
[[nodiscard]] static QuantityValue<CarrierQuantity> requiredCarrierValue(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
const PressureValue targetPressure,
|
||||||
|
const SurfaceState &state
|
||||||
|
) {
|
||||||
|
return evaluate<CarrierQuantity>(
|
||||||
|
equationOfState, pressureSurfaceRelationInput<InputQuantities>(targetPressure, state)...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename InputQuantity,
|
||||||
|
typename EquationOfState,
|
||||||
|
typename SurfaceState,
|
||||||
|
typename SurfaceVariation>
|
||||||
|
[[nodiscard]] static double inputJacobianContribution(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
const PressureValue targetPressure,
|
||||||
|
const SurfaceState &state,
|
||||||
|
const SurfaceVariation &variation
|
||||||
|
) {
|
||||||
|
if constexpr (std::same_as<InputQuantity, quantity::Pressure>) {
|
||||||
|
return 0.0;
|
||||||
|
} else {
|
||||||
|
const auto derivative = partialDerivative<CarrierQuantity, InputQuantity>(
|
||||||
|
equationOfState, pressureSurfaceRelationInput<InputQuantities>(targetPressure, state)...
|
||||||
|
);
|
||||||
|
return derivative.value() * variation.value(InputQuantity{}).value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename EquationOfState,
|
||||||
|
typename SurfaceState,
|
||||||
|
typename SurfaceVariation>
|
||||||
|
[[nodiscard]] static double carrierCorrectionJacobianAction(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
const PressureValue targetPressure,
|
||||||
|
const SurfaceState &state,
|
||||||
|
const SurfaceVariation &variation
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
0.0 + ... +
|
||||||
|
inputJacobianContribution<InputQuantities>(equationOfState, targetPressure, state, variation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EOS-owned resolution of a constant-pressure condition into the carrier
|
||||||
|
* quantity used by an equation formulation. No field or solver concepts
|
||||||
|
* enter this type.
|
||||||
|
*/
|
||||||
|
template <EquationOfStateModel EquationOfState, ThermodynamicRelationType SelectedRelation>
|
||||||
|
class ResolvedPressureSurfaceRelation final {
|
||||||
|
public:
|
||||||
|
using RelationType = SelectedRelation;
|
||||||
|
using CarrierQuantity = RelationOutputT<RelationType>;
|
||||||
|
|
||||||
|
ResolvedPressureSurfaceRelation(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
const PressureValue targetPressure
|
||||||
|
) noexcept
|
||||||
|
: m_equationOfState(std::addressof(equationOfState)),
|
||||||
|
m_targetPressure(targetPressure) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] PressureValue targetPressure() const noexcept {
|
||||||
|
return m_targetPressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SurfaceState>
|
||||||
|
[[nodiscard]] QuantityValue<CarrierQuantity> requiredCarrierValue(const SurfaceState &state) const {
|
||||||
|
return detail::PressureSurfaceRelationOperations<RelationType>::requiredCarrierValue(
|
||||||
|
*m_equationOfState, m_targetPressure, state
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename SurfaceState,
|
||||||
|
typename SurfaceVariation>
|
||||||
|
[[nodiscard]] double carrierCorrectionJacobianAction(
|
||||||
|
const SurfaceState &state,
|
||||||
|
const SurfaceVariation &variation
|
||||||
|
) const {
|
||||||
|
return detail::PressureSurfaceRelationOperations<RelationType>::carrierCorrectionJacobianAction(
|
||||||
|
*m_equationOfState, m_targetPressure, state, variation
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const EquationOfState *m_equationOfState;
|
||||||
|
PressureValue m_targetPressure;
|
||||||
|
};
|
||||||
|
} // namespace mean_field::eos
|
||||||
235
libmeanfield/interface/eos/quantities.cppm
Normal file
235
libmeanfield/interface/eos/quantities.cppm
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <compare>
|
||||||
|
#include <concepts>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
export module mean_field:eos.quantities;
|
||||||
|
|
||||||
|
export namespace mean_field::eos {
|
||||||
|
struct ThermodynamicQuantity { };
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept ThermodynamicQuantityType =
|
||||||
|
std::same_as<Candidate, std::remove_cv_t<Candidate>> && std::derived_from<Candidate, ThermodynamicQuantity>;
|
||||||
|
|
||||||
|
namespace quantity {
|
||||||
|
struct Density final : ThermodynamicQuantity {
|
||||||
|
static constexpr std::string_view identifier = "density";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Pressure final : ThermodynamicQuantity {
|
||||||
|
static constexpr std::string_view identifier = "pressure";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpecificEnthalpy final : ThermodynamicQuantity {
|
||||||
|
static constexpr std::string_view identifier = "specific_enthalpy";
|
||||||
|
};
|
||||||
|
} // namespace quantity
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept Numeric = std::integral<T> || std::floating_point<T>;
|
||||||
|
|
||||||
|
template <ThermodynamicQuantityType Quantity> class QuantityValue final {
|
||||||
|
public:
|
||||||
|
explicit constexpr QuantityValue(const double value) noexcept : m_value(value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr double value() const noexcept {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator==(
|
||||||
|
const QuantityValue &,
|
||||||
|
const QuantityValue &
|
||||||
|
) noexcept = default;
|
||||||
|
|
||||||
|
friend constexpr QuantityValue<Quantity> operator+(
|
||||||
|
const QuantityValue<Quantity> &lhs,
|
||||||
|
const QuantityValue<Quantity> &rhs
|
||||||
|
) noexcept {
|
||||||
|
return QuantityValue<Quantity>{lhs.m_value + rhs.m_value};
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr QuantityValue<Quantity> operator-(
|
||||||
|
const QuantityValue<Quantity> &lhs,
|
||||||
|
const QuantityValue<Quantity> &rhs
|
||||||
|
) noexcept {
|
||||||
|
return QuantityValue<Quantity>{lhs.m_value - rhs.m_value};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Numeric rhsT>
|
||||||
|
friend constexpr QuantityValue<Quantity> operator*(
|
||||||
|
const QuantityValue<Quantity> &lhs,
|
||||||
|
rhsT rhs
|
||||||
|
) noexcept {
|
||||||
|
return QuantityValue<Quantity>{lhs.m_value * static_cast<double>(rhs)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Numeric lhsT>
|
||||||
|
friend constexpr QuantityValue<Quantity> operator*(
|
||||||
|
lhsT lhs,
|
||||||
|
const QuantityValue<Quantity> &rhs
|
||||||
|
) noexcept {
|
||||||
|
return QuantityValue<Quantity>{static_cast<double>(lhs) * rhs.m_value};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Numeric rhsT>
|
||||||
|
friend constexpr QuantityValue<Quantity> operator/(
|
||||||
|
const QuantityValue<Quantity> &lhs,
|
||||||
|
rhsT rhs
|
||||||
|
) noexcept {
|
||||||
|
return QuantityValue<Quantity>{lhs.m_value / static_cast<double>(rhs)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Numeric compT>
|
||||||
|
friend constexpr std::partial_ordering operator<=>(
|
||||||
|
const QuantityValue<Quantity> &lhs,
|
||||||
|
compT rhs
|
||||||
|
) noexcept {
|
||||||
|
return lhs.m_value <=> static_cast<double>(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Numeric compT>
|
||||||
|
friend constexpr std::partial_ordering operator<=>(
|
||||||
|
compT lhs,
|
||||||
|
const QuantityValue<Quantity> &rhs
|
||||||
|
) noexcept {
|
||||||
|
return static_cast<double>(lhs) <=> rhs.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr std::partial_ordering operator<=>(
|
||||||
|
const QuantityValue<Quantity> &lhs,
|
||||||
|
const QuantityValue<Quantity> &rhs
|
||||||
|
) noexcept {
|
||||||
|
return lhs.m_value <=> rhs.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
double m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using DensityValue = QuantityValue<quantity::Density>;
|
||||||
|
using PressureValue = QuantityValue<quantity::Pressure>;
|
||||||
|
using SpecificEnthalpyValue = QuantityValue<quantity::SpecificEnthalpy>;
|
||||||
|
|
||||||
|
template <typename Candidate> struct IsQuantityValue : std::false_type { };
|
||||||
|
|
||||||
|
template <ThermodynamicQuantityType Quantity> struct IsQuantityValue<QuantityValue<Quantity>> : std::true_type { };
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept QuantityValueType = IsQuantityValue<std::remove_cvref_t<Candidate>>::value;
|
||||||
|
|
||||||
|
template <typename Candidate> struct QuantityOf;
|
||||||
|
|
||||||
|
template <ThermodynamicQuantityType Quantity> struct QuantityOf<QuantityValue<Quantity>> {
|
||||||
|
using Type = Quantity;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <QuantityValueType Value> using QuantityOfT = typename QuantityOf<std::remove_cvref_t<Value>>::Type;
|
||||||
|
|
||||||
|
template <ThermodynamicQuantityType OutputQuantity, ThermodynamicQuantityType InputQuantity>
|
||||||
|
class PartialDerivative final {
|
||||||
|
public:
|
||||||
|
explicit constexpr PartialDerivative(const double value) noexcept : m_value(value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr double value() const noexcept {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity>
|
||||||
|
operator+(
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &lhs,
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &rhs
|
||||||
|
) noexcept;
|
||||||
|
|
||||||
|
friend constexpr PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity>
|
||||||
|
operator-(
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &lhs,
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &rhs
|
||||||
|
) noexcept;
|
||||||
|
|
||||||
|
template <Numeric rhsT>
|
||||||
|
friend constexpr PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity>
|
||||||
|
operator*(
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &,
|
||||||
|
rhsT
|
||||||
|
) noexcept;
|
||||||
|
|
||||||
|
template <Numeric lhsT>
|
||||||
|
friend constexpr PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity>
|
||||||
|
operator*(
|
||||||
|
lhsT,
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &
|
||||||
|
) noexcept;
|
||||||
|
|
||||||
|
template <Numeric rhsT>
|
||||||
|
friend constexpr PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity>
|
||||||
|
operator/(
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &,
|
||||||
|
rhsT
|
||||||
|
) noexcept;
|
||||||
|
|
||||||
|
template <Numeric cmpT>
|
||||||
|
friend constexpr std::partial_ordering operator<=>(
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &lhs,
|
||||||
|
cmpT rhs
|
||||||
|
) noexcept {
|
||||||
|
return lhs.m_value <=> static_cast<double>(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Numeric cmpT>
|
||||||
|
friend constexpr std::partial_ordering operator<=>(
|
||||||
|
cmpT lhs,
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &rhs
|
||||||
|
) noexcept {
|
||||||
|
return static_cast<double>(lhs) <=> rhs.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr std::partial_ordering operator<=>(
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &lhs,
|
||||||
|
const PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity> &rhs
|
||||||
|
) noexcept {
|
||||||
|
return lhs.m_value <=> rhs.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
double m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <ThermodynamicQuantityType Quantity> struct WithRespectTo final { };
|
||||||
|
} // namespace mean_field::eos
|
||||||
93
libmeanfield/interface/eos/relations.cppm
Normal file
93
libmeanfield/interface/eos/relations.cppm
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
export module mean_field:eos.relations;
|
||||||
|
export import :eos.quantities;
|
||||||
|
|
||||||
|
export namespace mean_field::eos {
|
||||||
|
template <typename... Quantities> struct QuantityList final { };
|
||||||
|
|
||||||
|
template <typename Output, typename... Inputs> struct Relation final {
|
||||||
|
using OutputQuantity = Output;
|
||||||
|
using InputQuantities = QuantityList<Inputs...>;
|
||||||
|
|
||||||
|
static constexpr std::size_t inputCount = sizeof...(Inputs);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Relations> struct RelationCatalog final {
|
||||||
|
static constexpr std::size_t size = sizeof...(Relations);
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename... Types> struct TypesAreUnique;
|
||||||
|
|
||||||
|
template <typename Candidate> struct IsThermodynamicRelation : std::false_type { };
|
||||||
|
|
||||||
|
template <typename Output, typename... Inputs>
|
||||||
|
struct IsThermodynamicRelation<Relation<Output, Inputs...>>
|
||||||
|
: std::bool_constant<
|
||||||
|
ThermodynamicQuantityType<Output> && (ThermodynamicQuantityType<Inputs> && ...) &&
|
||||||
|
TypesAreUnique<Inputs...>::value> { };
|
||||||
|
|
||||||
|
template <typename... Types> struct TypesAreUnique : std::true_type { };
|
||||||
|
|
||||||
|
template <typename First, typename... Remaining>
|
||||||
|
struct TypesAreUnique<First, Remaining...>
|
||||||
|
: std::bool_constant<(!std::same_as<First, Remaining> && ...) && TypesAreUnique<Remaining...>::value> { };
|
||||||
|
|
||||||
|
template <typename Candidate> struct IsValidRelationCatalog : std::false_type { };
|
||||||
|
|
||||||
|
template <typename... Relations>
|
||||||
|
struct IsValidRelationCatalog<RelationCatalog<Relations...>>
|
||||||
|
: std::bool_constant<
|
||||||
|
(sizeof...(Relations) > 0) && (IsThermodynamicRelation<Relations>::value && ...) &&
|
||||||
|
TypesAreUnique<Relations...>::value> { };
|
||||||
|
|
||||||
|
template <typename Catalog, typename RelationType> struct CatalogContainsRelation : std::false_type { };
|
||||||
|
|
||||||
|
template <typename... Relations, typename RelationType>
|
||||||
|
struct CatalogContainsRelation<RelationCatalog<Relations...>, RelationType>
|
||||||
|
: std::bool_constant<(std::same_as<RelationType, Relations> || ...)> { };
|
||||||
|
|
||||||
|
template <typename RelationType, typename Quantity> struct RelationContainsInput : std::false_type { };
|
||||||
|
|
||||||
|
template <typename Output, typename... Inputs, typename Quantity>
|
||||||
|
struct RelationContainsInput<Relation<Output, Inputs...>, Quantity>
|
||||||
|
: std::bool_constant<(std::same_as<Quantity, Inputs> || ...)> { };
|
||||||
|
|
||||||
|
template <std::size_t Index, typename Quantities> struct QuantityAt;
|
||||||
|
|
||||||
|
template <std::size_t Index, typename... Quantities> struct QuantityAt<Index, QuantityList<Quantities...>> {
|
||||||
|
using Type = std::tuple_element_t<Index, std::tuple<Quantities...>>;
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept ThermodynamicRelationType = detail::IsThermodynamicRelation<std::remove_cv_t<Candidate>>::value;
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept ValidRelationCatalog = detail::IsValidRelationCatalog<std::remove_cv_t<Candidate>>::value;
|
||||||
|
|
||||||
|
template <typename Catalog, typename RelationType>
|
||||||
|
inline constexpr bool relationCatalogContains =
|
||||||
|
detail::CatalogContainsRelation<std::remove_cv_t<Catalog>, std::remove_cv_t<RelationType>>::value;
|
||||||
|
|
||||||
|
template <typename RelationType, typename Quantity>
|
||||||
|
inline constexpr bool relationContainsInput =
|
||||||
|
detail::RelationContainsInput<std::remove_cv_t<RelationType>, std::remove_cv_t<Quantity>>::value;
|
||||||
|
|
||||||
|
template <ThermodynamicRelationType RelationType> using RelationOutputT = typename RelationType::OutputQuantity;
|
||||||
|
|
||||||
|
template <std::size_t Index, ThermodynamicRelationType RelationType>
|
||||||
|
using RelationInputT = typename detail::QuantityAt<Index, typename RelationType::InputQuantities>::Type;
|
||||||
|
|
||||||
|
using PressureFromDensity = Relation<quantity::Pressure, quantity::Density>;
|
||||||
|
using PressureFromSpecificEnthalpy = Relation<quantity::Pressure, quantity::SpecificEnthalpy>;
|
||||||
|
using SpecificEnthalpyFromDensity = Relation<quantity::SpecificEnthalpy, quantity::Density>;
|
||||||
|
using SpecificEnthalpyFromPressure = Relation<quantity::SpecificEnthalpy, quantity::Pressure>;
|
||||||
|
using DensityFromSpecificEnthalpy = Relation<quantity::Density, quantity::SpecificEnthalpy>;
|
||||||
|
} // namespace mean_field::eos
|
||||||
645
libmeanfield/interface/eos/runtime.cppm
Normal file
645
libmeanfield/interface/eos/runtime.cppm
Normal file
@@ -0,0 +1,645 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <concepts>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <expected>
|
||||||
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
export module mean_field:eos.runtime;
|
||||||
|
export import :eos.evaluation;
|
||||||
|
|
||||||
|
export namespace mean_field::eos {
|
||||||
|
class ThermodynamicQuantityId final {
|
||||||
|
public:
|
||||||
|
explicit constexpr ThermodynamicQuantityId(const std::string_view name) noexcept : m_name(name) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::string_view name() const noexcept {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator==(
|
||||||
|
const ThermodynamicQuantityId &,
|
||||||
|
const ThermodynamicQuantityId &
|
||||||
|
) noexcept = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string_view m_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Quantity>
|
||||||
|
concept RuntimeIdentifiedThermodynamicQuantity = ThermodynamicQuantityType<Quantity> && requires {
|
||||||
|
{ Quantity::identifier } -> std::convertible_to<std::string_view>;
|
||||||
|
} && (std::string_view{Quantity::identifier}.size() > 0);
|
||||||
|
|
||||||
|
template <RuntimeIdentifiedThermodynamicQuantity Quantity>
|
||||||
|
inline constexpr ThermodynamicQuantityId thermodynamicQuantityId{std::string_view{Quantity::identifier}};
|
||||||
|
|
||||||
|
struct RuntimeQuantityValue final {
|
||||||
|
ThermodynamicQuantityId quantity;
|
||||||
|
double value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RuntimeRelationDescriptor final {
|
||||||
|
ThermodynamicQuantityId outputQuantity;
|
||||||
|
std::span<const ThermodynamicQuantityId> inputQuantities;
|
||||||
|
std::uint64_t partialDerivativeMask;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool hasPartialDerivative(const std::size_t inputIndex) const noexcept {
|
||||||
|
return inputIndex < inputQuantities.size() &&
|
||||||
|
(partialDerivativeMask & (std::uint64_t{1} << inputIndex)) != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename RelationType> struct HasRuntimeQuantityIdentifiers : std::false_type { };
|
||||||
|
|
||||||
|
template <typename Output, typename... Inputs>
|
||||||
|
struct HasRuntimeQuantityIdentifiers<Relation<Output, Inputs...>>
|
||||||
|
: std::bool_constant<
|
||||||
|
RuntimeIdentifiedThermodynamicQuantity<Output> &&
|
||||||
|
(RuntimeIdentifiedThermodynamicQuantity<Inputs> && ...)> { };
|
||||||
|
|
||||||
|
template <typename RelationType> struct RuntimeRelationQuantities;
|
||||||
|
|
||||||
|
template <typename Output, typename... Inputs> struct RuntimeRelationQuantities<Relation<Output, Inputs...>> {
|
||||||
|
using Type = std::tuple<Output, Inputs...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Relations>
|
||||||
|
using RuntimeCatalogQuantityTuple =
|
||||||
|
decltype(std::tuple_cat(std::declval<typename RuntimeRelationQuantities<Relations>::Type>()...));
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename FirstQuantity,
|
||||||
|
typename SecondQuantity>
|
||||||
|
[[nodiscard]] consteval bool runtimeQuantityIdentifiersAreCompatible() {
|
||||||
|
if constexpr (std::same_as<FirstQuantity, SecondQuantity>) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return thermodynamicQuantityId<FirstQuantity> != thermodynamicQuantityId<SecondQuantity>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename QuantityTuple,
|
||||||
|
std::size_t First,
|
||||||
|
std::size_t... Offsets>
|
||||||
|
[[nodiscard]] consteval bool runtimeQuantityIdentifierIsUnambiguous(std::index_sequence<Offsets...>) {
|
||||||
|
return (
|
||||||
|
runtimeQuantityIdentifiersAreCompatible<
|
||||||
|
std::tuple_element_t<First, QuantityTuple>,
|
||||||
|
std::tuple_element_t<First + 1 + Offsets, QuantityTuple>>() &&
|
||||||
|
...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename QuantityTuple,
|
||||||
|
std::size_t... Indices>
|
||||||
|
[[nodiscard]] consteval bool runtimeQuantityIdentifiersAreUnambiguous(std::index_sequence<Indices...>) {
|
||||||
|
return (
|
||||||
|
runtimeQuantityIdentifierIsUnambiguous<QuantityTuple, Indices>(
|
||||||
|
std::make_index_sequence<std::tuple_size_v<QuantityTuple> - Indices - 1>{}
|
||||||
|
) &&
|
||||||
|
...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool QuantitiesAreIdentified, typename... Relations>
|
||||||
|
struct RuntimeRelationsAreSupported : std::false_type { };
|
||||||
|
|
||||||
|
template <typename... Relations>
|
||||||
|
struct RuntimeRelationsAreSupported<true, Relations...>
|
||||||
|
: std::bool_constant<runtimeQuantityIdentifiersAreUnambiguous<RuntimeCatalogQuantityTuple<Relations...>>(
|
||||||
|
std::make_index_sequence<std::tuple_size_v<RuntimeCatalogQuantityTuple<Relations...>>>{}
|
||||||
|
)> { };
|
||||||
|
|
||||||
|
template <typename Catalog> struct RuntimeCatalogIsSupported : std::false_type { };
|
||||||
|
|
||||||
|
template <typename... Relations>
|
||||||
|
struct RuntimeCatalogIsSupported<RelationCatalog<Relations...>>
|
||||||
|
: RuntimeRelationsAreSupported<(HasRuntimeQuantityIdentifiers<Relations>::value && ...), Relations...> { };
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept RuntimeEquationOfStateModel =
|
||||||
|
EquationOfStateModel<Candidate> &&
|
||||||
|
detail::RuntimeCatalogIsSupported<typename std::remove_cvref_t<Candidate>::Relations>::value;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename EquationOfState, typename RelationType> struct RuntimeRelationStorage;
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename Output, typename... Inputs>
|
||||||
|
struct RuntimeRelationStorage<EquationOfState, Relation<Output, Inputs...>> {
|
||||||
|
using RelationType = Relation<Output, Inputs...>;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
sizeof...(Inputs) <= 64,
|
||||||
|
"Runtime EOS relation descriptors support at most 64 inputs."
|
||||||
|
);
|
||||||
|
|
||||||
|
inline static constexpr std::array<ThermodynamicQuantityId, sizeof...(Inputs)> inputQuantityIds{
|
||||||
|
thermodynamicQuantityId<Inputs>...
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t... Indices>
|
||||||
|
[[nodiscard]] static consteval std::uint64_t makePartialDerivativeMask(std::index_sequence<Indices...>) {
|
||||||
|
using InputTuple = std::tuple<Inputs...>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
std::uint64_t{0} | ... |
|
||||||
|
(SupportsPartialDerivative<EquationOfState, RelationType, std::tuple_element_t<Indices, InputTuple>>
|
||||||
|
? (std::uint64_t{1} << Indices)
|
||||||
|
: std::uint64_t{0})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static constexpr std::uint64_t partialDerivativeMask =
|
||||||
|
makePartialDerivativeMask(std::index_sequence_for<Inputs...>{});
|
||||||
|
|
||||||
|
inline static constexpr RuntimeRelationDescriptor descriptor{
|
||||||
|
thermodynamicQuantityId<Output>, std::span<const ThermodynamicQuantityId>{inputQuantityIds},
|
||||||
|
partialDerivativeMask
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename Catalog> struct RuntimeCatalogStorage;
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename... Relations>
|
||||||
|
struct RuntimeCatalogStorage<EquationOfState, RelationCatalog<Relations...>> {
|
||||||
|
inline static constexpr std::array descriptors{
|
||||||
|
RuntimeRelationStorage<EquationOfState, Relations>::descriptor...
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError>
|
||||||
|
runtimeEvaluationFailure(
|
||||||
|
const EvaluationErrorCode code,
|
||||||
|
std::string message
|
||||||
|
) {
|
||||||
|
return std::unexpected<EvaluationError>{EvaluationError{code, std::move(message)}};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename EquationOfState,
|
||||||
|
typename Output,
|
||||||
|
typename... Inputs>
|
||||||
|
[[nodiscard]] std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError>
|
||||||
|
evaluateRuntimeRelation(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
Relation<
|
||||||
|
Output,
|
||||||
|
Inputs...>,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues
|
||||||
|
) {
|
||||||
|
const auto invoke = [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
|
||||||
|
return eos::evaluate<Output>(equationOfState, QuantityValue<Inputs>{inputValues[Indices].value}...)
|
||||||
|
.value();
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
return invoke(std::index_sequence_for<Inputs...>{});
|
||||||
|
} catch (const EvaluationError &error) {
|
||||||
|
return std::unexpected<EvaluationError>{error};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename InputQuantity,
|
||||||
|
typename EquationOfState,
|
||||||
|
typename Output,
|
||||||
|
typename... Inputs>
|
||||||
|
[[nodiscard]] bool tryRuntimePartialDerivative(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
Relation<
|
||||||
|
Output,
|
||||||
|
Inputs...> relation,
|
||||||
|
const ThermodynamicQuantityId withRespectTo,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues,
|
||||||
|
std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError> &result
|
||||||
|
) {
|
||||||
|
if (withRespectTo != thermodynamicQuantityId<InputQuantity>) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (SupportsPartialDerivative<EquationOfState, Relation<Output, Inputs...>, InputQuantity>) {
|
||||||
|
const auto invoke = [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
|
||||||
|
return eos::partialDerivative<Output, InputQuantity>(
|
||||||
|
equationOfState, QuantityValue<Inputs>{inputValues[Indices].value}...
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = invoke(std::index_sequence_for<Inputs...>{});
|
||||||
|
} catch (const EvaluationError &error) {
|
||||||
|
result = std::unexpected<EvaluationError>{error};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = runtimeEvaluationFailure(
|
||||||
|
EvaluationErrorCode::unsupported_derivative,
|
||||||
|
"The requested EOS partial derivative is not available."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename EquationOfState,
|
||||||
|
typename Output,
|
||||||
|
typename... Inputs>
|
||||||
|
[[nodiscard]] std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError>
|
||||||
|
evaluateRuntimePartialDerivative(
|
||||||
|
const EquationOfState &equationOfState,
|
||||||
|
Relation<
|
||||||
|
Output,
|
||||||
|
Inputs...> relation,
|
||||||
|
const ThermodynamicQuantityId withRespectTo,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues
|
||||||
|
) {
|
||||||
|
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||||
|
EvaluationErrorCode::unsupported_derivative,
|
||||||
|
"The requested quantity is not an input to the EOS relation."
|
||||||
|
);
|
||||||
|
|
||||||
|
const bool matched =
|
||||||
|
(tryRuntimePartialDerivative<Inputs>(equationOfState, relation, withRespectTo, inputValues, result) ||
|
||||||
|
...);
|
||||||
|
|
||||||
|
static_cast<void>(matched);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename EquationOfState,
|
||||||
|
typename RelationType>
|
||||||
|
[[nodiscard]] bool runtimeRelationMatches(
|
||||||
|
const ThermodynamicQuantityId outputQuantity,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues
|
||||||
|
) {
|
||||||
|
const RuntimeRelationDescriptor &descriptor =
|
||||||
|
RuntimeRelationStorage<EquationOfState, RelationType>::descriptor;
|
||||||
|
|
||||||
|
if (descriptor.outputQuantity != outputQuantity ||
|
||||||
|
descriptor.inputQuantities.size() != inputValues.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t index = 0; index < inputValues.size(); ++index) {
|
||||||
|
if (descriptor.inputQuantities[index] != inputValues[index].quantity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename Catalog> struct RuntimeCatalogDispatch;
|
||||||
|
|
||||||
|
template <typename EquationOfState, typename... Relations>
|
||||||
|
struct RuntimeCatalogDispatch<EquationOfState, RelationCatalog<Relations...>> {
|
||||||
|
[[nodiscard]] static std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError>
|
||||||
|
evaluate(
|
||||||
|
const void *object,
|
||||||
|
const ThermodynamicQuantityId outputQuantity,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues
|
||||||
|
) {
|
||||||
|
const auto &equationOfState = *static_cast<const EquationOfState *>(object);
|
||||||
|
|
||||||
|
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||||
|
EvaluationErrorCode::unsupported_relation, "The requested EOS relation is not available."
|
||||||
|
);
|
||||||
|
|
||||||
|
const bool matched =
|
||||||
|
((runtimeRelationMatches<EquationOfState, Relations>(outputQuantity, inputValues)
|
||||||
|
? (result = evaluateRuntimeRelation(equationOfState, Relations{}, inputValues), true)
|
||||||
|
: false) ||
|
||||||
|
...);
|
||||||
|
|
||||||
|
static_cast<void>(matched);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError>
|
||||||
|
partialDerivative(
|
||||||
|
const void *object,
|
||||||
|
const ThermodynamicQuantityId outputQuantity,
|
||||||
|
const ThermodynamicQuantityId withRespectTo,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues
|
||||||
|
) {
|
||||||
|
const auto &equationOfState = *static_cast<const EquationOfState *>(object);
|
||||||
|
|
||||||
|
std::expected<double, EvaluationError> result = runtimeEvaluationFailure(
|
||||||
|
EvaluationErrorCode::unsupported_relation, "The requested EOS relation is not available."
|
||||||
|
);
|
||||||
|
|
||||||
|
const bool matched =
|
||||||
|
((runtimeRelationMatches<EquationOfState, Relations>(outputQuantity, inputValues)
|
||||||
|
? (result = evaluateRuntimePartialDerivative(
|
||||||
|
equationOfState, Relations{}, withRespectTo, inputValues
|
||||||
|
),
|
||||||
|
true)
|
||||||
|
: false) ||
|
||||||
|
...);
|
||||||
|
|
||||||
|
static_cast<void>(matched);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <RuntimeEquationOfStateModel EquationOfState>
|
||||||
|
using RuntimeAdapter = RuntimeCatalogDispatch<EquationOfState, typename EquationOfState::Relations>;
|
||||||
|
|
||||||
|
template <RuntimeEquationOfStateModel EquationOfState>
|
||||||
|
[[nodiscard]] constexpr std::span<const RuntimeRelationDescriptor> runtimeRelationDescriptors() noexcept {
|
||||||
|
return RuntimeCatalogStorage<EquationOfState, typename EquationOfState::Relations>::descriptors;
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
class EquationOfStateView final {
|
||||||
|
public:
|
||||||
|
template <RuntimeEquationOfStateModel EquationOfState>
|
||||||
|
explicit EquationOfStateView(EquationOfState &equationOfState) noexcept
|
||||||
|
: m_object(std::addressof(equationOfState)),
|
||||||
|
m_relations(detail::runtimeRelationDescriptors<std::remove_cv_t<EquationOfState>>()),
|
||||||
|
m_evaluate(&detail::RuntimeAdapter<std::remove_cv_t<EquationOfState>>::evaluate),
|
||||||
|
m_partialDerivative(&detail::RuntimeAdapter<std::remove_cv_t<EquationOfState>>::partialDerivative) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<const RuntimeRelationDescriptor> relations() const noexcept {
|
||||||
|
return m_relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool supports(
|
||||||
|
const ThermodynamicQuantityId outputQuantity,
|
||||||
|
const std::span<const ThermodynamicQuantityId> inputQuantities
|
||||||
|
) const noexcept {
|
||||||
|
return findRelation(outputQuantity, inputQuantities) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||||
|
RuntimeIdentifiedThermodynamicQuantity... InputQuantities>
|
||||||
|
[[nodiscard]] bool supports() const noexcept {
|
||||||
|
constexpr std::array<ThermodynamicQuantityId, sizeof...(InputQuantities)> inputs{
|
||||||
|
thermodynamicQuantityId<InputQuantities>...
|
||||||
|
};
|
||||||
|
|
||||||
|
return supports(thermodynamicQuantityId<OutputQuantity>, std::span<const ThermodynamicQuantityId>{inputs});
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<
|
||||||
|
RuntimeQuantityValue,
|
||||||
|
EvaluationError>
|
||||||
|
tryEvaluate(
|
||||||
|
const ThermodynamicQuantityId outputQuantity,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues
|
||||||
|
) const {
|
||||||
|
const auto validation = validateRelationRequest(outputQuantity, inputValues);
|
||||||
|
|
||||||
|
if (!validation.has_value()) {
|
||||||
|
return std::unexpected<EvaluationError>{validation.error()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = m_evaluate(m_object, outputQuantity, inputValues);
|
||||||
|
if (!result.has_value()) {
|
||||||
|
return std::unexpected<EvaluationError>{result.error()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return RuntimeQuantityValue{outputQuantity, *result};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||||
|
QuantityValueType... InputValues>
|
||||||
|
[[nodiscard]] std::expected<
|
||||||
|
QuantityValue<OutputQuantity>,
|
||||||
|
EvaluationError>
|
||||||
|
tryEvaluate(const InputValues... inputValues) const {
|
||||||
|
constexpr bool inputsHaveRuntimeIdentifiers =
|
||||||
|
(RuntimeIdentifiedThermodynamicQuantity<QuantityOfT<InputValues>> && ...);
|
||||||
|
|
||||||
|
static_assert(inputsHaveRuntimeIdentifiers, "Every runtime EOS input quantity needs a stable identifier.");
|
||||||
|
|
||||||
|
const std::array<RuntimeQuantityValue, sizeof...(InputValues)> runtimeInputs{
|
||||||
|
RuntimeQuantityValue{thermodynamicQuantityId<QuantityOfT<InputValues>>, inputValues.value()}...
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = tryEvaluate(
|
||||||
|
thermodynamicQuantityId<OutputQuantity>, std::span<const RuntimeQuantityValue>{runtimeInputs}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result.has_value()) {
|
||||||
|
return std::unexpected<EvaluationError>{result.error()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return QuantityValue<OutputQuantity>{result->value};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError>
|
||||||
|
tryPartialDerivative(
|
||||||
|
const ThermodynamicQuantityId outputQuantity,
|
||||||
|
const ThermodynamicQuantityId withRespectTo,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues
|
||||||
|
) const {
|
||||||
|
const auto validation = validateRelationRequest(outputQuantity, inputValues);
|
||||||
|
|
||||||
|
if (!validation.has_value()) {
|
||||||
|
return std::unexpected<EvaluationError>{validation.error()};
|
||||||
|
}
|
||||||
|
|
||||||
|
const RuntimeRelationDescriptor &descriptor = **validation;
|
||||||
|
bool derivativeAvailable = false;
|
||||||
|
|
||||||
|
for (std::size_t index = 0; index < descriptor.inputQuantities.size(); ++index) {
|
||||||
|
if (descriptor.inputQuantities[index] == withRespectTo) {
|
||||||
|
derivativeAvailable = descriptor.hasPartialDerivative(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!derivativeAvailable) {
|
||||||
|
return runtimeFailure<double>(
|
||||||
|
EvaluationErrorCode::unsupported_derivative,
|
||||||
|
"The requested EOS partial derivative is not available."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_partialDerivative(m_object, outputQuantity, withRespectTo, inputValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
RuntimeIdentifiedThermodynamicQuantity OutputQuantity,
|
||||||
|
RuntimeIdentifiedThermodynamicQuantity InputQuantity,
|
||||||
|
QuantityValueType... InputValues>
|
||||||
|
[[nodiscard]] std::expected<
|
||||||
|
PartialDerivative<
|
||||||
|
OutputQuantity,
|
||||||
|
InputQuantity>,
|
||||||
|
EvaluationError>
|
||||||
|
tryPartialDerivative(const InputValues... inputValues) const {
|
||||||
|
constexpr bool inputsHaveRuntimeIdentifiers =
|
||||||
|
(RuntimeIdentifiedThermodynamicQuantity<QuantityOfT<InputValues>> && ...);
|
||||||
|
|
||||||
|
static_assert(inputsHaveRuntimeIdentifiers, "Every runtime EOS input quantity needs a stable identifier.");
|
||||||
|
|
||||||
|
const std::array<RuntimeQuantityValue, sizeof...(InputValues)> runtimeInputs{
|
||||||
|
RuntimeQuantityValue{thermodynamicQuantityId<QuantityOfT<InputValues>>, inputValues.value()}...
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = tryPartialDerivative(
|
||||||
|
thermodynamicQuantityId<OutputQuantity>, thermodynamicQuantityId<InputQuantity>,
|
||||||
|
std::span<const RuntimeQuantityValue>{runtimeInputs}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result.has_value()) {
|
||||||
|
return std::unexpected<EvaluationError>{result.error()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return PartialDerivative<OutputQuantity, InputQuantity>{*result};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using RuntimeEvaluateFunction = std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError> (*)(
|
||||||
|
const void *,
|
||||||
|
ThermodynamicQuantityId,
|
||||||
|
std::span<const RuntimeQuantityValue>
|
||||||
|
);
|
||||||
|
|
||||||
|
using RuntimePartialDerivativeFunction = std::expected<
|
||||||
|
double,
|
||||||
|
EvaluationError> (*)(
|
||||||
|
const void *,
|
||||||
|
ThermodynamicQuantityId,
|
||||||
|
ThermodynamicQuantityId,
|
||||||
|
std::span<const RuntimeQuantityValue>
|
||||||
|
);
|
||||||
|
|
||||||
|
[[nodiscard]] const RuntimeRelationDescriptor *findRelation(
|
||||||
|
const ThermodynamicQuantityId outputQuantity,
|
||||||
|
const std::span<const ThermodynamicQuantityId> inputQuantities
|
||||||
|
) const noexcept {
|
||||||
|
for (const RuntimeRelationDescriptor &descriptor : m_relations) {
|
||||||
|
if (descriptor.outputQuantity != outputQuantity ||
|
||||||
|
descriptor.inputQuantities.size() != inputQuantities.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches = true;
|
||||||
|
for (std::size_t index = 0; index < inputQuantities.size(); ++index) {
|
||||||
|
if (descriptor.inputQuantities[index] != inputQuantities[index]) {
|
||||||
|
matches = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
return std::addressof(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<
|
||||||
|
const RuntimeRelationDescriptor *,
|
||||||
|
EvaluationError>
|
||||||
|
validateRelationRequest(
|
||||||
|
const ThermodynamicQuantityId outputQuantity,
|
||||||
|
const std::span<const RuntimeQuantityValue> inputValues
|
||||||
|
) const {
|
||||||
|
bool outputAvailable = false;
|
||||||
|
bool inputCountAvailable = false;
|
||||||
|
|
||||||
|
for (const RuntimeRelationDescriptor &descriptor : m_relations) {
|
||||||
|
if (descriptor.outputQuantity != outputQuantity) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputAvailable = true;
|
||||||
|
|
||||||
|
if (descriptor.inputQuantities.size() != inputValues.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputCountAvailable = true;
|
||||||
|
bool matches = true;
|
||||||
|
|
||||||
|
for (std::size_t index = 0; index < inputValues.size(); ++index) {
|
||||||
|
if (descriptor.inputQuantities[index] != inputValues[index].quantity) {
|
||||||
|
matches = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
return std::addressof(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outputAvailable) {
|
||||||
|
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||||
|
EvaluationErrorCode::unsupported_relation,
|
||||||
|
"The EOS does not provide a relation for output quantity '" + std::string{outputQuantity.name()} +
|
||||||
|
"'."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inputCountAvailable) {
|
||||||
|
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||||
|
EvaluationErrorCode::wrong_input_count, "No EOS relation for output quantity '" +
|
||||||
|
std::string{outputQuantity.name()} +
|
||||||
|
"' accepts the supplied number of inputs."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtimeFailure<const RuntimeRelationDescriptor *>(
|
||||||
|
EvaluationErrorCode::wrong_input_quantity, "No EOS relation for output quantity '" +
|
||||||
|
std::string{outputQuantity.name()} +
|
||||||
|
"' accepts the supplied input quantities."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Value>
|
||||||
|
[[nodiscard]] static std::expected<
|
||||||
|
Value,
|
||||||
|
EvaluationError>
|
||||||
|
runtimeFailure(
|
||||||
|
const EvaluationErrorCode code,
|
||||||
|
std::string message
|
||||||
|
) {
|
||||||
|
return std::unexpected<EvaluationError>{EvaluationError{code, std::move(message)}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *m_object;
|
||||||
|
std::span<const RuntimeRelationDescriptor> m_relations;
|
||||||
|
RuntimeEvaluateFunction m_evaluate;
|
||||||
|
RuntimePartialDerivativeFunction m_partialDerivative;
|
||||||
|
};
|
||||||
|
} // namespace mean_field::eos
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -888,6 +889,282 @@ export namespace mean_field::field {
|
|||||||
mfem::Array<int> m_trueToReduced;
|
mfem::Array<int> m_trueToReduced;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Boundary rows expressed in a field's reduced solver ordering.
|
||||||
|
*
|
||||||
|
* This object is deliberately independent of any particular physical
|
||||||
|
* surface condition. Its template constructor below combines a field,
|
||||||
|
* a semantic boundary, and a domain schema. Consequently the same
|
||||||
|
* topology machinery can be used by any compiled surface formulation;
|
||||||
|
* it is not tied to enthalpy or pressure.
|
||||||
|
*/
|
||||||
|
class FieldBoundaryDofMap final {
|
||||||
|
public:
|
||||||
|
FieldBoundaryDofMap() = default;
|
||||||
|
|
||||||
|
FieldBoundaryDofMap(
|
||||||
|
const int fieldReducedSize,
|
||||||
|
const mfem::Array<int> &boundaryReducedDofs
|
||||||
|
)
|
||||||
|
: m_fieldReducedSize(fieldReducedSize),
|
||||||
|
m_boundaryReducedDofs(boundaryReducedDofs) {
|
||||||
|
if (m_fieldReducedSize < 0) {
|
||||||
|
throw std::invalid_argument("FieldBoundaryDofMap requires a non-negative field size.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_boundaryReducedDofMarker.SetSize(m_fieldReducedSize);
|
||||||
|
m_boundaryReducedDofMarker = 0;
|
||||||
|
|
||||||
|
int previousReducedDof = -1;
|
||||||
|
for (const int reducedDof : m_boundaryReducedDofs) {
|
||||||
|
if (reducedDof < 0 || reducedDof >= m_fieldReducedSize) {
|
||||||
|
throw std::invalid_argument("FieldBoundaryDofMap contains a DOF outside the reduced field vector.");
|
||||||
|
}
|
||||||
|
if (reducedDof <= previousReducedDof) {
|
||||||
|
throw std::invalid_argument("FieldBoundaryDofMap indices must be strictly increasing and unique.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_boundaryReducedDofMarker[reducedDof] = 1;
|
||||||
|
previousReducedDof = reducedDof;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int field_size() const noexcept {
|
||||||
|
return m_fieldReducedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int size() const noexcept {
|
||||||
|
return m_boundaryReducedDofs.Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const noexcept {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const mfem::Array<int> &reduced_dofs() const noexcept {
|
||||||
|
return m_boundaryReducedDofs;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const mfem::Array<int> &reduced_dof_marker() const noexcept {
|
||||||
|
return m_boundaryReducedDofMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool contains(const int reducedDof) const {
|
||||||
|
if (reducedDof < 0 || reducedDof >= m_fieldReducedSize) {
|
||||||
|
throw std::out_of_range("Reduced DOF index is outside FieldBoundaryDofMap.");
|
||||||
|
}
|
||||||
|
return m_boundaryReducedDofMarker[reducedDof] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_fieldReducedSize{0};
|
||||||
|
mfem::Array<int> m_boundaryReducedDofs;
|
||||||
|
mfem::Array<int> m_boundaryReducedDofMarker;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Point-supported rows in a field's reduced solver ordering. */
|
||||||
|
class FieldPointDofMap final {
|
||||||
|
public:
|
||||||
|
FieldPointDofMap() = default;
|
||||||
|
|
||||||
|
FieldPointDofMap(
|
||||||
|
const int fieldReducedSize,
|
||||||
|
const mfem::Array<int> &pointReducedDofs
|
||||||
|
)
|
||||||
|
: m_selectedDofs(
|
||||||
|
fieldReducedSize,
|
||||||
|
pointReducedDofs
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int field_size() const noexcept {
|
||||||
|
return m_selectedDofs.field_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int size() const noexcept {
|
||||||
|
return m_selectedDofs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const noexcept {
|
||||||
|
return m_selectedDofs.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const mfem::Array<int> &reduced_dofs() const noexcept {
|
||||||
|
return m_selectedDofs.reduced_dofs();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const mfem::Array<int> &reduced_dof_marker() const noexcept {
|
||||||
|
return m_selectedDofs.reduced_dof_marker();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool contains(const int reducedDof) const {
|
||||||
|
return m_selectedDofs.contains(reducedDof);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FieldBoundaryDofMap m_selectedDofs;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <
|
||||||
|
MfemDomainField FieldT,
|
||||||
|
utils::domain::IsBoundary BoundaryT,
|
||||||
|
utils::domain::IsSchema SchemaT>
|
||||||
|
[[nodiscard]] FieldBoundaryDofMap make_field_boundary_dof_map(
|
||||||
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
|
const FieldDofMap &fieldDofMap
|
||||||
|
) {
|
||||||
|
static_assert(
|
||||||
|
SchemaT::template contains_boundary<BoundaryT>(),
|
||||||
|
"The requested boundary is not registered in the supplied DomainSchema."
|
||||||
|
);
|
||||||
|
|
||||||
|
MFEM_VERIFY(
|
||||||
|
!finiteElementSpace.Nonconforming(),
|
||||||
|
"Field boundary true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
fieldDofMap.full_size() == finiteElementSpace.GetTrueVSize(),
|
||||||
|
"The field map and finite-element space have incompatible true-DOF sizes."
|
||||||
|
);
|
||||||
|
|
||||||
|
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||||
|
MFEM_VERIFY(mesh != nullptr, "Field boundary DOF resolution requires an MFEM mesh.");
|
||||||
|
|
||||||
|
mfem::Array<int> boundaryVDofMarker(finiteElementSpace.GetVSize());
|
||||||
|
boundaryVDofMarker = 0;
|
||||||
|
|
||||||
|
mfem::Array<int> boundaryElementVDofs;
|
||||||
|
for (int boundaryElement = 0; boundaryElement < mesh->GetNBE(); ++boundaryElement) {
|
||||||
|
if (!SchemaT::template boundary_attribute_matches<BoundaryT>(mesh->GetBdrAttribute(boundaryElement))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
finiteElementSpace.GetBdrElementVDofs(boundaryElement, boundaryElementVDofs);
|
||||||
|
for (const int encodedVDof : boundaryElementVDofs) {
|
||||||
|
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
vdof >= 0 && vdof < finiteElementSpace.GetVSize(), "MFEM returned an invalid boundary vector DOF."
|
||||||
|
);
|
||||||
|
boundaryVDofMarker[vdof] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finiteElementSpace.Synchronize(boundaryVDofMarker);
|
||||||
|
|
||||||
|
mfem::Array<int> boundaryReducedDofMarker(fieldDofMap.reduced_size());
|
||||||
|
boundaryReducedDofMarker = 0;
|
||||||
|
|
||||||
|
for (int vdof = 0; vdof < boundaryVDofMarker.Size(); ++vdof) {
|
||||||
|
if (boundaryVDofMarker[vdof] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||||
|
if (trueDof < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<int> reducedDof = fieldDofMap.reduced_dof(trueDof);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
reducedDof.has_value(),
|
||||||
|
"A boundary DOF selected for the field is absent from that field's reduced solver map."
|
||||||
|
);
|
||||||
|
boundaryReducedDofMarker[*reducedDof] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mfem::Array<int> boundaryReducedDofs;
|
||||||
|
mfem::FiniteElementSpace::MarkerToList(boundaryReducedDofMarker, boundaryReducedDofs);
|
||||||
|
return FieldBoundaryDofMap(fieldDofMap.reduced_size(), boundaryReducedDofs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <MfemDomainField FieldT>
|
||||||
|
[[nodiscard]] FieldPointDofMap make_field_point_dof_map(
|
||||||
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
|
const FieldDofMap &fieldDofMap,
|
||||||
|
const mfem::Vector &point,
|
||||||
|
const double tolerance
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
!finiteElementSpace.Nonconforming(),
|
||||||
|
"Field point true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
fieldDofMap.full_size() == finiteElementSpace.GetTrueVSize(),
|
||||||
|
"The field map and finite-element space have incompatible true-DOF sizes."
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
std::isfinite(tolerance) && tolerance >= 0.0, "The field point tolerance must be finite and non-negative."
|
||||||
|
);
|
||||||
|
|
||||||
|
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||||
|
MFEM_VERIFY(mesh != nullptr, "Field point DOF resolution requires an MFEM mesh.");
|
||||||
|
MFEM_VERIFY(
|
||||||
|
point.Size() == mesh->SpaceDimension(), "The requested field point has the wrong coordinate dimension."
|
||||||
|
);
|
||||||
|
|
||||||
|
mfem::Array<int> pointVDofMarker(finiteElementSpace.GetVSize());
|
||||||
|
pointVDofMarker = 0;
|
||||||
|
|
||||||
|
mfem::Array<int> vertexVDofs;
|
||||||
|
for (int vertex = 0; vertex < mesh->GetNV(); ++vertex) {
|
||||||
|
const mfem::real_t *coordinates = mesh->GetVertex(vertex);
|
||||||
|
double distanceSquared = 0.0;
|
||||||
|
for (int component = 0; component < point.Size(); ++component) {
|
||||||
|
const double difference = coordinates[component] - point(component);
|
||||||
|
distanceSquared += difference * difference;
|
||||||
|
}
|
||||||
|
if (std::sqrt(distanceSquared) > tolerance) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
finiteElementSpace.GetVertexVDofs(vertex, vertexVDofs);
|
||||||
|
for (const int encodedVDof : vertexVDofs) {
|
||||||
|
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
vdof >= 0 && vdof < finiteElementSpace.GetVSize(), "MFEM returned an invalid point vector DOF."
|
||||||
|
);
|
||||||
|
pointVDofMarker[vdof] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finiteElementSpace.Synchronize(pointVDofMarker);
|
||||||
|
|
||||||
|
mfem::Array<int> pointReducedDofMarker(fieldDofMap.reduced_size());
|
||||||
|
pointReducedDofMarker = 0;
|
||||||
|
for (int vdof = 0; vdof < pointVDofMarker.Size(); ++vdof) {
|
||||||
|
if (pointVDofMarker[vdof] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||||
|
if (trueDof < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<int> reducedDof = fieldDofMap.reduced_dof(trueDof);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
reducedDof.has_value(),
|
||||||
|
"A point DOF selected for the field is absent from that field's reduced solver map."
|
||||||
|
);
|
||||||
|
pointReducedDofMarker[*reducedDof] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mfem::Array<int> pointReducedDofs;
|
||||||
|
mfem::FiniteElementSpace::MarkerToList(pointReducedDofMarker, pointReducedDofs);
|
||||||
|
|
||||||
|
const long long localPointDofCount = pointReducedDofs.Size();
|
||||||
|
long long globalPointDofCount = 0;
|
||||||
|
MPI_Allreduce(
|
||||||
|
&localPointDofCount, &globalPointDofCount, 1, MPI_LONG_LONG, MPI_SUM, finiteElementSpace.GetComm()
|
||||||
|
);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
globalPointDofCount == finiteElementSpace.GetVDim(),
|
||||||
|
"The requested geometric point must identify exactly one field vertex globally."
|
||||||
|
);
|
||||||
|
|
||||||
|
return FieldPointDofMap(fieldDofMap.reduced_size(), pointReducedDofs);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Canonical adapter between an MFEM GridFunction and a reduced field
|
* Canonical adapter between an MFEM GridFunction and a reduced field
|
||||||
* vector.
|
* vector.
|
||||||
@@ -1015,9 +1292,6 @@ export namespace mean_field::field {
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
FieldDofGridFunctionAdapter
|
FieldDofGridFunctionAdapter
|
||||||
make_field_dof_grid_function_adapter(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
make_field_dof_grid_function_adapter(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||||
return FieldDofGridFunctionAdapter(
|
return FieldDofGridFunctionAdapter(make_field_dof_map<FieldT, SchemaT>(finiteElementSpace), finiteElementSpace);
|
||||||
make_field_dof_map<FieldT, SchemaT>(finiteElementSpace),
|
|
||||||
finiteElementSpace
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} // namespace mean_field::field
|
} // namespace mean_field::field
|
||||||
|
|||||||
@@ -41,7 +41,11 @@ export namespace mean_field::integrators {
|
|||||||
const mfem::GridFunction &compactification_coordinate,
|
const mfem::GridFunction &compactification_coordinate,
|
||||||
utils::EOS_P<EOS_T> eos
|
utils::EOS_P<EOS_T> eos
|
||||||
)
|
)
|
||||||
: m_mapping(mapper, displacement, compactification_coordinate),
|
: m_mapping(
|
||||||
|
mapper,
|
||||||
|
displacement,
|
||||||
|
compactification_coordinate
|
||||||
|
),
|
||||||
m_eos(std::move(eos)) {
|
m_eos(std::move(eos)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,15 @@ import :mapping.compactification;
|
|||||||
import :utils.user;
|
import :utils.user;
|
||||||
|
|
||||||
export namespace mean_field::mapping {
|
export namespace mean_field::mapping {
|
||||||
enum class FaceElementSide : uint8_t { element_1, element_2 };
|
enum class FaceElementSide : uint8_t { element_1, element_2 };
|
||||||
|
|
||||||
class ElementDisplacementData {
|
class ElementDisplacementData {
|
||||||
public:
|
public:
|
||||||
ElementDisplacementData(
|
ElementDisplacementData(
|
||||||
const mfem::FiniteElement &element, const mfem::Vector &displacement_dofs,
|
const mfem::FiniteElement &element,
|
||||||
mfem::Ordering::Type ordering = mfem::Ordering::byNODES);
|
const mfem::Vector &displacement_dofs,
|
||||||
|
mfem::Ordering::Type ordering = mfem::Ordering::byNODES
|
||||||
|
);
|
||||||
|
|
||||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||||
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
|
[[nodiscard]] const mfem::DenseMatrix &GetDofMatrix() const noexcept;
|
||||||
@@ -22,43 +24,46 @@ public:
|
|||||||
[[nodiscard]] int GetDofCount() const noexcept;
|
[[nodiscard]] int GetDofCount() const noexcept;
|
||||||
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
|
[[nodiscard]] mfem::Ordering::Type GetOrdering() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const mfem::FiniteElement *m_element;
|
const mfem::FiniteElement *m_element;
|
||||||
mfem::DenseMatrix m_dof_matrix;
|
mfem::DenseMatrix m_dof_matrix;
|
||||||
int m_dimension;
|
int m_dimension;
|
||||||
mfem::Ordering::Type m_ordering;
|
mfem::Ordering::Type m_ordering;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CompactificationPointData {
|
struct CompactificationPointData {
|
||||||
double coordinate{0.0};
|
double coordinate{0.0};
|
||||||
mfem::Vector coordinate_gradient;
|
mfem::Vector coordinate_gradient;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] ElementDisplacementData
|
[[nodiscard]] ElementDisplacementData ElementDisplacementDataFromElementVDofs(
|
||||||
ElementDisplacementDataFromElementVDofs(const mfem::FiniteElement &element,
|
const mfem::FiniteElement &element,
|
||||||
const mfem::Vector &displacement_dofs);
|
const mfem::Vector &displacement_dofs
|
||||||
|
);
|
||||||
|
|
||||||
class ElementCompactificationData {
|
class ElementCompactificationData {
|
||||||
public:
|
public:
|
||||||
ElementCompactificationData(const mfem::FiniteElement &element,
|
ElementCompactificationData(
|
||||||
const mfem::Vector &dofs);
|
const mfem::FiniteElement &element,
|
||||||
|
const mfem::Vector &dofs
|
||||||
|
);
|
||||||
|
|
||||||
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
[[nodiscard]] const mfem::FiniteElement &GetElement() const noexcept;
|
||||||
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
|
[[nodiscard]] const mfem::Vector &GetDofs() const noexcept;
|
||||||
[[nodiscard]] int GetDofCount() const noexcept;
|
[[nodiscard]] int GetDofCount() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const mfem::FiniteElement *m_element;
|
const mfem::FiniteElement *m_element;
|
||||||
mfem::Vector m_dofs;
|
mfem::Vector m_dofs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ElementMappingData {
|
struct ElementMappingData {
|
||||||
const ElementDisplacementData &displacement;
|
const ElementDisplacementData &displacement;
|
||||||
const ElementCompactificationData &compactification;
|
const ElementCompactificationData &compactification;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DomainMapper {
|
class DomainMapper {
|
||||||
public:
|
public:
|
||||||
class Workspace {
|
class Workspace {
|
||||||
public:
|
public:
|
||||||
explicit Workspace(int dimension = 3);
|
explicit Workspace(int dimension = 3);
|
||||||
@@ -93,96 +98,113 @@ public:
|
|||||||
compactification::ExteriorMapVariation m_exterior_variation;
|
compactification::ExteriorMapVariation m_exterior_variation;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DomainMapper(
|
DomainMapper(
|
||||||
utils::DomainMapperOptions options,
|
utils::DomainMapperOptions options,
|
||||||
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map);
|
std::unique_ptr<const compactification::ExteriorDomainMap> exterior_map
|
||||||
|
);
|
||||||
|
|
||||||
DomainMapper(const DomainMapper &) = delete;
|
DomainMapper(const DomainMapper &) = delete;
|
||||||
DomainMapper &operator=(const DomainMapper &) = delete;
|
DomainMapper &operator=(const DomainMapper &) = delete;
|
||||||
DomainMapper(DomainMapper &&) = default;
|
DomainMapper(DomainMapper &&) = default;
|
||||||
DomainMapper &operator=(DomainMapper &&) = default;
|
DomainMapper &operator=(DomainMapper &&) = default;
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus
|
[[nodiscard]] MappingStatus EvaluatePoint(
|
||||||
EvaluatePoint(const ElementMappingData &element_data,
|
const ElementMappingData &element_data,
|
||||||
mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
Workspace &workspace, MappingPointContext &context) const;
|
Workspace &workspace,
|
||||||
|
MappingPointContext &context
|
||||||
|
) const;
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus
|
[[nodiscard]] MappingStatus EvaluateVolume(
|
||||||
EvaluateVolume(const ElementMappingData &element_data,
|
const ElementMappingData &element_data,
|
||||||
mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
Workspace &workspace, VolumeMappingContext &context) const;
|
Workspace &workspace,
|
||||||
|
VolumeMappingContext &context
|
||||||
|
) const;
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus
|
[[nodiscard]] MappingStatus EvaluateFace(
|
||||||
EvaluateFace(const ElementMappingData &element_data,
|
const ElementMappingData &element_data,
|
||||||
mfem::FaceElementTransformations &transformation,
|
mfem::FaceElementTransformations &transformation,
|
||||||
FaceElementSide side,
|
FaceElementSide side,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
Workspace &workspace, FaceMappingContext &context) const;
|
Workspace &workspace,
|
||||||
|
FaceMappingContext &context
|
||||||
|
) const;
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus
|
[[nodiscard]] MappingStatus EvaluatePointVariation(
|
||||||
EvaluatePointVariation(const ElementMappingData &element_data,
|
const ElementMappingData &element_data,
|
||||||
const ElementDisplacementData &direction,
|
const ElementDisplacementData &direction,
|
||||||
mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
const MappingPointContext &base_context,
|
const MappingPointContext &base_context,
|
||||||
Workspace &workspace,
|
Workspace &workspace,
|
||||||
MappingPointVariation &variation) const;
|
MappingPointVariation &variation
|
||||||
|
) const;
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus
|
[[nodiscard]] MappingStatus EvaluateVolumeVariation(
|
||||||
EvaluateVolumeVariation(const ElementMappingData &element_data,
|
const ElementMappingData &element_data,
|
||||||
const ElementDisplacementData &direction,
|
const ElementDisplacementData &direction,
|
||||||
mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
const VolumeMappingContext &base_context,
|
const VolumeMappingContext &base_context,
|
||||||
Workspace &workspace,
|
Workspace &workspace,
|
||||||
VolumeMappingVariation &variation) const;
|
VolumeMappingVariation &variation
|
||||||
|
) const;
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus EvaluateFaceVariation(
|
[[nodiscard]] MappingStatus EvaluateFaceVariation(
|
||||||
const ElementMappingData &element_data,
|
const ElementMappingData &element_data,
|
||||||
const ElementDisplacementData &direction,
|
const ElementDisplacementData &direction,
|
||||||
mfem::FaceElementTransformations &transformation, FaceElementSide side,
|
mfem::FaceElementTransformations &transformation,
|
||||||
|
FaceElementSide side,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
const FaceMappingContext &base_context, Workspace &workspace,
|
const FaceMappingContext &base_context,
|
||||||
FaceMappingVariation &variation) const;
|
Workspace &workspace,
|
||||||
|
FaceMappingVariation &variation
|
||||||
|
) const;
|
||||||
|
|
||||||
[[nodiscard]] bool IsCompactifiedElement(
|
[[nodiscard]] bool IsCompactifiedElement(const mfem::ElementTransformation &transformation) const noexcept;
|
||||||
const mfem::ElementTransformation &transformation) const noexcept;
|
|
||||||
[[nodiscard]] int GetDimension() const noexcept;
|
[[nodiscard]] int GetDimension() const noexcept;
|
||||||
[[nodiscard]] const compactification::ExteriorDomainMap &
|
[[nodiscard]] const compactification::ExteriorDomainMap &GetExteriorMap() const noexcept;
|
||||||
GetExteriorMap() const noexcept;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ValidateElementData(const ElementMappingData &element_data) const;
|
void ValidateElementData(const ElementMappingData &element_data) const;
|
||||||
|
|
||||||
void EvaluateField(const ElementDisplacementData &field,
|
void EvaluateField(
|
||||||
|
const ElementDisplacementData &field,
|
||||||
mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
Workspace &workspace, mfem::Vector &value,
|
Workspace &workspace,
|
||||||
mfem::DenseMatrix &jacobian) const;
|
mfem::Vector &value,
|
||||||
|
mfem::DenseMatrix &jacobian
|
||||||
|
) const;
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
|
[[nodiscard]] MappingStatus EvaluateCompactificationCoordinate(
|
||||||
const ElementCompactificationData &compactification,
|
const ElementCompactificationData &compactification,
|
||||||
mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point, Workspace &workspace,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
CompactificationPointData &point_data) const;
|
Workspace &workspace,
|
||||||
|
CompactificationPointData &point_data
|
||||||
|
) const;
|
||||||
|
|
||||||
[[nodiscard]] static mfem::ElementTransformation &
|
[[nodiscard]] static mfem::ElementTransformation &SelectFaceElementTransformation(
|
||||||
SelectFaceElementTransformation(
|
mfem::FaceElementTransformations &transformation,
|
||||||
mfem::FaceElementTransformations &transformation, FaceElementSide side);
|
FaceElementSide side
|
||||||
|
);
|
||||||
|
|
||||||
[[nodiscard]] static const mfem::IntegrationPoint &
|
[[nodiscard]] static const mfem::IntegrationPoint &SelectFaceElementIntegrationPoint(
|
||||||
SelectFaceElementIntegrationPoint(
|
mfem::FaceElementTransformations &transformation,
|
||||||
mfem::FaceElementTransformations &transformation, FaceElementSide side);
|
FaceElementSide side
|
||||||
|
);
|
||||||
|
|
||||||
utils::DomainMapperOptions m_options;
|
utils::DomainMapperOptions m_options;
|
||||||
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
|
std::unique_ptr<const compactification::ExteriorDomainMap> m_exterior_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GridFunctionMappingEvaluator {
|
class GridFunctionMappingEvaluator {
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
* The evaluator references the supplied grid functions and caches copies of
|
* The evaluator references the supplied grid functions and caches copies of
|
||||||
* their element-local DOFs. Call InvalidateCache() or Refresh() after either
|
* their element-local DOFs. Call InvalidateCache() or Refresh() after either
|
||||||
@@ -194,7 +216,8 @@ public:
|
|||||||
GridFunctionMappingEvaluator(
|
GridFunctionMappingEvaluator(
|
||||||
const DomainMapper &mapper,
|
const DomainMapper &mapper,
|
||||||
const mfem::GridFunction &displacement,
|
const mfem::GridFunction &displacement,
|
||||||
const mfem::GridFunction &compactification_coordinate);
|
const mfem::GridFunction &compactification_coordinate
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Discard all element-local field data. The next evaluation reloads its
|
* Discard all element-local field data. The next evaluation reloads its
|
||||||
@@ -210,37 +233,43 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Refresh();
|
void Refresh();
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus
|
[[nodiscard]] MappingStatus EvaluatePoint(
|
||||||
EvaluatePoint(mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
MappingPointContext &context);
|
MappingPointContext &context
|
||||||
|
);
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus
|
[[nodiscard]] MappingStatus EvaluateVolume(
|
||||||
EvaluateVolume(mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
VolumeMappingContext &context);
|
VolumeMappingContext &context
|
||||||
|
);
|
||||||
|
|
||||||
[[nodiscard]] MappingStatus
|
[[nodiscard]] MappingStatus EvaluateFace(
|
||||||
EvaluateFace(mfem::FaceElementTransformations &transformation,
|
mfem::FaceElementTransformations &transformation,
|
||||||
FaceElementSide side,
|
FaceElementSide side,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
FaceMappingContext &context);
|
FaceMappingContext &context
|
||||||
|
);
|
||||||
|
|
||||||
[[nodiscard]] VolumeQuadratureContext
|
[[nodiscard]] VolumeQuadratureContext GetQuadratureContext(
|
||||||
GetQuadratureContext(mfem::ElementTransformation &transformation,
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point);
|
const mfem::IntegrationPoint &integration_point
|
||||||
|
);
|
||||||
|
|
||||||
[[nodiscard]] FaceQuadratureContext
|
[[nodiscard]] FaceQuadratureContext GetFaceQuadratureContext(
|
||||||
GetFaceQuadratureContext(
|
|
||||||
mfem::FaceElementTransformations &transformation,
|
mfem::FaceElementTransformations &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
FaceElementSide side = FaceElementSide::element_1);
|
FaceElementSide side = FaceElementSide::element_1
|
||||||
|
);
|
||||||
|
|
||||||
void GetPhysicalPoint(mfem::ElementTransformation &transformation,
|
void GetPhysicalPoint(
|
||||||
|
mfem::ElementTransformation &transformation,
|
||||||
const mfem::IntegrationPoint &integration_point,
|
const mfem::IntegrationPoint &integration_point,
|
||||||
mfem::Vector &physical_position);
|
mfem::Vector &physical_position
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ValidateFieldBindings() const;
|
void ValidateFieldBindings() const;
|
||||||
[[nodiscard]] bool InvalidateForChangedSpaces();
|
[[nodiscard]] bool InvalidateForChangedSpaces();
|
||||||
void LoadElement(int element_id);
|
void LoadElement(int element_id);
|
||||||
@@ -261,6 +290,6 @@ private:
|
|||||||
std::unique_ptr<ElementDisplacementData> m_displacement_data;
|
std::unique_ptr<ElementDisplacementData> m_displacement_data;
|
||||||
std::unique_ptr<ElementCompactificationData> m_compactification_data;
|
std::unique_ptr<ElementCompactificationData> m_compactification_data;
|
||||||
int m_cached_element_id{-1};
|
int m_cached_element_id{-1};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mean_field::mapping
|
} // namespace mean_field::mapping
|
||||||
|
|||||||
@@ -54,10 +54,19 @@ export import :operators.prepared_displacement_residual;
|
|||||||
export import :model.structure_profile;
|
export import :model.structure_profile;
|
||||||
export import :model.structure.base;
|
export import :model.structure.base;
|
||||||
export import :model.structure.polytropic;
|
export import :model.structure.polytropic;
|
||||||
export import :eos.base;
|
export import :eos.quantities;
|
||||||
|
export import :eos.relations;
|
||||||
|
export import :eos.concepts;
|
||||||
|
export import :eos.evaluation;
|
||||||
|
export import :eos.pressure_surface;
|
||||||
|
export import :eos.runtime;
|
||||||
export import :eos.polytrope;
|
export import :eos.polytrope;
|
||||||
export import :surface.base;
|
export import :surface.constant;
|
||||||
export import :surface.isobaric;
|
export import :surface.dependencies;
|
||||||
|
export import :surface.compiled;
|
||||||
|
export import :surface.compiler;
|
||||||
export import :model.stellar;
|
export import :model.stellar;
|
||||||
export import :operators.prepared_mass_normalization;
|
export import :operators.prepared_mass_normalization;
|
||||||
|
export import :operators.prepared_centering_constraint;
|
||||||
|
export import :operators.prepared_surface_constraint;
|
||||||
export import :operators.prepared_stellar_equilibrium;
|
export import :operators.prepared_stellar_equilibrium;
|
||||||
|
|||||||
@@ -7,62 +7,84 @@ module;
|
|||||||
|
|
||||||
export module mean_field:model.stellar;
|
export module mean_field:model.stellar;
|
||||||
|
|
||||||
export import :eos.base;
|
export import :eos.runtime;
|
||||||
export import :model.structure.base;
|
export import :model.structure.base;
|
||||||
export import :surface.base;
|
export import :surface.compiler;
|
||||||
|
|
||||||
export namespace mean_field::models {
|
export namespace mean_field::models {
|
||||||
|
namespace detail {
|
||||||
template <typename Candidate>
|
template <typename Candidate>
|
||||||
concept StructurePrescription =
|
concept ConstEquationOfStateReference =
|
||||||
std::derived_from<std::remove_cvref_t<Candidate>, mean_field::models::structure::StructureBase>;
|
std::is_lvalue_reference_v<Candidate> && std::is_const_v<std::remove_reference_t<Candidate>> &&
|
||||||
|
eos::EquationOfStateModel<std::remove_cvref_t<Candidate>>;
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
template <typename Candidate>
|
template <typename Candidate>
|
||||||
concept SurfacePrescription = std::derived_from<std::remove_cvref_t<Candidate>, mean_field::surface::SurfaceBase>;
|
concept StructurePrescription = requires(
|
||||||
|
const std::remove_cvref_t<Candidate> &structurePrescription,
|
||||||
|
const structure::StructureSeedRequest &seedRequest
|
||||||
|
) {
|
||||||
|
{ structurePrescription.equationOfState() } noexcept -> detail::ConstEquationOfStateReference;
|
||||||
|
{ structurePrescription.targetMass() } noexcept -> std::same_as<double>;
|
||||||
|
{ structurePrescription.makeInitialSeed(seedRequest) } -> std::same_as<structure::StructureSeed>;
|
||||||
|
{ structurePrescription.validate() } -> std::same_as<void>;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
template <StructurePrescription Candidate>
|
||||||
* Public ownership facade for a physical structure prescription and its
|
using StructureEquationOfStateT =
|
||||||
* stellar-surface prescription.
|
std::remove_cvref_t<decltype(std::declval<const std::remove_cvref_t<Candidate> &>().equationOfState())>;
|
||||||
*
|
|
||||||
* The concrete prescriptions are allocated once at construction. Their
|
template <typename Candidate, typename EquationOfState>
|
||||||
* stable addresses allow future prepared operators and contexts to borrow
|
concept SurfacePrescription =
|
||||||
* references without making ownership part of the user-facing API.
|
surface::ConstantPressureSurfaceType<Candidate> &&
|
||||||
*/
|
surface::PressureSurfaceCompilable<surface::BarotropicSurfaceFormulation, std::remove_cvref_t<EquationOfState>>;
|
||||||
|
|
||||||
|
template <StructurePrescription Structure>
|
||||||
|
requires SurfacePrescription<surface::ConstantPressureSurface, StructureEquationOfStateT<Structure>>
|
||||||
class StellarModel final {
|
class StellarModel final {
|
||||||
public:
|
public:
|
||||||
template <
|
using StructurePrescriptionType = Structure;
|
||||||
StructurePrescription StructureType,
|
using SurfacePrescriptionType = surface::ConstantPressureSurface;
|
||||||
SurfacePrescription SurfaceType>
|
using EquationOfStateType = StructureEquationOfStateT<Structure>;
|
||||||
|
using SurfaceConstraintType =
|
||||||
|
surface::CompiledPressureSurfaceConstraintT<surface::BarotropicSurfaceFormulation, EquationOfStateType>;
|
||||||
|
|
||||||
|
template <typename StructureArgument>
|
||||||
|
requires std::same_as<
|
||||||
|
std::remove_cvref_t<StructureArgument>,
|
||||||
|
Structure>
|
||||||
explicit StellarModel(
|
explicit StellarModel(
|
||||||
StructureType &&structurePrescription,
|
StructureArgument &&structurePrescription,
|
||||||
SurfaceType &&surfacePrescription
|
const surface::ConstantPressureSurface surfacePrescription
|
||||||
)
|
)
|
||||||
: StellarModel(
|
: m_structurePrescription(
|
||||||
std::make_unique<std::remove_cvref_t<StructureType>>(
|
std::make_unique<Structure>(std::forward<StructureArgument>(structurePrescription))
|
||||||
std::forward<StructureType>(structurePrescription)
|
|
||||||
),
|
),
|
||||||
std::make_unique<std::remove_cvref_t<SurfaceType>>(std::forward<SurfaceType>(surfacePrescription))
|
m_surfacePrescription(std::make_unique<surface::ConstantPressureSurface>(surfacePrescription)),
|
||||||
|
m_compiledSurfaceConstraint(
|
||||||
|
std::make_unique<SurfaceConstraintType>(validateAndCompileSurface(
|
||||||
|
*m_structurePrescription,
|
||||||
|
*m_surfacePrescription
|
||||||
|
))
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~StellarModel() = default;
|
~StellarModel() = default;
|
||||||
|
|
||||||
StellarModel(const StellarModel &) = delete;
|
StellarModel(const StellarModel &) = delete;
|
||||||
|
|
||||||
StellarModel &operator=(const StellarModel &) = delete;
|
StellarModel &operator=(const StellarModel &) = delete;
|
||||||
|
|
||||||
StellarModel(StellarModel &&) noexcept = default;
|
StellarModel(StellarModel &&) noexcept = default;
|
||||||
|
|
||||||
StellarModel &operator=(StellarModel &&) noexcept = default;
|
StellarModel &operator=(StellarModel &&) noexcept = default;
|
||||||
|
|
||||||
[[nodiscard]] const mean_field::models::structure::StructureBase &structurePrescription() const noexcept {
|
[[nodiscard]] const Structure &structurePrescription() const noexcept {
|
||||||
return *m_structurePrescription;
|
return *m_structurePrescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const mean_field::surface::SurfaceBase &surfacePrescription() const noexcept {
|
[[nodiscard]] const surface::ConstantPressureSurface &surfacePrescription() const noexcept {
|
||||||
return *m_surfacePrescription;
|
return *m_surfacePrescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const mean_field::eos::EquationOfState &equationOfState() const noexcept {
|
[[nodiscard]] const EquationOfStateType &equationOfState() const noexcept {
|
||||||
return m_structurePrescription->equationOfState();
|
return m_structurePrescription->equationOfState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,45 +92,99 @@ export namespace mean_field::models {
|
|||||||
return m_structurePrescription->targetMass();
|
return m_structurePrescription->targetMass();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mean_field::models::structure::StructureSeed
|
[[nodiscard]] structure::StructureSeed makeInitialSeed(const structure::StructureSeedRequest &request) const {
|
||||||
makeInitialSeed(const mean_field::models::structure::StructureSeedRequest &request) const {
|
|
||||||
return m_structurePrescription->makeInitialSeed(request);
|
return m_structurePrescription->makeInitialSeed(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const mean_field::surface::ResolvedSurfaceCondition &resolvedSurfaceCondition() const noexcept {
|
[[nodiscard]] const SurfaceConstraintType &compiledSurfaceConstraint() const noexcept {
|
||||||
return m_resolvedSurfaceCondition;
|
return *m_compiledSurfaceConstraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit StellarModel(
|
[[nodiscard]] static SurfaceConstraintType validateAndCompileSurface(
|
||||||
std::unique_ptr<mean_field::models::structure::StructureBase> structurePrescription,
|
const Structure &structurePrescription,
|
||||||
std::unique_ptr<mean_field::surface::SurfaceBase> surfacePrescription
|
const surface::ConstantPressureSurface &surfacePrescription
|
||||||
)
|
|
||||||
: m_structurePrescription(std::move(structurePrescription)),
|
|
||||||
m_surfacePrescription(std::move(surfacePrescription)),
|
|
||||||
m_resolvedSurfaceCondition(validateAndResolve(
|
|
||||||
*m_structurePrescription,
|
|
||||||
*m_surfacePrescription
|
|
||||||
)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static mean_field::surface::ResolvedSurfaceCondition validateAndResolve(
|
|
||||||
const mean_field::models::structure::StructureBase &structurePrescription,
|
|
||||||
const mean_field::surface::SurfaceBase &surfacePrescription
|
|
||||||
) {
|
) {
|
||||||
structurePrescription.validate();
|
structurePrescription.validate();
|
||||||
|
|
||||||
const mean_field::eos::EquationOfState &equationOfState = structurePrescription.equationOfState();
|
return surface::compilePressureSurfaceConstraint<surface::BarotropicSurfaceFormulation>(
|
||||||
|
surfacePrescription, structurePrescription.equationOfState()
|
||||||
surfacePrescription.validate(equationOfState);
|
);
|
||||||
|
|
||||||
return surfacePrescription.resolve(equationOfState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<mean_field::models::structure::StructureBase> m_structurePrescription;
|
std::unique_ptr<Structure> m_structurePrescription;
|
||||||
|
std::unique_ptr<surface::ConstantPressureSurface> m_surfacePrescription;
|
||||||
|
std::unique_ptr<SurfaceConstraintType> m_compiledSurfaceConstraint;
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<mean_field::surface::SurfaceBase> m_surfacePrescription;
|
template <typename Structure>
|
||||||
|
StellarModel(
|
||||||
|
Structure &&,
|
||||||
|
surface::ConstantPressureSurface
|
||||||
|
) -> StellarModel<std::remove_cvref_t<Structure>>;
|
||||||
|
|
||||||
mean_field::surface::ResolvedSurfaceCondition m_resolvedSurfaceCondition;
|
namespace detail {
|
||||||
|
template <typename Candidate> struct IsStellarModel : std::false_type { };
|
||||||
|
|
||||||
|
template <typename Structure> struct IsStellarModel<StellarModel<Structure>> : std::true_type { };
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept StellarModelType = detail::IsStellarModel<std::remove_cvref_t<Candidate>>::value;
|
||||||
|
|
||||||
|
class StellarModelView final {
|
||||||
|
public:
|
||||||
|
template <typename Model>
|
||||||
|
requires StellarModelType<Model> &&
|
||||||
|
eos::RuntimeEquationOfStateModel<typename std::remove_cvref_t<Model>::EquationOfStateType>
|
||||||
|
explicit StellarModelView(Model &model) noexcept
|
||||||
|
: m_equationOfState(model.equationOfState()),
|
||||||
|
m_structurePrescription(std::addressof(model.structurePrescription())),
|
||||||
|
m_makeInitialSeed(&makeInitialSeedFor<typename std::remove_cvref_t<Model>::StructurePrescriptionType>),
|
||||||
|
m_targetMass(model.targetMass()),
|
||||||
|
m_surfaceCondition(model.compiledSurfaceConstraint().descriptor()),
|
||||||
|
m_surfaceDependencies(model.compiledSurfaceConstraint().runtimeDependencies()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] eos::EquationOfStateView equationOfState() const noexcept {
|
||||||
|
return m_equationOfState;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] double targetMass() const noexcept {
|
||||||
|
return m_targetMass;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] structure::StructureSeed makeInitialSeed(const structure::StructureSeedRequest &request) const {
|
||||||
|
return m_makeInitialSeed(m_structurePrescription, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] surface::PressureSurfaceDescriptor surfaceCondition() const noexcept {
|
||||||
|
return m_surfaceCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] surface::RuntimeSurfaceConstraintDependencies surfaceDependencies() const noexcept {
|
||||||
|
return m_surfaceDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using MakeInitialSeedFunction = structure::StructureSeed (*)(
|
||||||
|
const void *,
|
||||||
|
const structure::StructureSeedRequest &
|
||||||
|
);
|
||||||
|
|
||||||
|
template <StructurePrescription Structure>
|
||||||
|
[[nodiscard]] static structure::StructureSeed makeInitialSeedFor(
|
||||||
|
const void *structurePrescription,
|
||||||
|
const structure::StructureSeedRequest &request
|
||||||
|
) {
|
||||||
|
return static_cast<const Structure *>(structurePrescription)->makeInitialSeed(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
eos::EquationOfStateView m_equationOfState;
|
||||||
|
const void *m_structurePrescription;
|
||||||
|
MakeInitialSeedFunction m_makeInitialSeed;
|
||||||
|
double m_targetMass;
|
||||||
|
surface::PressureSurfaceDescriptor m_surfaceCondition;
|
||||||
|
surface::RuntimeSurfaceConstraintDependencies m_surfaceDependencies;
|
||||||
};
|
};
|
||||||
} // namespace mean_field::models
|
} // namespace mean_field::models
|
||||||
|
|||||||
@@ -12,20 +12,20 @@ export import :model.structure.base;
|
|||||||
import :utils.misc;
|
import :utils.misc;
|
||||||
|
|
||||||
export namespace mean_field::models::structure {
|
export namespace mean_field::models::structure {
|
||||||
class PolytropicStructure final : public StructureBase {
|
class PolytropicStructure final {
|
||||||
public:
|
public:
|
||||||
explicit PolytropicStructure(
|
explicit PolytropicStructure(
|
||||||
eos::Polytrope equationOfState,
|
eos::Polytrope equationOfState,
|
||||||
double targetMass
|
double targetMass
|
||||||
);
|
);
|
||||||
|
|
||||||
[[nodiscard]] const eos::EquationOfState &equationOfState() const noexcept override;
|
[[nodiscard]] const eos::Polytrope &equationOfState() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] double targetMass() const noexcept override;
|
[[nodiscard]] double targetMass() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] StructureSeed makeInitialSeed(const StructureSeedRequest &request) const override;
|
[[nodiscard]] StructureSeed makeInitialSeed(const StructureSeedRequest &request) const;
|
||||||
|
|
||||||
void validate() const override;
|
void validate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct LaneEmdenPoint {
|
struct LaneEmdenPoint {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module;
|
module;
|
||||||
#include <mfem.hpp>
|
#include <mfem.hpp>
|
||||||
export module mean_field:model.structure.base;
|
export module mean_field:model.structure.base;
|
||||||
export import :eos.base;
|
export import :eos.runtime;
|
||||||
|
|
||||||
export namespace mean_field::models::structure {
|
export namespace mean_field::models::structure {
|
||||||
struct StructureSeed {
|
struct StructureSeed {
|
||||||
@@ -23,7 +23,7 @@ export namespace mean_field::models::structure {
|
|||||||
public:
|
public:
|
||||||
virtual ~StructureBase() = default;
|
virtual ~StructureBase() = default;
|
||||||
|
|
||||||
[[nodiscard]] virtual const eos::EquationOfState &equationOfState() const noexcept = 0;
|
[[nodiscard]] virtual eos::EquationOfStateView equationOfState() const noexcept = 0;
|
||||||
|
|
||||||
[[nodiscard]] virtual double targetMass() const noexcept = 0;
|
[[nodiscard]] virtual double targetMass() const noexcept = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <mfem.hpp>
|
||||||
|
|
||||||
|
export module mean_field:operators.prepared_centering_constraint;
|
||||||
|
|
||||||
|
export import :field.mfem;
|
||||||
|
|
||||||
|
export namespace mean_field::operators {
|
||||||
|
struct PreparedCenteringConstraintReport final {
|
||||||
|
bool cachedCenterDisplacement{false};
|
||||||
|
|
||||||
|
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||||
|
return cachedCenterDisplacement;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Strong translational gauge: the material point at the computational
|
||||||
|
* origin has zero displacement. The three corresponding displacement
|
||||||
|
* residual rows replace redundant force-balance rows.
|
||||||
|
*/
|
||||||
|
class PreparedCenteringConstraint final {
|
||||||
|
public:
|
||||||
|
explicit PreparedCenteringConstraint(field::FieldPointDofMap centerRows)
|
||||||
|
: m_centerRows(std::move(centerRows)),
|
||||||
|
m_centerDisplacement(m_centerRows.size()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] PreparedCenteringConstraintReport Prepare(
|
||||||
|
const mfem::Vector &displacement,
|
||||||
|
const bool displacementChanged
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
displacement.Size() == m_centerRows.field_size(),
|
||||||
|
"The centering constraint received a displacement vector with the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
|
PreparedCenteringConstraintReport report;
|
||||||
|
if (!m_isPrepared || displacementChanged) {
|
||||||
|
for (int centerIndex = 0; centerIndex < m_centerRows.size(); ++centerIndex) {
|
||||||
|
const double value = displacement(m_centerRows.reduced_dofs()[centerIndex]);
|
||||||
|
MFEM_VERIFY(
|
||||||
|
std::isfinite(value), "The centering constraint received a non-finite center displacement."
|
||||||
|
);
|
||||||
|
m_centerDisplacement(centerIndex) = value;
|
||||||
|
}
|
||||||
|
report.cachedCenterDisplacement = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_isPrepared = true;
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyResidualRows(mfem::Vector &displacementResidual) const {
|
||||||
|
VerifyPrepared();
|
||||||
|
MFEM_VERIFY(
|
||||||
|
displacementResidual.Size() == m_centerRows.field_size(),
|
||||||
|
"The centering constraint received a displacement residual with the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int centerIndex = 0; centerIndex < m_centerRows.size(); ++centerIndex) {
|
||||||
|
displacementResidual(m_centerRows.reduced_dofs()[centerIndex]) = m_centerDisplacement(centerIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyJacobianRows(
|
||||||
|
const mfem::Vector &displacementVariation,
|
||||||
|
mfem::Vector &displacementAction
|
||||||
|
) const {
|
||||||
|
VerifyPrepared();
|
||||||
|
MFEM_VERIFY(
|
||||||
|
displacementVariation.Size() == m_centerRows.field_size() &&
|
||||||
|
displacementAction.Size() == m_centerRows.field_size(),
|
||||||
|
"The centering constraint received a Jacobian vector with the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const int centerRow : m_centerRows.reduced_dofs()) {
|
||||||
|
displacementAction(centerRow) = displacementVariation(centerRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||||
|
return m_isPrepared;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const field::FieldPointDofMap &GetCenterRows() const noexcept {
|
||||||
|
return m_centerRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void VerifyPrepared() const {
|
||||||
|
MFEM_VERIFY(m_isPrepared, "The centering constraint must be prepared before row application.");
|
||||||
|
}
|
||||||
|
|
||||||
|
field::FieldPointDofMap m_centerRows;
|
||||||
|
mfem::Vector m_centerDisplacement;
|
||||||
|
bool m_isPrepared{false};
|
||||||
|
};
|
||||||
|
} // namespace mean_field::operators
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
#include <compare>
|
#include <compare>
|
||||||
|
#include <concepts>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include <mfem.hpp>
|
#include <mfem.hpp>
|
||||||
|
|
||||||
@@ -16,9 +18,11 @@ export import :operators.context.gravity_field;
|
|||||||
export import :operators.gravity_field;
|
export import :operators.gravity_field;
|
||||||
export import :operators.gravity_field_jacobian;
|
export import :operators.gravity_field_jacobian;
|
||||||
export import :operators.prepared_barotropic_closure;
|
export import :operators.prepared_barotropic_closure;
|
||||||
|
export import :operators.prepared_centering_constraint;
|
||||||
export import :operators.prepared_displacement_residual;
|
export import :operators.prepared_displacement_residual;
|
||||||
export import :operators.prepared_hydrostatic_equilibrium;
|
export import :operators.prepared_hydrostatic_equilibrium;
|
||||||
export import :operators.prepared_mass_normalization;
|
export import :operators.prepared_mass_normalization;
|
||||||
|
export import :operators.prepared_surface_constraint;
|
||||||
export import :physics.rigid_rotation;
|
export import :physics.rigid_rotation;
|
||||||
export import :utils.blocks;
|
export import :utils.blocks;
|
||||||
|
|
||||||
@@ -50,11 +54,14 @@ export namespace mean_field::operators {
|
|||||||
PreparedHydrostaticEquilibriumReport hydrostatic;
|
PreparedHydrostaticEquilibriumReport hydrostatic;
|
||||||
PreparedDisplacementResidualReport displacement;
|
PreparedDisplacementResidualReport displacement;
|
||||||
PreparedMassNormalizationReport massNormalization;
|
PreparedMassNormalizationReport massNormalization;
|
||||||
|
PreparedSurfaceConstraintReport surfaceConstraint;
|
||||||
|
PreparedCenteringConstraintReport centeringConstraint;
|
||||||
bool assembledResidual{false};
|
bool assembledResidual{false};
|
||||||
|
|
||||||
[[nodiscard]] bool DidAnyChildWork() const noexcept {
|
[[nodiscard]] bool DidAnyChildWork() const noexcept {
|
||||||
return gravity.DidAnyWork() || barotropicClosure.DidAnyWork() || hydrostatic.DidAnyWork() ||
|
return gravity.DidAnyWork() || barotropicClosure.DidAnyWork() || hydrostatic.DidAnyWork() ||
|
||||||
displacement.DidAnyWork() || massNormalization.DidAnyWork();
|
displacement.DidAnyWork() || massNormalization.DidAnyWork() || surfaceConstraint.DidAnyWork() ||
|
||||||
|
centeringConstraint.DidAnyWork();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool DidAnyWork() const noexcept {
|
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||||
@@ -74,19 +81,27 @@ export namespace mean_field::operators {
|
|||||||
|
|
||||||
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
|
||||||
public:
|
public:
|
||||||
|
template <models::StellarModelType Model>
|
||||||
|
requires std::same_as<
|
||||||
|
typename std::remove_cvref_t<Model>::EquationOfStateType,
|
||||||
|
eos::Polytrope> &&
|
||||||
|
SingleFieldPressureSurfaceConstraintFor<
|
||||||
|
typename std::remove_cvref_t<Model>::SurfaceConstraintType,
|
||||||
|
field::Enthalpy> &&
|
||||||
|
std::is_lvalue_reference_v<Model &&>
|
||||||
PreparedStellarEquilibriumOperator(
|
PreparedStellarEquilibriumOperator(
|
||||||
fem::FEM &f,
|
fem::FEM &f,
|
||||||
const mapping::DomainMapper &domainMapper,
|
const mapping::DomainMapper &domainMapper,
|
||||||
const eos::Polytrope &equationOfState,
|
Model &&stellarModel
|
||||||
double targetMass
|
)
|
||||||
);
|
: PreparedStellarEquilibriumOperator(
|
||||||
|
f,
|
||||||
PreparedStellarEquilibriumOperator(
|
domainMapper,
|
||||||
fem::FEM &f,
|
stellarModel.equationOfState(),
|
||||||
const mapping::DomainMapper &domainMapper,
|
stellarModel.targetMass(),
|
||||||
const eos::Polytrope &equationOfState,
|
PressureSurfaceConstraintView{stellarModel.compiledSurfaceConstraint()}
|
||||||
const models::StellarModel &stellarModel
|
) {
|
||||||
);
|
}
|
||||||
|
|
||||||
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
|
PreparedStellarEquilibriumOperator(const PreparedStellarEquilibriumOperator &) = delete;
|
||||||
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
|
PreparedStellarEquilibriumOperator &operator=(const PreparedStellarEquilibriumOperator &) = delete;
|
||||||
@@ -122,6 +137,8 @@ export namespace mean_field::operators {
|
|||||||
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
|
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
|
||||||
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
|
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
|
||||||
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
|
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
|
||||||
|
[[nodiscard]] const PreparedPressureSurfaceConstraint &GetSurfaceConstraintOperator() const noexcept;
|
||||||
|
[[nodiscard]] const PreparedCenteringConstraint &GetCenteringConstraintOperator() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ConstructionData;
|
struct ConstructionData;
|
||||||
@@ -133,6 +150,15 @@ export namespace mean_field::operators {
|
|||||||
const mapping::DomainMapper &domainMapper,
|
const mapping::DomainMapper &domainMapper,
|
||||||
const eos::Polytrope &equationOfState,
|
const eos::Polytrope &equationOfState,
|
||||||
double targetMass,
|
double targetMass,
|
||||||
|
PressureSurfaceConstraintView surfaceConstraint
|
||||||
|
);
|
||||||
|
|
||||||
|
PreparedStellarEquilibriumOperator(
|
||||||
|
fem::FEM &f,
|
||||||
|
const mapping::DomainMapper &domainMapper,
|
||||||
|
const eos::Polytrope &equationOfState,
|
||||||
|
double targetMass,
|
||||||
|
PressureSurfaceConstraintView surfaceConstraint,
|
||||||
ConstructionData constructionData
|
ConstructionData constructionData
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -150,6 +176,8 @@ export namespace mean_field::operators {
|
|||||||
PreparedHydrostaticEquilibriumOperator m_hydrostaticOperator;
|
PreparedHydrostaticEquilibriumOperator m_hydrostaticOperator;
|
||||||
PreparedDisplacementResidualOperator m_displacementOperator;
|
PreparedDisplacementResidualOperator m_displacementOperator;
|
||||||
PreparedMassNormalizationOperator m_massNormalizationOperator;
|
PreparedMassNormalizationOperator m_massNormalizationOperator;
|
||||||
|
PreparedPressureSurfaceConstraint m_surfaceConstraintOperator;
|
||||||
|
PreparedCenteringConstraint m_centeringConstraintOperator;
|
||||||
|
|
||||||
StellarEquilibriumDependencies m_preparedDependencies;
|
StellarEquilibriumDependencies m_preparedDependencies;
|
||||||
mfem::Vector m_cachedResidual;
|
mfem::Vector m_cachedResidual;
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <concepts>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <mfem.hpp>
|
||||||
|
|
||||||
|
export module mean_field:operators.prepared_surface_constraint;
|
||||||
|
|
||||||
|
export import :field.mfem;
|
||||||
|
export import :surface.compiled;
|
||||||
|
|
||||||
|
namespace mean_field::operators::detail {
|
||||||
|
template <eos::ThermodynamicQuantityType Quantity> struct SingleQuantitySurfaceState final {
|
||||||
|
eos::QuantityValue<Quantity> quantityValue;
|
||||||
|
|
||||||
|
[[nodiscard]] eos::QuantityValue<Quantity> value(Quantity) const noexcept {
|
||||||
|
return quantityValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace mean_field::operators::detail
|
||||||
|
|
||||||
|
export namespace mean_field::operators {
|
||||||
|
/*
|
||||||
|
* Runtime enforcement currently supports a pointwise pressure constraint
|
||||||
|
* whose row field is also its sole state field. The concept is expressed
|
||||||
|
* entirely in compiled-constraint metadata: no thermodynamic carrier or
|
||||||
|
* concrete field is selected by this prepared layer.
|
||||||
|
*/
|
||||||
|
template <typename Candidate>
|
||||||
|
concept SingleFieldPressureSurfaceConstraint =
|
||||||
|
requires {
|
||||||
|
typename std::remove_cvref_t<Candidate>::PhysicalQuantity;
|
||||||
|
typename std::remove_cvref_t<Candidate>::CarrierQuantity;
|
||||||
|
typename std::remove_cvref_t<Candidate>::CarrierField;
|
||||||
|
typename std::remove_cvref_t<Candidate>::SurfaceDependencies;
|
||||||
|
} && std::same_as<typename std::remove_cvref_t<Candidate>::PhysicalQuantity, eos::quantity::Pressure> &&
|
||||||
|
std::same_as<
|
||||||
|
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField,
|
||||||
|
typename std::remove_cvref_t<Candidate>::CarrierField> &&
|
||||||
|
std::same_as<
|
||||||
|
typename std::remove_cvref_t<Candidate>::SurfaceDependencies::StateFieldTypes,
|
||||||
|
field::TypeList<typename std::remove_cvref_t<Candidate>::CarrierField>>;
|
||||||
|
|
||||||
|
template <typename Candidate, typename Field>
|
||||||
|
concept SingleFieldPressureSurfaceConstraintFor =
|
||||||
|
SingleFieldPressureSurfaceConstraint<Candidate> &&
|
||||||
|
std::same_as<typename std::remove_cvref_t<Candidate>::SurfaceDependencies::RowField, Field>;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Non-owning runtime bridge for a statically compiled pressure constraint.
|
||||||
|
* There is one function-pointer dispatch per complete row application;
|
||||||
|
* the concrete loop remains templated so EOS operations can be inlined.
|
||||||
|
*/
|
||||||
|
class PressureSurfaceConstraintView final {
|
||||||
|
public:
|
||||||
|
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||||
|
explicit PressureSurfaceConstraintView(const Constraint &constraint) noexcept
|
||||||
|
: m_constraint(std::addressof(constraint)),
|
||||||
|
m_applyResidualRows(&applyResidualRows<Constraint>),
|
||||||
|
m_applyJacobianRows(&applyJacobianRows<Constraint>),
|
||||||
|
m_descriptor(constraint.descriptor()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyResidualRows(
|
||||||
|
const mfem::Vector &surfaceState,
|
||||||
|
const field::FieldBoundaryDofMap &surfaceRows,
|
||||||
|
mfem::Vector &rowResidual
|
||||||
|
) const {
|
||||||
|
m_applyResidualRows(m_constraint, surfaceState, surfaceRows, rowResidual);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyJacobianRows(
|
||||||
|
const mfem::Vector &surfaceState,
|
||||||
|
const field::FieldBoundaryDofMap &surfaceRows,
|
||||||
|
const mfem::Vector &stateVariation,
|
||||||
|
mfem::Vector &rowAction
|
||||||
|
) const {
|
||||||
|
m_applyJacobianRows(m_constraint, surfaceState, surfaceRows, stateVariation, rowAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] surface::PressureSurfaceDescriptor descriptor() const noexcept {
|
||||||
|
return m_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ApplyResidualRowsFunction = void (*)(
|
||||||
|
const void *,
|
||||||
|
const mfem::Vector &,
|
||||||
|
const field::FieldBoundaryDofMap &,
|
||||||
|
mfem::Vector &
|
||||||
|
);
|
||||||
|
using ApplyJacobianRowsFunction = void (*)(
|
||||||
|
const void *,
|
||||||
|
const mfem::Vector &,
|
||||||
|
const field::FieldBoundaryDofMap &,
|
||||||
|
const mfem::Vector &,
|
||||||
|
mfem::Vector &
|
||||||
|
);
|
||||||
|
|
||||||
|
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||||
|
static void applyResidualRows(
|
||||||
|
const void *constraint,
|
||||||
|
const mfem::Vector &surfaceState,
|
||||||
|
const field::FieldBoundaryDofMap &surfaceRows,
|
||||||
|
mfem::Vector &rowResidual
|
||||||
|
) {
|
||||||
|
using CarrierQuantity = typename Constraint::CarrierQuantity;
|
||||||
|
|
||||||
|
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||||
|
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||||
|
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||||
|
};
|
||||||
|
rowResidual(surfaceRows.reduced_dofs()[surfaceIndex]) =
|
||||||
|
static_cast<const Constraint *>(constraint)->residual(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <SingleFieldPressureSurfaceConstraint Constraint>
|
||||||
|
static void applyJacobianRows(
|
||||||
|
const void *constraint,
|
||||||
|
const mfem::Vector &surfaceState,
|
||||||
|
const field::FieldBoundaryDofMap &surfaceRows,
|
||||||
|
const mfem::Vector &stateVariation,
|
||||||
|
mfem::Vector &rowAction
|
||||||
|
) {
|
||||||
|
using CarrierQuantity = typename Constraint::CarrierQuantity;
|
||||||
|
|
||||||
|
for (int surfaceIndex = 0; surfaceIndex < surfaceRows.size(); ++surfaceIndex) {
|
||||||
|
const int reducedDof = surfaceRows.reduced_dofs()[surfaceIndex];
|
||||||
|
const detail::SingleQuantitySurfaceState<CarrierQuantity> state{
|
||||||
|
eos::QuantityValue<CarrierQuantity>{surfaceState(surfaceIndex)}
|
||||||
|
};
|
||||||
|
const detail::SingleQuantitySurfaceState<CarrierQuantity> variation{
|
||||||
|
eos::QuantityValue<CarrierQuantity>{stateVariation(reducedDof)}
|
||||||
|
};
|
||||||
|
rowAction(reducedDof) = static_cast<const Constraint *>(constraint)->jacobianAction(state, variation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *m_constraint;
|
||||||
|
ApplyResidualRowsFunction m_applyResidualRows;
|
||||||
|
ApplyJacobianRowsFunction m_applyJacobianRows;
|
||||||
|
surface::PressureSurfaceDescriptor m_descriptor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PreparedSurfaceConstraintReport final {
|
||||||
|
bool cachedSurfaceState{false};
|
||||||
|
|
||||||
|
[[nodiscard]] bool DidAnyWork() const noexcept {
|
||||||
|
return cachedSurfaceState;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PreparedPressureSurfaceConstraint final {
|
||||||
|
public:
|
||||||
|
PreparedPressureSurfaceConstraint(
|
||||||
|
field::FieldBoundaryDofMap surfaceRows,
|
||||||
|
const PressureSurfaceConstraintView constraint
|
||||||
|
)
|
||||||
|
: m_surfaceRows(std::move(surfaceRows)),
|
||||||
|
m_constraint(constraint),
|
||||||
|
m_surfaceState(m_surfaceRows.size()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] PreparedSurfaceConstraintReport Prepare(
|
||||||
|
const mfem::Vector &reducedState,
|
||||||
|
const bool stateChanged
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
reducedState.Size() == m_surfaceRows.field_size(),
|
||||||
|
"The pressure surface constraint received a state vector with the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
|
PreparedSurfaceConstraintReport report;
|
||||||
|
if (!m_isPrepared || stateChanged) {
|
||||||
|
for (int surfaceIndex = 0; surfaceIndex < m_surfaceRows.size(); ++surfaceIndex) {
|
||||||
|
const double value = reducedState(m_surfaceRows.reduced_dofs()[surfaceIndex]);
|
||||||
|
MFEM_VERIFY(std::isfinite(value), "The pressure surface constraint received non-finite state.");
|
||||||
|
m_surfaceState(surfaceIndex) = value;
|
||||||
|
}
|
||||||
|
report.cachedSurfaceState = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_isPrepared = true;
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyResidualRows(mfem::Vector &rowResidual) const {
|
||||||
|
VerifyPrepared();
|
||||||
|
MFEM_VERIFY(
|
||||||
|
rowResidual.Size() == m_surfaceRows.field_size(),
|
||||||
|
"The pressure surface constraint received a residual vector with the wrong size."
|
||||||
|
);
|
||||||
|
m_constraint.ApplyResidualRows(m_surfaceState, m_surfaceRows, rowResidual);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyJacobianRows(
|
||||||
|
const mfem::Vector &stateVariation,
|
||||||
|
mfem::Vector &rowAction
|
||||||
|
) const {
|
||||||
|
VerifyPrepared();
|
||||||
|
MFEM_VERIFY(
|
||||||
|
stateVariation.Size() == m_surfaceRows.field_size() && rowAction.Size() == m_surfaceRows.field_size(),
|
||||||
|
"The pressure surface constraint received a Jacobian vector with the wrong size."
|
||||||
|
);
|
||||||
|
m_constraint.ApplyJacobianRows(m_surfaceState, m_surfaceRows, stateVariation, rowAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsPrepared() const noexcept {
|
||||||
|
return m_isPrepared;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const field::FieldBoundaryDofMap &GetSurfaceRows() const noexcept {
|
||||||
|
return m_surfaceRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] surface::PressureSurfaceDescriptor GetPhysicalCondition() const noexcept {
|
||||||
|
return m_constraint.descriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void VerifyPrepared() const {
|
||||||
|
MFEM_VERIFY(m_isPrepared, "The pressure surface constraint must be prepared before row application.");
|
||||||
|
}
|
||||||
|
|
||||||
|
field::FieldBoundaryDofMap m_surfaceRows;
|
||||||
|
PressureSurfaceConstraintView m_constraint;
|
||||||
|
mfem::Vector m_surfaceState;
|
||||||
|
bool m_isPrepared{false};
|
||||||
|
};
|
||||||
|
} // namespace mean_field::operators
|
||||||
66
libmeanfield/interface/surface/compiled.cppm
Normal file
66
libmeanfield/interface/surface/compiled.cppm
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
export module mean_field:surface.compiled;
|
||||||
|
|
||||||
|
export import :eos.pressure_surface;
|
||||||
|
export import :surface.constant;
|
||||||
|
export import :surface.dependencies;
|
||||||
|
|
||||||
|
export namespace mean_field::surface {
|
||||||
|
template <
|
||||||
|
eos::EquationOfStateModel EquationOfState,
|
||||||
|
SurfaceConstraintFormulationType Formulation,
|
||||||
|
eos::ThermodynamicRelationType SelectedRelation,
|
||||||
|
typename Dependencies>
|
||||||
|
class CompiledPressureSurfaceConstraint final {
|
||||||
|
public:
|
||||||
|
using PhysicalCondition = ConstantPressureSurface;
|
||||||
|
using PhysicalQuantity = eos::quantity::Pressure;
|
||||||
|
using CarrierQuantity = typename Formulation::CarrierQuantity;
|
||||||
|
using CarrierField = typename Formulation::CarrierField;
|
||||||
|
using Relation = SelectedRelation;
|
||||||
|
using SurfaceDependencies = Dependencies;
|
||||||
|
|
||||||
|
CompiledPressureSurfaceConstraint(
|
||||||
|
const ConstantPressureSurface condition,
|
||||||
|
const EquationOfState &equationOfState
|
||||||
|
) noexcept
|
||||||
|
: m_condition(condition),
|
||||||
|
m_resolvedRelation(
|
||||||
|
equationOfState,
|
||||||
|
condition.targetPressure()
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] eos::PressureValue targetPressure() const noexcept {
|
||||||
|
return m_condition.targetPressure();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] PressureSurfaceDescriptor descriptor() const noexcept {
|
||||||
|
return m_condition.descriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr RuntimeSurfaceConstraintDependencies runtimeDependencies() noexcept {
|
||||||
|
return SurfaceDependencies::runtimeDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SurfaceState> [[nodiscard]] double residual(const SurfaceState &state) const {
|
||||||
|
return state.value(CarrierQuantity{}).value() - m_resolvedRelation.requiredCarrierValue(state).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename SurfaceState,
|
||||||
|
typename SurfaceVariation>
|
||||||
|
[[nodiscard]] double jacobianAction(
|
||||||
|
const SurfaceState &state,
|
||||||
|
const SurfaceVariation &variation
|
||||||
|
) const {
|
||||||
|
return variation.value(CarrierQuantity{}).value() -
|
||||||
|
m_resolvedRelation.carrierCorrectionJacobianAction(state, variation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConstantPressureSurface m_condition;
|
||||||
|
eos::ResolvedPressureSurfaceRelation<EquationOfState, Relation> m_resolvedRelation;
|
||||||
|
};
|
||||||
|
} // namespace mean_field::surface
|
||||||
143
libmeanfield/interface/surface/compiler.cppm
Normal file
143
libmeanfield/interface/surface/compiler.cppm
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
export module mean_field:surface.compiler;
|
||||||
|
|
||||||
|
export import :surface.compiled;
|
||||||
|
|
||||||
|
export namespace mean_field::surface {
|
||||||
|
namespace detail {
|
||||||
|
template <typename RelationType, typename Formulation, typename EquationOfState>
|
||||||
|
struct PressureSurfaceRelationMatches : std::false_type { };
|
||||||
|
|
||||||
|
template <typename OutputQuantity, typename... InputQuantities, typename Formulation, typename EquationOfState>
|
||||||
|
struct PressureSurfaceRelationMatches<
|
||||||
|
eos::Relation<OutputQuantity, InputQuantities...>,
|
||||||
|
Formulation,
|
||||||
|
EquationOfState>
|
||||||
|
: std::bool_constant<
|
||||||
|
std::same_as<OutputQuantity, typename Formulation::CarrierQuantity> &&
|
||||||
|
(std::same_as<eos::quantity::Pressure, InputQuantities> || ...) &&
|
||||||
|
((std::same_as<eos::quantity::Pressure, InputQuantities> ||
|
||||||
|
(surfaceBindingCount<typename Formulation::StateBindings, InputQuantities> == 1 &&
|
||||||
|
eos::SupportsPartialDerivative<
|
||||||
|
EquationOfState,
|
||||||
|
eos::Relation<OutputQuantity, InputQuantities...>,
|
||||||
|
InputQuantities>)) &&
|
||||||
|
...)> { };
|
||||||
|
|
||||||
|
template <typename Catalog, typename Formulation, typename EquationOfState>
|
||||||
|
struct MatchingPressureSurfaceRelations;
|
||||||
|
|
||||||
|
template <typename... Relations, typename Formulation, typename EquationOfState>
|
||||||
|
struct MatchingPressureSurfaceRelations<eos::RelationCatalog<Relations...>, Formulation, EquationOfState> {
|
||||||
|
using Tuple = decltype(std::tuple_cat(
|
||||||
|
std::conditional_t<
|
||||||
|
PressureSurfaceRelationMatches<Relations, Formulation, EquationOfState>::value,
|
||||||
|
std::tuple<Relations>,
|
||||||
|
std::tuple<>>{}...
|
||||||
|
));
|
||||||
|
|
||||||
|
static constexpr std::size_t count = std::tuple_size_v<Tuple>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t Count, typename Tuple> struct UniquePressureSurfaceRelation {
|
||||||
|
using Type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Tuple> struct UniquePressureSurfaceRelation<1, Tuple> {
|
||||||
|
using Type = std::tuple_element_t<0, Tuple>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dependencies, typename Field> struct AppendSurfaceDependency;
|
||||||
|
|
||||||
|
template <typename RowField, typename... StateFields, typename Field>
|
||||||
|
struct AppendSurfaceDependency<SurfaceConstraintDependencies<RowField, StateFields...>, Field> {
|
||||||
|
using Type = SurfaceConstraintDependencies<RowField, StateFields..., Field>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dependencies, typename InputQuantity, typename Bindings>
|
||||||
|
struct AppendPressureSurfaceInputDependency {
|
||||||
|
using Type =
|
||||||
|
typename AppendSurfaceDependency<Dependencies, SurfaceFieldForQuantityT<Bindings, InputQuantity>>::Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dependencies, typename Bindings>
|
||||||
|
struct AppendPressureSurfaceInputDependency<Dependencies, eos::quantity::Pressure, Bindings> {
|
||||||
|
using Type = Dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dependencies, typename Bindings, typename... InputQuantities>
|
||||||
|
struct AppendPressureSurfaceInputDependencies;
|
||||||
|
|
||||||
|
template <typename Dependencies, typename Bindings>
|
||||||
|
struct AppendPressureSurfaceInputDependencies<Dependencies, Bindings> {
|
||||||
|
using Type = Dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dependencies, typename Bindings, typename FirstInput, typename... RemainingInputs>
|
||||||
|
struct AppendPressureSurfaceInputDependencies<Dependencies, Bindings, FirstInput, RemainingInputs...> {
|
||||||
|
using WithFirst = typename AppendPressureSurfaceInputDependency<Dependencies, FirstInput, Bindings>::Type;
|
||||||
|
using Type = typename AppendPressureSurfaceInputDependencies<WithFirst, Bindings, RemainingInputs...>::Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename RelationType, typename Formulation> struct PressureSurfaceDependenciesForRelation;
|
||||||
|
|
||||||
|
template <typename OutputQuantity, typename... InputQuantities, typename Formulation>
|
||||||
|
struct PressureSurfaceDependenciesForRelation<eos::Relation<OutputQuantity, InputQuantities...>, Formulation> {
|
||||||
|
using InitialDependencies =
|
||||||
|
SurfaceConstraintDependencies<typename Formulation::CarrierField, typename Formulation::CarrierField>;
|
||||||
|
using Type = typename AppendPressureSurfaceInputDependencies<
|
||||||
|
InitialDependencies,
|
||||||
|
typename Formulation::StateBindings,
|
||||||
|
InputQuantities...>::Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||||
|
struct PressureSurfaceCompilation {
|
||||||
|
using Matches =
|
||||||
|
MatchingPressureSurfaceRelations<typename EquationOfState::Relations, Formulation, EquationOfState>;
|
||||||
|
using Relation = typename UniquePressureSurfaceRelation<Matches::count, typename Matches::Tuple>::Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||||
|
requires(PressureSurfaceCompilation<Formulation, EquationOfState>::Matches::count == 1)
|
||||||
|
struct CompiledPressureSurfaceConstraintType {
|
||||||
|
using Compilation = PressureSurfaceCompilation<Formulation, EquationOfState>;
|
||||||
|
using Relation = typename Compilation::Relation;
|
||||||
|
using Dependencies = typename PressureSurfaceDependenciesForRelation<Relation, Formulation>::Type;
|
||||||
|
using Type = CompiledPressureSurfaceConstraint<EquationOfState, Formulation, Relation, Dependencies>;
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Formulation, typename EquationOfState>
|
||||||
|
concept PressureSurfaceCompilable =
|
||||||
|
SurfaceConstraintFormulationType<Formulation> && eos::EquationOfStateModel<EquationOfState> &&
|
||||||
|
(detail::PressureSurfaceCompilation<std::remove_cvref_t<Formulation>, std::remove_cvref_t<EquationOfState>>::
|
||||||
|
Matches::count == 1);
|
||||||
|
|
||||||
|
template <SurfaceConstraintFormulationType Formulation, eos::EquationOfStateModel EquationOfState>
|
||||||
|
requires PressureSurfaceCompilable<Formulation, EquationOfState>
|
||||||
|
using CompiledPressureSurfaceConstraintT = typename detail::CompiledPressureSurfaceConstraintType<
|
||||||
|
std::remove_cvref_t<Formulation>,
|
||||||
|
std::remove_cvref_t<EquationOfState>>::Type;
|
||||||
|
|
||||||
|
template <
|
||||||
|
SurfaceConstraintFormulationType Formulation,
|
||||||
|
eos::EquationOfStateModel EquationOfState>
|
||||||
|
requires PressureSurfaceCompilable<
|
||||||
|
Formulation,
|
||||||
|
EquationOfState>
|
||||||
|
[[nodiscard]] CompiledPressureSurfaceConstraintT<
|
||||||
|
Formulation,
|
||||||
|
EquationOfState>
|
||||||
|
compilePressureSurfaceConstraint(
|
||||||
|
const ConstantPressureSurface condition,
|
||||||
|
const EquationOfState &equationOfState
|
||||||
|
) noexcept {
|
||||||
|
return CompiledPressureSurfaceConstraintT<Formulation, EquationOfState>{condition, equationOfState};
|
||||||
|
}
|
||||||
|
} // namespace mean_field::surface
|
||||||
65
libmeanfield/interface/surface/constant.cppm
Normal file
65
libmeanfield/interface/surface/constant.cppm
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <format>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
export module mean_field:surface.constant;
|
||||||
|
|
||||||
|
export import :eos.quantities;
|
||||||
|
|
||||||
|
export namespace mean_field::surface {
|
||||||
|
struct PressureSurfaceDescriptor final {
|
||||||
|
double targetPressure;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The only physical surface prescription currently supported by
|
||||||
|
* MeanField. It says nothing about which thermodynamic variable appears
|
||||||
|
* in a nonlinear state vector; resolving pressure into that representation
|
||||||
|
* is an EOS responsibility.
|
||||||
|
*/
|
||||||
|
class ConstantPressureSurface final {
|
||||||
|
public:
|
||||||
|
using PhysicalQuantity = eos::quantity::Pressure;
|
||||||
|
using TargetValue = eos::PressureValue;
|
||||||
|
|
||||||
|
explicit ConstantPressureSurface(const TargetValue targetPressure) : m_targetPressure(targetPressure) {
|
||||||
|
if (!std::isfinite(targetPressure.value())) {
|
||||||
|
throw std::invalid_argument(
|
||||||
|
std::format(
|
||||||
|
"The target surface pressure must be finite. Instead P = {} was provided.",
|
||||||
|
targetPressure.value()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (targetPressure.value() < 0.0) {
|
||||||
|
throw std::invalid_argument(
|
||||||
|
std::format(
|
||||||
|
"The target surface pressure must be non-negative. Instead P = {} was provided.",
|
||||||
|
targetPressure.value()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] TargetValue targetPressure() const noexcept {
|
||||||
|
return m_targetPressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] PressureSurfaceDescriptor descriptor() const noexcept {
|
||||||
|
return PressureSurfaceDescriptor{.targetPressure = m_targetPressure.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TargetValue m_targetPressure;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept ConstantPressureSurfaceType = std::same_as<std::remove_cvref_t<Candidate>, ConstantPressureSurface>;
|
||||||
|
|
||||||
|
// Familiar physical terminology retained as a synonym, not as a second
|
||||||
|
// surface-condition type.
|
||||||
|
using Isobaric = ConstantPressureSurface;
|
||||||
|
} // namespace mean_field::surface
|
||||||
186
libmeanfield/interface/surface/dependencies.cppm
Normal file
186
libmeanfield/interface/surface/dependencies.cppm
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <concepts>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
export module mean_field:surface.dependencies;
|
||||||
|
|
||||||
|
export import :eos.relations;
|
||||||
|
export import :field.registry;
|
||||||
|
|
||||||
|
export namespace mean_field::surface {
|
||||||
|
template <typename Candidate>
|
||||||
|
concept SurfaceFieldType = requires {
|
||||||
|
{ Candidate::name } -> std::convertible_to<std::string_view>;
|
||||||
|
} && (std::string_view{Candidate::name}.size() > 0);
|
||||||
|
|
||||||
|
class SurfaceFieldId final {
|
||||||
|
public:
|
||||||
|
explicit constexpr SurfaceFieldId(const std::string_view name) noexcept : m_name(name) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::string_view name() const noexcept {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator==(
|
||||||
|
const SurfaceFieldId &,
|
||||||
|
const SurfaceFieldId &
|
||||||
|
) noexcept = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string_view m_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <SurfaceFieldType Field> inline constexpr SurfaceFieldId surfaceFieldId{std::string_view{Field::name}};
|
||||||
|
|
||||||
|
template <eos::ThermodynamicQuantityType ThermodynamicQuantity, SurfaceFieldType Field>
|
||||||
|
struct SurfaceStateBinding final {
|
||||||
|
using Quantity = ThermodynamicQuantity;
|
||||||
|
using FieldType = Field;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Bindings> struct SurfaceStateBindings final { };
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename... Types> struct SurfaceTypesAreUnique : std::true_type { };
|
||||||
|
|
||||||
|
template <typename First, typename... Remaining>
|
||||||
|
struct SurfaceTypesAreUnique<First, Remaining...>
|
||||||
|
: std::bool_constant<
|
||||||
|
(!std::same_as<First, Remaining> && ...) && SurfaceTypesAreUnique<Remaining...>::value> { };
|
||||||
|
|
||||||
|
template <typename Bindings> struct SurfaceBindingsAreValid : std::false_type { };
|
||||||
|
|
||||||
|
template <typename... Bindings>
|
||||||
|
struct SurfaceBindingsAreValid<SurfaceStateBindings<Bindings...>>
|
||||||
|
: std::bool_constant<
|
||||||
|
(sizeof...(Bindings) > 0) &&
|
||||||
|
(requires {
|
||||||
|
typename Bindings::Quantity;
|
||||||
|
typename Bindings::FieldType;
|
||||||
|
} && ...) &&
|
||||||
|
(eos::ThermodynamicQuantityType<
|
||||||
|
typename Bindings::Quantity> && ...) &&
|
||||||
|
(SurfaceFieldType<typename Bindings::FieldType> && ...) &&
|
||||||
|
SurfaceTypesAreUnique<
|
||||||
|
typename Bindings::Quantity...>::value> { };
|
||||||
|
|
||||||
|
template <typename Bindings, typename Quantity> struct SurfaceBindingCount;
|
||||||
|
|
||||||
|
template <typename Quantity, typename... Bindings>
|
||||||
|
struct SurfaceBindingCount<SurfaceStateBindings<Bindings...>, Quantity>
|
||||||
|
: std::integral_constant<
|
||||||
|
std::size_t,
|
||||||
|
(std::size_t{0} + ... +
|
||||||
|
(std::same_as<Quantity, typename Bindings::Quantity> ? std::size_t{1} : std::size_t{0}))> {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Bindings, typename Quantity> struct SurfaceFieldForQuantity;
|
||||||
|
|
||||||
|
template <typename Quantity, typename First, typename... Remaining>
|
||||||
|
struct SurfaceFieldForQuantity<SurfaceStateBindings<First, Remaining...>, Quantity>
|
||||||
|
: std::conditional_t<
|
||||||
|
std::same_as<Quantity, typename First::Quantity>,
|
||||||
|
std::type_identity<typename First::FieldType>,
|
||||||
|
SurfaceFieldForQuantity<SurfaceStateBindings<Remaining...>, Quantity>> { };
|
||||||
|
|
||||||
|
template <typename Candidate, std::size_t CarrierBindingCount>
|
||||||
|
struct CarrierFieldMatchesSurfaceBinding : std::false_type { };
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
struct CarrierFieldMatchesSurfaceBinding<Candidate, 1>
|
||||||
|
: std::bool_constant<std::same_as<
|
||||||
|
typename SurfaceFieldForQuantity<
|
||||||
|
typename Candidate::StateBindings,
|
||||||
|
typename Candidate::CarrierQuantity>::type,
|
||||||
|
typename Candidate::CarrierField>> { };
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename Candidate,
|
||||||
|
bool BindingsAreValid = SurfaceBindingsAreValid<typename Candidate::StateBindings>::value>
|
||||||
|
struct FormulationBindingsMatchCarrier : std::false_type { };
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
struct FormulationBindingsMatchCarrier<Candidate, true>
|
||||||
|
: CarrierFieldMatchesSurfaceBinding<
|
||||||
|
Candidate,
|
||||||
|
SurfaceBindingCount<
|
||||||
|
typename Candidate::StateBindings,
|
||||||
|
typename Candidate::CarrierQuantity>::value> { };
|
||||||
|
|
||||||
|
template <typename Candidate, typename = void>
|
||||||
|
struct IsSurfaceConstraintFormulation : std::false_type { };
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
struct IsSurfaceConstraintFormulation<
|
||||||
|
Candidate,
|
||||||
|
std::void_t<
|
||||||
|
typename Candidate::CarrierQuantity,
|
||||||
|
typename Candidate::CarrierField,
|
||||||
|
typename Candidate::StateBindings>>
|
||||||
|
: std::bool_constant<
|
||||||
|
eos::ThermodynamicQuantityType<typename Candidate::CarrierQuantity> &&
|
||||||
|
SurfaceFieldType<typename Candidate::CarrierField> &&
|
||||||
|
FormulationBindingsMatchCarrier<Candidate>::value> { };
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept ValidSurfaceStateBindings = detail::SurfaceBindingsAreValid<std::remove_cv_t<Candidate>>::value;
|
||||||
|
|
||||||
|
template <ValidSurfaceStateBindings Bindings, typename Quantity>
|
||||||
|
inline constexpr std::size_t surfaceBindingCount = detail::SurfaceBindingCount<Bindings, Quantity>::value;
|
||||||
|
|
||||||
|
template <ValidSurfaceStateBindings Bindings, typename Quantity>
|
||||||
|
requires(surfaceBindingCount<Bindings, Quantity> == 1)
|
||||||
|
using SurfaceFieldForQuantityT = typename detail::SurfaceFieldForQuantity<Bindings, Quantity>::type;
|
||||||
|
|
||||||
|
template <
|
||||||
|
eos::ThermodynamicQuantityType CarrierThermodynamicQuantity,
|
||||||
|
SurfaceFieldType CarrierFieldType,
|
||||||
|
ValidSurfaceStateBindings Bindings>
|
||||||
|
requires(
|
||||||
|
surfaceBindingCount<Bindings, CarrierThermodynamicQuantity> == 1 &&
|
||||||
|
std::same_as<SurfaceFieldForQuantityT<Bindings, CarrierThermodynamicQuantity>, CarrierFieldType>
|
||||||
|
)
|
||||||
|
struct SurfaceConstraintFormulation final {
|
||||||
|
using CarrierQuantity = CarrierThermodynamicQuantity;
|
||||||
|
using CarrierField = CarrierFieldType;
|
||||||
|
using StateBindings = Bindings;
|
||||||
|
};
|
||||||
|
|
||||||
|
using BarotropicSurfaceFormulation = SurfaceConstraintFormulation<
|
||||||
|
eos::quantity::SpecificEnthalpy,
|
||||||
|
field::Enthalpy,
|
||||||
|
SurfaceStateBindings<SurfaceStateBinding<eos::quantity::SpecificEnthalpy, field::Enthalpy>>>;
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept SurfaceConstraintFormulationType =
|
||||||
|
detail::IsSurfaceConstraintFormulation<std::remove_cv_t<Candidate>>::value;
|
||||||
|
|
||||||
|
struct RuntimeSurfaceConstraintDependencies final {
|
||||||
|
SurfaceFieldId residualRowField;
|
||||||
|
std::span<const SurfaceFieldId> stateFields;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <SurfaceFieldType ResidualField, SurfaceFieldType... StateFields>
|
||||||
|
struct SurfaceConstraintDependencies final {
|
||||||
|
using RowField = ResidualField;
|
||||||
|
using StateFieldTypes = field::TypeList<StateFields...>;
|
||||||
|
|
||||||
|
inline static constexpr std::array<SurfaceFieldId, sizeof...(StateFields)> runtimeStateFields{
|
||||||
|
surfaceFieldId<StateFields>...
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr RuntimeSurfaceConstraintDependencies runtimeDescription() noexcept {
|
||||||
|
return RuntimeSurfaceConstraintDependencies{
|
||||||
|
.residualRowField = surfaceFieldId<ResidualField>,
|
||||||
|
.stateFields = std::span<const SurfaceFieldId>{runtimeStateFields}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace mean_field::surface
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <format>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
export module mean_field:surface.isobaric;
|
|
||||||
|
|
||||||
export import :surface.base;
|
|
||||||
|
|
||||||
export namespace mean_field::surface {
|
|
||||||
class Isobaric final : public SurfaceBase {
|
|
||||||
public:
|
|
||||||
explicit Isobaric(const double targetPressure = 0.0) : m_targetPressure(targetPressure) {
|
|
||||||
validateTargetPressure();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] double targetPressure() const noexcept {
|
|
||||||
return m_targetPressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] ResolvedSurfaceCondition
|
|
||||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const override {
|
|
||||||
return ResolvedSurfaceCondition{resolveTargetEnthalpy(equationOfState)};
|
|
||||||
}
|
|
||||||
|
|
||||||
void validate(const mean_field::eos::EquationOfState &equationOfState) const override {
|
|
||||||
static_cast<void>(resolveTargetEnthalpy(equationOfState));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
[[nodiscard]] double resolveTargetEnthalpy(const mean_field::eos::EquationOfState &equationOfState) const {
|
|
||||||
validateTargetPressure();
|
|
||||||
|
|
||||||
const double targetEnthalpy = equationOfState.enthalpy_from_pressure(m_targetPressure);
|
|
||||||
|
|
||||||
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
|
|
||||||
throw std::domain_error(
|
|
||||||
std::format(
|
|
||||||
"The equation of state resolved the isobaric "
|
|
||||||
"target P = {} to the invalid enthalpy h = {}.",
|
|
||||||
m_targetPressure, targetEnthalpy
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetEnthalpy;
|
|
||||||
}
|
|
||||||
|
|
||||||
void validateTargetPressure() const {
|
|
||||||
if (!std::isfinite(m_targetPressure) || m_targetPressure < 0.0) {
|
|
||||||
throw std::invalid_argument(
|
|
||||||
std::format(
|
|
||||||
"The target surface pressure must be finite and "
|
|
||||||
"non-negative. Instead P = {} was provided.",
|
|
||||||
m_targetPressure
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double m_targetPressure;
|
|
||||||
};
|
|
||||||
} // namespace mean_field::surface
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
export module mean_field:surface.base;
|
|
||||||
|
|
||||||
export import :eos.base;
|
|
||||||
|
|
||||||
export namespace mean_field::surface {
|
|
||||||
struct ResolvedSurfaceCondition final {
|
|
||||||
double targetEnthalpy{0.0};
|
|
||||||
|
|
||||||
explicit ResolvedSurfaceCondition(const double requestedTargetEnthalpy)
|
|
||||||
: targetEnthalpy(requestedTargetEnthalpy) {
|
|
||||||
if (!std::isfinite(targetEnthalpy) || targetEnthalpy < 0.0) {
|
|
||||||
throw std::invalid_argument(
|
|
||||||
"A resolved surface enthalpy must be finite and "
|
|
||||||
"non-negative."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] double residual(const double enthalpy) const {
|
|
||||||
if (!std::isfinite(enthalpy)) {
|
|
||||||
throw std::invalid_argument("A surface enthalpy value must be finite.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return enthalpy - targetEnthalpy;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static double jacobianAction(const double enthalpyVariation) {
|
|
||||||
if (!std::isfinite(enthalpyVariation)) {
|
|
||||||
throw std::invalid_argument("A surface enthalpy variation must be finite.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return enthalpyVariation;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SurfaceBase {
|
|
||||||
public:
|
|
||||||
virtual ~SurfaceBase() = default;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual ResolvedSurfaceCondition
|
|
||||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const = 0;
|
|
||||||
|
|
||||||
virtual void validate(const mean_field::eos::EquationOfState &equationOfState) const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
SurfaceBase() = default;
|
|
||||||
};
|
|
||||||
} // namespace mean_field::surface
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -11,10 +11,12 @@ export module mean_field:utils.misc;
|
|||||||
import :utils.domain;
|
import :utils.domain;
|
||||||
|
|
||||||
export namespace mean_field::utils {
|
export namespace mean_field::utils {
|
||||||
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
|
constexpr double APPROX_MAX_ACCEPTABLE_POTENTIAL_ERROR_SI_BURNING = 1e-4;
|
||||||
|
|
||||||
bool is_vacuum(const mfem::ElementTransformation &Tr,
|
bool is_vacuum(
|
||||||
mfem::Array<mfem::Vector *> elvec) {
|
const mfem::ElementTransformation &Tr,
|
||||||
|
mfem::Array<mfem::Vector *> elvec
|
||||||
|
) {
|
||||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||||
@@ -27,10 +29,12 @@ bool is_vacuum(const mfem::ElementTransformation &Tr,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_vacuum(const mfem::ElementTransformation &Tr,
|
bool is_vacuum(
|
||||||
const mfem::Array2D<mfem::DenseMatrix *> &elmats) {
|
const mfem::ElementTransformation &Tr,
|
||||||
|
const mfem::Array2D<mfem::DenseMatrix *> &elmats
|
||||||
|
) {
|
||||||
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
using Schema = domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
if (Schema::template attribute_belongs_to<domain::Vacuum>(Tr.Attribute)) {
|
||||||
@@ -46,47 +50,51 @@ bool is_vacuum(const mfem::ElementTransformation &Tr,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::string_view ANSI_GREEN = "\033[32m";
|
constexpr std::string_view ANSI_GREEN = "\033[32m";
|
||||||
constexpr std::string_view ANSI_RED = "\033[31m";
|
constexpr std::string_view ANSI_RED = "\033[31m";
|
||||||
constexpr std::string_view ANSI_YELLOW = "\033[33m";
|
constexpr std::string_view ANSI_YELLOW = "\033[33m";
|
||||||
constexpr std::string_view ANSI_BLUE = "\033[34m";
|
constexpr std::string_view ANSI_BLUE = "\033[34m";
|
||||||
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
|
constexpr std::string_view ANSI_MAGENTA = "\033[35m";
|
||||||
constexpr std::string_view ANSI_CYAN = "\033[36m";
|
constexpr std::string_view ANSI_CYAN = "\033[36m";
|
||||||
constexpr std::string_view ANSI_RESET = "\033[0m";
|
constexpr std::string_view ANSI_RESET = "\033[0m";
|
||||||
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
|
constexpr std::string_view ANSI_BCYAN = "\033[1;36m";
|
||||||
|
|
||||||
constexpr double G = 1.0;
|
constexpr double G = 1.0;
|
||||||
constexpr double MASS = 1.0;
|
constexpr double MASS = 1.0;
|
||||||
constexpr double RADIUS = 1.0;
|
constexpr double RADIUS = 1.0;
|
||||||
|
|
||||||
[[maybe_unused]] constexpr char HOST[10] = "localhost";
|
[[maybe_unused]] constexpr char HOST[10] = "localhost";
|
||||||
[[maybe_unused]] constexpr int PORT = 19916;
|
[[maybe_unused]] constexpr int PORT = 19916;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept is_xad = std::is_same_v<T, xad::AReal<long double>> ||
|
concept is_xad = std::is_same_v<T, xad::AReal<long double>> || std::is_same_v<T, xad::AReal<double>> ||
|
||||||
std::is_same_v<T, xad::AReal<double>> ||
|
|
||||||
std::is_same_v<T, xad::AReal<float>>;
|
std::is_same_v<T, xad::AReal<float>>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
|
concept is_real = std::is_floating_point_v<T> || is_xad<T>;
|
||||||
|
|
||||||
template <is_real T>
|
template <is_real T> using EOS_P = std::function<T(const T &rho, const T &temp)>;
|
||||||
using EOS_P = std::function<T(const T &rho, const T &temp)>;
|
|
||||||
|
|
||||||
enum class DOMAINS : uint8_t {
|
enum class DOMAINS : uint8_t {
|
||||||
CORE = 1 << 0,
|
CORE = 1 << 0,
|
||||||
ENVELOPE = 1 << 1,
|
ENVELOPE = 1 << 1,
|
||||||
VACUUM = 1 << 2,
|
VACUUM = 1 << 2,
|
||||||
STELLAR = CORE | ENVELOPE,
|
STELLAR = CORE | ENVELOPE,
|
||||||
ALL = CORE | ENVELOPE | VACUUM
|
ALL = CORE | ENVELOPE | VACUUM
|
||||||
};
|
};
|
||||||
|
|
||||||
DOMAINS operator|(DOMAINS lhs, DOMAINS rhs);
|
DOMAINS operator|(
|
||||||
|
DOMAINS lhs,
|
||||||
|
DOMAINS rhs
|
||||||
|
);
|
||||||
|
|
||||||
DOMAINS operator&(DOMAINS lhs, DOMAINS rhs);
|
DOMAINS operator&(
|
||||||
|
DOMAINS lhs,
|
||||||
|
DOMAINS rhs
|
||||||
|
);
|
||||||
|
|
||||||
int get_mesh_order(const mfem::Mesh &mesh);
|
int get_mesh_order(const mfem::Mesh &mesh);
|
||||||
|
|
||||||
} // namespace mean_field::utils
|
} // namespace mean_field::utils
|
||||||
|
|||||||
@@ -94,6 +94,65 @@ namespace field_dof_map_test_utils {
|
|||||||
domain::RelationList<>>;
|
domain::RelationList<>>;
|
||||||
} // namespace field_dof_map_test_utils
|
} // namespace field_dof_map_test_utils
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Field Boundary DOF Map Selects The Stellar Surface In Reduced Field Ordering",
|
||||||
|
tags::surface_boundary_dof_topology
|
||||||
|
) {
|
||||||
|
namespace domain = mean_field::utils::domain;
|
||||||
|
namespace field = mean_field::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);
|
||||||
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
|
const field::FieldDofMap enthalpyMap =
|
||||||
|
field::make_field_dof_map<field::Enthalpy, field_dof_map_test_utils::Schema>(*f.enthalpyFes);
|
||||||
|
const field::FieldBoundaryDofMap stellarSurface =
|
||||||
|
field::make_field_boundary_dof_map<field::Enthalpy, domain::StellarSurface, field_dof_map_test_utils::Schema>(
|
||||||
|
*f.enthalpyFes, enthalpyMap
|
||||||
|
);
|
||||||
|
CHECK(stellarSurface.field_size() == enthalpyMap.reduced_size());
|
||||||
|
CHECK(field_dof_map_test_utils::global_sum(stellarSurface.size()) > 0);
|
||||||
|
CHECK(
|
||||||
|
field_dof_map_test_utils::global_sum(stellarSurface.size()) <
|
||||||
|
field_dof_map_test_utils::global_sum(enthalpyMap.reduced_size())
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const int reducedDof : stellarSurface.reduced_dofs()) {
|
||||||
|
CAPTURE(reducedDof);
|
||||||
|
CHECK(stellarSurface.contains(reducedDof));
|
||||||
|
CHECK(enthalpyMap.contains_true_dof(enthalpyMap.true_dof(reducedDof)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Field Point DOF Map Selects One Vector Vertex At The Computational Origin",
|
||||||
|
tags::translational_centering_topology
|
||||||
|
) {
|
||||||
|
namespace field = mean_field::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);
|
||||||
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
|
const field::FieldDofMap displacementMap =
|
||||||
|
field::make_field_dof_map<field::Displacement, field_dof_map_test_utils::Schema>(*f.displacementFes);
|
||||||
|
mfem::Vector origin(f.mesh->SpaceDimension());
|
||||||
|
origin = 0.0;
|
||||||
|
|
||||||
|
const field::FieldPointDofMap centerRows =
|
||||||
|
field::make_field_point_dof_map<field::Displacement>(*f.displacementFes, displacementMap, origin, 1.0e-12);
|
||||||
|
|
||||||
|
CHECK(centerRows.field_size() == displacementMap.reduced_size());
|
||||||
|
CHECK(field_dof_map_test_utils::global_sum(centerRows.size()) == f.mesh->SpaceDimension());
|
||||||
|
|
||||||
|
for (const int reducedDof : centerRows.reduced_dofs()) {
|
||||||
|
CAPTURE(reducedDof);
|
||||||
|
CHECK(centerRows.contains(reducedDof));
|
||||||
|
CHECK(displacementMap.contains_true_dof(displacementMap.true_dof(reducedDof)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Field DOF Map Preserves Canonical Bidirectional Indexing",
|
"Field DOF Map Preserves Canonical Bidirectional Indexing",
|
||||||
tags::field_dof_unit
|
tags::field_dof_unit
|
||||||
@@ -523,9 +582,7 @@ TEST_CASE(
|
|||||||
|
|
||||||
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::Displacement>);
|
STATIC_REQUIRE(field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::Displacement>);
|
||||||
|
|
||||||
STATIC_REQUIRE_FALSE(
|
STATIC_REQUIRE_FALSE(field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::BarotropicConstant>);
|
||||||
field_dof_map_test_utils::CanMakeFieldDofGridFunctionAdapter<field::BarotropicConstant>
|
|
||||||
);
|
|
||||||
|
|
||||||
CHECK(true);
|
CHECK(true);
|
||||||
}
|
}
|
||||||
@@ -751,8 +808,7 @@ TEST_CASE(
|
|||||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||||
|
|
||||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||||
auto finiteElementSpace =
|
auto finiteElementSpace = field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
|
||||||
field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
|
|
||||||
|
|
||||||
REQUIRE(finiteElementSpace != nullptr);
|
REQUIRE(finiteElementSpace != nullptr);
|
||||||
|
|
||||||
@@ -797,8 +853,7 @@ TEST_CASE(
|
|||||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||||
|
|
||||||
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
auto fec = field::Field<field::Enthalpy>::make_fec<field::Enthalpy::Scalar>(2);
|
||||||
auto finiteElementSpace =
|
auto finiteElementSpace = field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *fec);
|
||||||
field::Field<field::Enthalpy>::make_fespace<field::Enthalpy::Scalar>(mesh, *fec);
|
|
||||||
|
|
||||||
REQUIRE(finiteElementSpace != nullptr);
|
REQUIRE(finiteElementSpace != nullptr);
|
||||||
|
|
||||||
@@ -850,8 +905,7 @@ TEST_CASE(
|
|||||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||||
|
|
||||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||||
auto finiteElementSpace =
|
auto finiteElementSpace = field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
|
||||||
field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
|
|
||||||
|
|
||||||
REQUIRE(finiteElementSpace != nullptr);
|
REQUIRE(finiteElementSpace != nullptr);
|
||||||
|
|
||||||
@@ -901,8 +955,7 @@ TEST_CASE(
|
|||||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||||
|
|
||||||
auto fec = field::Field<field::Displacement>::make_fec<field::Displacement::Vector>(2);
|
auto fec = field::Field<field::Displacement>::make_fec<field::Displacement::Vector>(2);
|
||||||
auto finiteElementSpace =
|
auto finiteElementSpace = field::Field<field::Displacement>::make_fespace<field::Displacement::Vector>(mesh, *fec);
|
||||||
field::Field<field::Displacement>::make_fespace<field::Displacement::Vector>(mesh, *fec);
|
|
||||||
|
|
||||||
REQUIRE(finiteElementSpace != nullptr);
|
REQUIRE(finiteElementSpace != nullptr);
|
||||||
|
|
||||||
@@ -940,12 +993,10 @@ TEST_CASE(
|
|||||||
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
mfem::ParMesh mesh(MPI_COMM_WORLD, serialMesh);
|
||||||
|
|
||||||
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
auto fec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||||
auto finiteElementSpace =
|
auto finiteElementSpace = field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
|
||||||
field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *fec);
|
|
||||||
|
|
||||||
auto otherFec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
auto otherFec = field::Field<field::Density>::make_fec<field::Density::Scalar>(2);
|
||||||
auto otherFiniteElementSpace =
|
auto otherFiniteElementSpace = field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *otherFec);
|
||||||
field::Field<field::Density>::make_fespace<field::Density::Scalar>(mesh, *otherFec);
|
|
||||||
|
|
||||||
REQUIRE(finiteElementSpace != nullptr);
|
REQUIRE(finiteElementSpace != nullptr);
|
||||||
REQUIRE(otherFiniteElementSpace != nullptr);
|
REQUIRE(otherFiniteElementSpace != nullptr);
|
||||||
@@ -959,8 +1010,7 @@ TEST_CASE(
|
|||||||
const mfem::Array<int> empty;
|
const mfem::Array<int> empty;
|
||||||
CHECK_THROWS_AS(
|
CHECK_THROWS_AS(
|
||||||
(field::FieldDofGridFunctionAdapter(
|
(field::FieldDofGridFunctionAdapter(
|
||||||
field::FieldDofMap(finiteElementSpace->GetTrueVSize() + 1, empty),
|
field::FieldDofMap(finiteElementSpace->GetTrueVSize() + 1, empty), *finiteElementSpace
|
||||||
*finiteElementSpace
|
|
||||||
)),
|
)),
|
||||||
std::invalid_argument
|
std::invalid_argument
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ using namespace mean_field;
|
|||||||
namespace {
|
namespace {
|
||||||
struct SerialMappingData {
|
struct SerialMappingData {
|
||||||
explicit SerialMappingData(mfem::Mesh &mesh)
|
explicit SerialMappingData(mfem::Mesh &mesh)
|
||||||
: compactification_fes(&mesh, &compactification_fec),
|
: compactification_fes(
|
||||||
|
&mesh,
|
||||||
|
&compactification_fec
|
||||||
|
),
|
||||||
compactification_coordinate(&compactification_fes),
|
compactification_coordinate(&compactification_fes),
|
||||||
mapper(field_dof_test_utils::make_domain_mapper()) {
|
mapper(field_dof_test_utils::make_domain_mapper()) {
|
||||||
compactification_coordinate = 0.0;
|
compactification_coordinate = 0.0;
|
||||||
|
|||||||
@@ -129,8 +129,7 @@ TEST_CASE(
|
|||||||
element_residual[displacement_block] = &displacement_residual;
|
element_residual[displacement_block] = &displacement_residual;
|
||||||
|
|
||||||
integrators::GravityMomentumIntegrator integrator(
|
integrators::GravityMomentumIntegrator integrator(
|
||||||
domain_mapper, displacement, compactification_coordinate,
|
domain_mapper, displacement, compactification_coordinate, integrators::GravityForceJacobianMode::field_coupled
|
||||||
integrators::GravityForceJacobianMode::field_coupled
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const int maximum_order = std::max(
|
const int maximum_order = std::max(
|
||||||
@@ -300,9 +299,7 @@ TEST_CASE(
|
|||||||
compactification_coordinate = 0.0;
|
compactification_coordinate = 0.0;
|
||||||
mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
|
mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
|
||||||
|
|
||||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
mapping::GridFunctionMappingEvaluator mapping_evaluator(domain_mapper, displacement, compactification_coordinate);
|
||||||
domain_mapper, displacement, compactification_coordinate
|
|
||||||
);
|
|
||||||
|
|
||||||
auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); };
|
auto reference_density = [](const mfem::Vector &x) { return 1.0 + x(0); };
|
||||||
|
|
||||||
@@ -381,8 +378,7 @@ TEST_CASE(
|
|||||||
element_residual[displacement_block] = &displacement_residual;
|
element_residual[displacement_block] = &displacement_residual;
|
||||||
|
|
||||||
integrators::GravityMomentumIntegrator integrator(
|
integrators::GravityMomentumIntegrator integrator(
|
||||||
domain_mapper, displacement, compactification_coordinate,
|
domain_mapper, displacement, compactification_coordinate, integrators::GravityForceJacobianMode::field_coupled
|
||||||
integrators::GravityForceJacobianMode::field_coupled
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||||
@@ -473,9 +469,7 @@ TEST_CASE(
|
|||||||
mfem::GridFunction compactification_coordinate(&compactification_fes);
|
mfem::GridFunction compactification_coordinate(&compactification_fes);
|
||||||
compactification_coordinate = 0.0;
|
compactification_coordinate = 0.0;
|
||||||
mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
|
mapping::DomainMapper domain_mapper = field_dof_test_utils::make_domain_mapper();
|
||||||
mapping::GridFunctionMappingEvaluator mapping_evaluator(
|
mapping::GridFunctionMappingEvaluator mapping_evaluator(domain_mapper, displacement, compactification_coordinate);
|
||||||
domain_mapper, displacement, compactification_coordinate
|
|
||||||
);
|
|
||||||
|
|
||||||
auto radial_gravity = [](const mfem::Vector &x, mfem::Vector &gravity) {
|
auto radial_gravity = [](const mfem::Vector &x, mfem::Vector &gravity) {
|
||||||
gravity.SetSize(3);
|
gravity.SetSize(3);
|
||||||
@@ -553,8 +547,7 @@ TEST_CASE(
|
|||||||
element_residual[displacement_block] = &displacement_residual;
|
element_residual[displacement_block] = &displacement_residual;
|
||||||
|
|
||||||
integrators::GravityMomentumIntegrator integrator(
|
integrators::GravityMomentumIntegrator integrator(
|
||||||
domain_mapper, displacement, compactification_coordinate,
|
domain_mapper, displacement, compactification_coordinate, integrators::GravityForceJacobianMode::field_coupled
|
||||||
integrators::GravityForceJacobianMode::field_coupled
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
const mfem::IntegrationRule &integration_rule = mfem::IntRules.Get(velocity_element->GetGeomType(), 8);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
|||||||
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <concepts>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@@ -12,17 +14,11 @@ import test_helpers;
|
|||||||
namespace {
|
namespace {
|
||||||
struct StellarModelExtensionTracker final {
|
struct StellarModelExtensionTracker final {
|
||||||
int structureValidationCount{0};
|
int structureValidationCount{0};
|
||||||
int surfaceValidationCount{0};
|
|
||||||
int surfaceResolutionCount{0};
|
|
||||||
|
|
||||||
const mean_field::eos::EquationOfState *structureEquationOfState{nullptr};
|
const mean_field::eos::Polytrope *structureEquationOfState{nullptr};
|
||||||
|
|
||||||
const mean_field::eos::EquationOfState *surfaceValidationEquationOfState{nullptr};
|
|
||||||
|
|
||||||
const mean_field::eos::EquationOfState *surfaceResolutionEquationOfState{nullptr};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class StellarModelTestStructure final : public mean_field::models::structure::StructureBase {
|
class StellarModelTestStructure final {
|
||||||
public:
|
public:
|
||||||
explicit StellarModelTestStructure(std::shared_ptr<StellarModelExtensionTracker> tracker)
|
explicit StellarModelTestStructure(std::shared_ptr<StellarModelExtensionTracker> tracker)
|
||||||
: m_tracker(std::move(tracker)),
|
: m_tracker(std::move(tracker)),
|
||||||
@@ -32,17 +28,17 @@ namespace {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const mean_field::eos::EquationOfState &equationOfState() const noexcept override {
|
[[nodiscard]] const mean_field::eos::Polytrope &equationOfState() const noexcept {
|
||||||
m_tracker->structureEquationOfState = &m_equationOfState;
|
m_tracker->structureEquationOfState = &m_equationOfState;
|
||||||
return m_equationOfState;
|
return m_equationOfState;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double targetMass() const noexcept override {
|
[[nodiscard]] double targetMass() const noexcept {
|
||||||
return 2.5;
|
return 2.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mean_field::models::structure::StructureSeed
|
[[nodiscard]] mean_field::models::structure::StructureSeed
|
||||||
makeInitialSeed(const mean_field::models::structure::StructureSeedRequest &request) const override {
|
makeInitialSeed(const mean_field::models::structure::StructureSeedRequest &request) const {
|
||||||
mean_field::models::structure::StructureSeed seed;
|
mean_field::models::structure::StructureSeed seed;
|
||||||
|
|
||||||
seed.radius.SetSize(2);
|
seed.radius.SetSize(2);
|
||||||
@@ -65,7 +61,7 @@ namespace {
|
|||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate() const override {
|
void validate() const {
|
||||||
++m_tracker->structureValidationCount;
|
++m_tracker->structureValidationCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,59 +70,85 @@ namespace {
|
|||||||
mean_field::eos::Polytrope m_equationOfState;
|
mean_field::eos::Polytrope m_equationOfState;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StellarModelTestSurface final : public mean_field::surface::SurfaceBase {
|
class StructureWithoutSeed final {
|
||||||
public:
|
public:
|
||||||
explicit StellarModelTestSurface(std::shared_ptr<StellarModelExtensionTracker> tracker)
|
[[nodiscard]] const mean_field::eos::Polytrope &equationOfState() const noexcept;
|
||||||
: m_tracker(std::move(tracker)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]] double targetMass() const noexcept;
|
||||||
mean_field::surface::ResolvedSurfaceCondition
|
|
||||||
resolve(const mean_field::eos::EquationOfState &equationOfState) const override {
|
|
||||||
++m_tracker->surfaceResolutionCount;
|
|
||||||
|
|
||||||
m_tracker->surfaceResolutionEquationOfState = &equationOfState;
|
void validate() const;
|
||||||
|
|
||||||
return mean_field::surface::ResolvedSurfaceCondition{0.375};
|
|
||||||
}
|
|
||||||
|
|
||||||
void validate(const mean_field::eos::EquationOfState &equationOfState) const override {
|
|
||||||
++m_tracker->surfaceValidationCount;
|
|
||||||
|
|
||||||
m_tracker->surfaceValidationEquationOfState = &equationOfState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<StellarModelExtensionTracker> m_tracker;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SurfaceWithoutPhysicalQuantity final { };
|
||||||
|
|
||||||
|
struct ModelSurfaceState final {
|
||||||
|
double specificEnthalpy;
|
||||||
|
|
||||||
|
[[nodiscard]] mean_field::eos::SpecificEnthalpyValue
|
||||||
|
value(mean_field::eos::quantity::SpecificEnthalpy) const noexcept {
|
||||||
|
return mean_field::eos::SpecificEnthalpyValue{specificEnthalpy};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using PolytropicStellarModel = mean_field::models::StellarModel<mean_field::models::structure::PolytropicStructure>;
|
||||||
|
|
||||||
|
using ExtensionStellarModel = mean_field::models::StellarModel<StellarModelTestStructure>;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Stellar Model Owns Structure And Surface Prescriptions",
|
"Stellar Model Owns Structure And Surface Prescriptions",
|
||||||
tags::barotrope &tags::unit &tags::model
|
tags::stellar_model_type_contract
|
||||||
) {
|
) {
|
||||||
STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<mean_field::models::StellarModel>);
|
STATIC_CHECK(mean_field::models::StructurePrescription<mean_field::models::structure::PolytropicStructure>);
|
||||||
|
STATIC_CHECK(mean_field::models::StructurePrescription<StellarModelTestStructure>);
|
||||||
|
STATIC_CHECK_FALSE(mean_field::models::StructurePrescription<StructureWithoutSeed>);
|
||||||
|
|
||||||
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<mean_field::models::StellarModel>);
|
STATIC_CHECK(
|
||||||
|
mean_field::models::SurfacePrescription<
|
||||||
|
mean_field::surface::ConstantPressureSurface, mean_field::eos::Polytrope>
|
||||||
|
);
|
||||||
|
STATIC_CHECK_FALSE(
|
||||||
|
mean_field::models::SurfacePrescription<SurfaceWithoutPhysicalQuantity, mean_field::eos::Polytrope>
|
||||||
|
);
|
||||||
|
|
||||||
STATIC_REQUIRE(std::is_nothrow_move_constructible_v<mean_field::models::StellarModel>);
|
STATIC_CHECK_FALSE(std::derived_from<StellarModelTestStructure, mean_field::models::structure::StructureBase>);
|
||||||
|
STATIC_CHECK_FALSE(
|
||||||
|
std::derived_from<
|
||||||
|
mean_field::models::structure::PolytropicStructure, mean_field::models::structure::StructureBase>
|
||||||
|
);
|
||||||
|
STATIC_CHECK(
|
||||||
|
std::same_as<
|
||||||
|
decltype(std::declval<const mean_field::models::structure::StructureBase &>().equationOfState()),
|
||||||
|
mean_field::eos::EquationOfStateView>
|
||||||
|
);
|
||||||
|
|
||||||
STATIC_REQUIRE(std::is_nothrow_move_assignable_v<mean_field::models::StellarModel>);
|
STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<PolytropicStellarModel>);
|
||||||
|
|
||||||
|
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<PolytropicStellarModel>);
|
||||||
|
|
||||||
|
STATIC_REQUIRE(std::is_nothrow_move_constructible_v<PolytropicStellarModel>);
|
||||||
|
|
||||||
|
STATIC_REQUIRE(std::is_nothrow_move_assignable_v<PolytropicStellarModel>);
|
||||||
|
|
||||||
mean_field::models::StellarModel model{
|
mean_field::models::StellarModel model{
|
||||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||||
mean_field::surface::Isobaric{0.0}
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.0}}
|
||||||
};
|
};
|
||||||
|
|
||||||
CHECK(model.targetMass() == 1.0);
|
STATIC_CHECK(std::same_as<decltype(model), PolytropicStellarModel>);
|
||||||
CHECK(model.resolvedSurfaceCondition().targetEnthalpy == 0.0);
|
STATIC_CHECK(
|
||||||
|
std::same_as<
|
||||||
CHECK(
|
decltype(model.structurePrescription()), const mean_field::models::structure::PolytropicStructure &>
|
||||||
dynamic_cast<const mean_field::models::structure::PolytropicStructure *>(&model.structurePrescription()) !=
|
|
||||||
nullptr
|
|
||||||
);
|
);
|
||||||
|
STATIC_CHECK(
|
||||||
|
std::same_as<decltype(model.surfacePrescription()), const mean_field::surface::ConstantPressureSurface &>
|
||||||
|
);
|
||||||
|
STATIC_CHECK(std::same_as<decltype(model.equationOfState()), const mean_field::eos::Polytrope &>);
|
||||||
|
|
||||||
CHECK(dynamic_cast<const mean_field::surface::Isobaric *>(&model.surfacePrescription()) != nullptr);
|
CHECK(model.targetMass() == 1.0);
|
||||||
|
CHECK(model.compiledSurfaceConstraint().targetPressure() == mean_field::eos::PressureValue{0.0});
|
||||||
|
CHECK(&model.equationOfState() == &model.structurePrescription().equationOfState());
|
||||||
|
CHECK(model.surfacePrescription().targetPressure() == mean_field::eos::PressureValue{0.0});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
@@ -135,7 +157,7 @@ TEST_CASE(
|
|||||||
) {
|
) {
|
||||||
mean_field::models::StellarModel model{
|
mean_field::models::StellarModel model{
|
||||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||||
mean_field::surface::Isobaric{}
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.0}}
|
||||||
};
|
};
|
||||||
|
|
||||||
const mean_field::models::structure::StructureSeed seed =
|
const mean_field::models::structure::StructureSeed seed =
|
||||||
@@ -153,45 +175,49 @@ TEST_CASE(
|
|||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Moving A Stellar Model Preserves Stable Prescription Addresses",
|
"Moving A Stellar Model Preserves Stable Prescription Addresses",
|
||||||
tags::barotrope &tags::unit &tags::model
|
tags::barotrope &tags::unit &tags::model &tags::surface_constraint_lifetime
|
||||||
) {
|
) {
|
||||||
mean_field::models::StellarModel originalModel{
|
mean_field::models::StellarModel originalModel{
|
||||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||||
mean_field::surface::Isobaric{}
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.0}}
|
||||||
};
|
};
|
||||||
|
|
||||||
const mean_field::models::structure::StructureBase *structureAddress = &originalModel.structurePrescription();
|
const mean_field::models::structure::PolytropicStructure *structureAddress = &originalModel.structurePrescription();
|
||||||
|
|
||||||
const mean_field::surface::SurfaceBase *surfaceAddress = &originalModel.surfacePrescription();
|
const mean_field::surface::ConstantPressureSurface *surfaceAddress = &originalModel.surfacePrescription();
|
||||||
|
|
||||||
const mean_field::eos::EquationOfState *equationOfStateAddress = &originalModel.equationOfState();
|
const mean_field::eos::Polytrope *equationOfStateAddress = &originalModel.equationOfState();
|
||||||
|
|
||||||
|
const auto *compiledSurfaceConstraintAddress = &originalModel.compiledSurfaceConstraint();
|
||||||
|
|
||||||
mean_field::models::StellarModel movedModel{std::move(originalModel)};
|
mean_field::models::StellarModel movedModel{std::move(originalModel)};
|
||||||
|
|
||||||
CHECK(&movedModel.structurePrescription() == structureAddress);
|
CHECK(&movedModel.structurePrescription() == structureAddress);
|
||||||
CHECK(&movedModel.surfacePrescription() == surfaceAddress);
|
CHECK(&movedModel.surfacePrescription() == surfaceAddress);
|
||||||
CHECK(&movedModel.equationOfState() == equationOfStateAddress);
|
CHECK(&movedModel.equationOfState() == equationOfStateAddress);
|
||||||
|
CHECK(&movedModel.compiledSurfaceConstraint() == compiledSurfaceConstraintAddress);
|
||||||
CHECK(movedModel.targetMass() == 1.0);
|
CHECK(movedModel.targetMass() == 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Stellar Model Resolves A Positive Isobaric Surface",
|
"Stellar Model Compiles A Positive Constant Pressure Surface",
|
||||||
tags::barotrope &tags::unit &tags::model
|
tags::barotrope &tags::unit &tags::model
|
||||||
) {
|
) {
|
||||||
constexpr double targetPressure = 0.03125;
|
constexpr double targetPressure = 0.03125;
|
||||||
|
|
||||||
mean_field::models::StellarModel model{
|
mean_field::models::StellarModel model{
|
||||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||||
mean_field::surface::Isobaric{targetPressure}
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{targetPressure}}
|
||||||
};
|
};
|
||||||
|
|
||||||
const double targetEnthalpy = model.resolvedSurfaceCondition().targetEnthalpy;
|
const double requiredSpecificEnthalpy = mean_field::eos::evaluate<mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
model.equationOfState(), mean_field::eos::PressureValue{targetPressure}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
CHECK(targetEnthalpy > 0.0);
|
CHECK(requiredSpecificEnthalpy > 0.0);
|
||||||
CHECK(
|
CHECK(model.compiledSurfaceConstraint().targetPressure() == mean_field::eos::PressureValue{targetPressure});
|
||||||
std::abs(model.equationOfState().pressure_from_enthalpy(targetEnthalpy) - targetPressure) <
|
CHECK(model.compiledSurfaceConstraint().residual(ModelSurfaceState{requiredSpecificEnthalpy}) == 0.0);
|
||||||
64.0 * std::numeric_limits<double>::epsilon()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
@@ -200,27 +226,22 @@ TEST_CASE(
|
|||||||
) {
|
) {
|
||||||
const auto tracker = std::make_shared<StellarModelExtensionTracker>();
|
const auto tracker = std::make_shared<StellarModelExtensionTracker>();
|
||||||
|
|
||||||
mean_field::models::StellarModel model{StellarModelTestStructure{tracker}, StellarModelTestSurface{tracker}};
|
mean_field::models::StellarModel model{
|
||||||
|
StellarModelTestStructure{tracker},
|
||||||
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.375}}
|
||||||
|
};
|
||||||
|
|
||||||
|
STATIC_CHECK(std::same_as<decltype(model), ExtensionStellarModel>);
|
||||||
|
|
||||||
REQUIRE(tracker->structureValidationCount == 1);
|
REQUIRE(tracker->structureValidationCount == 1);
|
||||||
REQUIRE(tracker->surfaceValidationCount == 1);
|
|
||||||
REQUIRE(tracker->surfaceResolutionCount == 1);
|
|
||||||
|
|
||||||
CHECK(dynamic_cast<const StellarModelTestStructure *>(&model.structurePrescription()) != nullptr);
|
const mean_field::eos::Polytrope *ownedEquationOfState = &model.equationOfState();
|
||||||
|
|
||||||
CHECK(dynamic_cast<const StellarModelTestSurface *>(&model.surfacePrescription()) != nullptr);
|
|
||||||
|
|
||||||
const mean_field::eos::EquationOfState *ownedEquationOfState = &model.equationOfState();
|
|
||||||
|
|
||||||
CHECK(tracker->structureEquationOfState == ownedEquationOfState);
|
CHECK(tracker->structureEquationOfState == ownedEquationOfState);
|
||||||
|
|
||||||
CHECK(tracker->surfaceValidationEquationOfState == ownedEquationOfState);
|
|
||||||
|
|
||||||
CHECK(tracker->surfaceResolutionEquationOfState == ownedEquationOfState);
|
|
||||||
|
|
||||||
CHECK(model.targetMass() == 2.5);
|
CHECK(model.targetMass() == 2.5);
|
||||||
|
|
||||||
CHECK(model.resolvedSurfaceCondition().targetEnthalpy == 0.375);
|
CHECK(model.compiledSurfaceConstraint().targetPressure() == mean_field::eos::PressureValue{0.375});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
@@ -229,21 +250,23 @@ TEST_CASE(
|
|||||||
) {
|
) {
|
||||||
mean_field::models::StellarModel sourceModel{
|
mean_field::models::StellarModel sourceModel{
|
||||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.25},
|
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.25},
|
||||||
mean_field::surface::Isobaric{0.0}
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.0}}
|
||||||
};
|
};
|
||||||
|
|
||||||
mean_field::models::StellarModel destinationModel{
|
mean_field::models::StellarModel destinationModel{
|
||||||
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{2.0, 0.5}, 4.0},
|
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{2.0, 0.5}, 4.0},
|
||||||
mean_field::surface::Isobaric{0.02}
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.02}}
|
||||||
};
|
};
|
||||||
|
|
||||||
const mean_field::models::structure::StructureBase *sourceStructureAddress = &sourceModel.structurePrescription();
|
const mean_field::models::structure::PolytropicStructure *sourceStructureAddress =
|
||||||
|
&sourceModel.structurePrescription();
|
||||||
|
|
||||||
const mean_field::surface::SurfaceBase *sourceSurfaceAddress = &sourceModel.surfacePrescription();
|
const mean_field::surface::ConstantPressureSurface *sourceSurfaceAddress = &sourceModel.surfacePrescription();
|
||||||
|
|
||||||
const mean_field::eos::EquationOfState *sourceEquationOfStateAddress = &sourceModel.equationOfState();
|
const mean_field::eos::Polytrope *sourceEquationOfStateAddress = &sourceModel.equationOfState();
|
||||||
|
|
||||||
const double sourceTargetEnthalpy = sourceModel.resolvedSurfaceCondition().targetEnthalpy;
|
const mean_field::eos::PressureValue sourceTargetPressure =
|
||||||
|
sourceModel.compiledSurfaceConstraint().targetPressure();
|
||||||
|
|
||||||
destinationModel = std::move(sourceModel);
|
destinationModel = std::move(sourceModel);
|
||||||
|
|
||||||
@@ -255,5 +278,83 @@ TEST_CASE(
|
|||||||
|
|
||||||
CHECK(destinationModel.targetMass() == 1.25);
|
CHECK(destinationModel.targetMass() == 1.25);
|
||||||
|
|
||||||
CHECK(destinationModel.resolvedSurfaceCondition().targetEnthalpy == sourceTargetEnthalpy);
|
CHECK(destinationModel.compiledSurfaceConstraint().targetPressure() == sourceTargetPressure);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Stellar Model View Supports Heterogeneous Typed Models",
|
||||||
|
tags::stellar_model_runtime_view
|
||||||
|
) {
|
||||||
|
const auto tracker = std::make_shared<StellarModelExtensionTracker>();
|
||||||
|
|
||||||
|
const mean_field::models::StellarModel polytropicModel{
|
||||||
|
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||||
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.0}}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mean_field::models::StellarModel extensionModel{
|
||||||
|
StellarModelTestStructure{tracker},
|
||||||
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.375}}
|
||||||
|
};
|
||||||
|
|
||||||
|
STATIC_CHECK(std::is_trivially_copyable_v<mean_field::models::StellarModelView>);
|
||||||
|
STATIC_CHECK_FALSE(std::constructible_from<mean_field::models::StellarModelView, PolytropicStellarModel &&>);
|
||||||
|
|
||||||
|
const std::array views{
|
||||||
|
mean_field::models::StellarModelView{polytropicModel}, mean_field::models::StellarModelView{extensionModel}
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK(views[0].targetMass() == 1.0);
|
||||||
|
CHECK(views[1].targetMass() == 2.5);
|
||||||
|
CHECK(views[1].surfaceCondition().targetPressure == 0.375);
|
||||||
|
REQUIRE(views[1].surfaceDependencies().stateFields.size() == 1);
|
||||||
|
CHECK(
|
||||||
|
views[1].surfaceDependencies().residualRowField ==
|
||||||
|
mean_field::surface::surfaceFieldId<mean_field::field::Enthalpy>
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto pressure =
|
||||||
|
views[0].equationOfState().tryEvaluate<mean_field::eos::quantity::Pressure>(mean_field::eos::DensityValue{0.7});
|
||||||
|
|
||||||
|
REQUIRE(pressure.has_value());
|
||||||
|
CHECK(
|
||||||
|
pressure->value() == mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(
|
||||||
|
polytropicModel.equationOfState(), mean_field::eos::DensityValue{0.7}
|
||||||
|
)
|
||||||
|
.value()
|
||||||
|
);
|
||||||
|
|
||||||
|
const mean_field::models::structure::StructureSeed seed =
|
||||||
|
views[1].makeInitialSeed({.centralDensity = 1.75, .radialSampleCount = 2});
|
||||||
|
|
||||||
|
CHECK(seed.centralDensity == 1.75);
|
||||||
|
CHECK(seed.radius.Size() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Stellar Model View Retains Stable Pointees When Its Owner Moves",
|
||||||
|
tags::stellar_model_runtime_view
|
||||||
|
) {
|
||||||
|
mean_field::models::StellarModel originalModel{
|
||||||
|
mean_field::models::structure::PolytropicStructure{mean_field::eos::Polytrope{3.0, 0.25}, 1.0},
|
||||||
|
mean_field::surface::ConstantPressureSurface{mean_field::eos::PressureValue{0.0}}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mean_field::models::StellarModelView view{originalModel};
|
||||||
|
PolytropicStellarModel movedModel{std::move(originalModel)};
|
||||||
|
|
||||||
|
const auto pressure =
|
||||||
|
view.equationOfState().tryEvaluate<mean_field::eos::quantity::Pressure>(mean_field::eos::DensityValue{0.7});
|
||||||
|
const mean_field::models::structure::StructureSeed seed =
|
||||||
|
view.makeInitialSeed({.centralDensity = 1.0, .radialSampleCount = 8});
|
||||||
|
|
||||||
|
REQUIRE(pressure.has_value());
|
||||||
|
CHECK(view.targetMass() == movedModel.targetMass());
|
||||||
|
CHECK(
|
||||||
|
pressure->value() == mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(
|
||||||
|
movedModel.equationOfState(), mean_field::eos::DensityValue{0.7}
|
||||||
|
)
|
||||||
|
.value()
|
||||||
|
);
|
||||||
|
CHECK(seed.radius.Size() == 8);
|
||||||
}
|
}
|
||||||
@@ -164,9 +164,7 @@ TEST_CASE(
|
|||||||
CHECK_FALSE(gravityPotentialReport.updatedDisplacement);
|
CHECK_FALSE(gravityPotentialReport.updatedDisplacement);
|
||||||
CHECK_FALSE(gravityPotentialReport.updatedBernoulliConstant);
|
CHECK_FALSE(gravityPotentialReport.updatedBernoulliConstant);
|
||||||
|
|
||||||
CHECK(
|
CHECK(context.GetBaseGravityPotentialTrue()(context.GetGravityPotentialMap().true_dof(0)) == gravityPotential(0));
|
||||||
context.GetBaseGravityPotentialTrue()(context.GetGravityPotentialMap().true_dof(0)) == gravityPotential(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
++dependencies.bernoulliConstant.revision;
|
++dependencies.bernoulliConstant.revision;
|
||||||
|
|
||||||
|
|||||||
@@ -11,98 +11,86 @@ import mean_field;
|
|||||||
import test_helpers;
|
import test_helpers;
|
||||||
|
|
||||||
namespace gravity_displacement_force_test_utils {
|
namespace gravity_displacement_force_test_utils {
|
||||||
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||||
|
|
||||||
constexpr auto densityValue =
|
constexpr auto densityValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||||
mean_field::utils::blocks::density_field.mass_term);
|
|
||||||
|
|
||||||
constexpr auto displacementValue =
|
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::displacement_field.geometry_term
|
||||||
mean_field::utils::blocks::displacement_field.geometry_term);
|
);
|
||||||
|
|
||||||
constexpr auto gravityGradientValue =
|
constexpr auto gravityGradientValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||||
mean_field::utils::blocks::gravity_field.gradient_term);
|
|
||||||
|
|
||||||
constexpr auto gravityPotentialValue =
|
constexpr auto gravityPotentialValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||||
mean_field::utils::blocks::gravity_field.poisson_term);
|
|
||||||
|
|
||||||
constexpr auto enthalpyValue =
|
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||||
mean_field::utils::blocks::enthalpy_field.specific_term);
|
);
|
||||||
|
|
||||||
constexpr auto barotropicConstantValue =
|
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||||
mean_field::utils::blocks::barotropic_constant_field
|
);
|
||||||
.mass_normalization_term);
|
|
||||||
|
|
||||||
constexpr auto gravityGradientResidual =
|
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::gravity_field.gradient_term
|
||||||
mean_field::utils::blocks::gravity_field.gradient_term);
|
);
|
||||||
|
|
||||||
constexpr auto gravityPotentialResidual =
|
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::gravity_field.poisson_term
|
||||||
mean_field::utils::blocks::gravity_field.poisson_term);
|
);
|
||||||
|
|
||||||
constexpr auto densityResidual =
|
constexpr auto densityResidual =
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||||
mean_field::utils::blocks::density_field.mass_term);
|
|
||||||
|
|
||||||
constexpr auto displacementResidual =
|
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::displacement_field.geometry_term
|
||||||
mean_field::utils::blocks::displacement_field.geometry_term);
|
);
|
||||||
|
|
||||||
constexpr auto enthalpyResidual =
|
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||||
mean_field::utils::blocks::enthalpy_field.specific_term);
|
);
|
||||||
|
|
||||||
constexpr auto massResidual =
|
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||||
mean_field::utils::blocks::barotropic_constant_field
|
);
|
||||||
.mass_normalization_term);
|
|
||||||
|
|
||||||
[[nodiscard]] mean_field::operators::GravityDisplacementForceLayout
|
[[nodiscard]] mean_field::operators::GravityDisplacementForceLayout make_layout(const mean_field::fem::FEM &f) {
|
||||||
make_layout(const mean_field::fem::FEM &f) {
|
|
||||||
using DomainSchema = gravity_prepared_test_utils::DomainSchema;
|
using DomainSchema = gravity_prepared_test_utils::DomainSchema;
|
||||||
|
|
||||||
const auto densityMap =
|
const auto densityMap = gravity_prepared_test_utils::make_field_map<mean_field::field::Density>(f);
|
||||||
gravity_prepared_test_utils::make_field_map<mean_field::field::Density>(
|
const auto displacementMap = gravity_prepared_test_utils::make_field_map<mean_field::field::Displacement>(f);
|
||||||
f);
|
|
||||||
const auto displacementMap = gravity_prepared_test_utils::make_field_map<
|
|
||||||
mean_field::field::Displacement>(f);
|
|
||||||
const auto gravityFluxMap =
|
const auto gravityFluxMap =
|
||||||
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
|
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes);
|
||||||
DomainSchema>(*f.gravityFluxFes);
|
const auto gravityPotentialMap =
|
||||||
const auto gravityPotentialMap = mean_field::field::make_field_dof_map<
|
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
|
||||||
mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
|
|
||||||
const auto enthalpyMap =
|
const auto enthalpyMap =
|
||||||
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy,
|
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes);
|
||||||
DomainSchema>(*f.enthalpyFes);
|
|
||||||
|
|
||||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||||
densityMap.reduced_size(), displacementMap.reduced_size(),
|
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
|
||||||
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
|
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||||
enthalpyMap.reduced_size(), 1};
|
};
|
||||||
|
|
||||||
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
||||||
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
|
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
|
||||||
densityMap.reduced_size(), displacementMap.reduced_size(),
|
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||||
enthalpyMap.reduced_size(), 1};
|
};
|
||||||
|
|
||||||
return {valueSizes, residualSizes};
|
return {valueSizes, residualSizes};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector make_density(const mean_field::fem::FEM &f,
|
[[nodiscard]] mfem::Vector make_density(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double phase
|
||||||
|
) {
|
||||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||||
|
|
||||||
mfem::FunctionCoefficient densityCoefficient(
|
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
|
||||||
[phase](const mfem::Vector &position) {
|
|
||||||
return 0.82 + 0.07 * std::sin(0.8 * position(0) + phase) +
|
return 0.82 + 0.07 * std::sin(0.8 * position(0) + phase) +
|
||||||
0.05 * std::cos(0.6 * position(1) - 0.3 * phase) +
|
0.05 * std::cos(0.6 * position(1) - 0.3 * phase) + 0.03 * position(2) * position(2);
|
||||||
0.03 * position(2) * position(2);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
densityField.ProjectCoefficient(densityCoefficient);
|
densityField.ProjectCoefficient(densityCoefficient);
|
||||||
@@ -110,16 +98,17 @@ make_layout(const mean_field::fem::FEM &f) {
|
|||||||
mfem::Vector densityTrue;
|
mfem::Vector densityTrue;
|
||||||
densityField.GetTrueDofs(densityTrue);
|
densityField.GetTrueDofs(densityTrue);
|
||||||
return densityTrue;
|
return densityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector make_density_direction(const mean_field::fem::FEM &f,
|
[[nodiscard]] mfem::Vector make_density_direction(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double phase
|
||||||
|
) {
|
||||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||||
|
|
||||||
mfem::FunctionCoefficient densityCoefficient(
|
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
|
||||||
[phase](const mfem::Vector &position) {
|
return 0.19 * std::sin(0.9 * position(0) + phase) - 0.13 * std::cos(0.7 * position(1) - phase) +
|
||||||
return 0.19 * std::sin(0.9 * position(0) + phase) -
|
0.08 * position(2);
|
||||||
0.13 * std::cos(0.7 * position(1) - phase) + 0.08 * position(2);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
densityField.ProjectCoefficient(densityCoefficient);
|
densityField.ProjectCoefficient(densityCoefficient);
|
||||||
@@ -127,14 +116,15 @@ make_layout(const mean_field::fem::FEM &f) {
|
|||||||
mfem::Vector densityTrue;
|
mfem::Vector densityTrue;
|
||||||
densityField.GetTrueDofs(densityTrue);
|
densityField.GetTrueDofs(densityTrue);
|
||||||
return densityTrue;
|
return densityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector make_gravity_gradient(const mean_field::fem::FEM &f,
|
[[nodiscard]] mfem::Vector make_gravity_gradient(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double phase
|
||||||
|
) {
|
||||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||||
|
|
||||||
auto gravityFunction = [phase](const mfem::Vector &position,
|
auto gravityFunction = [phase](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
mfem::Vector &value) {
|
|
||||||
value.SetSize(3);
|
value.SetSize(3);
|
||||||
|
|
||||||
value(0) = 0.31 + 0.08 * position(0) + 0.03 * phase * position(1);
|
value(0) = 0.31 + 0.08 * position(0) + 0.03 * phase * position(1);
|
||||||
@@ -151,15 +141,15 @@ make_layout(const mean_field::fem::FEM &f) {
|
|||||||
mfem::Vector gravityTrue;
|
mfem::Vector gravityTrue;
|
||||||
gravityField.GetTrueDofs(gravityTrue);
|
gravityField.GetTrueDofs(gravityTrue);
|
||||||
return gravityTrue;
|
return gravityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_gravity_gradient_direction(
|
||||||
make_gravity_gradient_direction(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const double phase) {
|
const double phase
|
||||||
|
) {
|
||||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||||
|
|
||||||
auto gravityFunction = [phase](const mfem::Vector &position,
|
auto gravityFunction = [phase](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
mfem::Vector &value) {
|
|
||||||
value.SetSize(3);
|
value.SetSize(3);
|
||||||
|
|
||||||
value(0) = 0.14 * std::sin(position(0) + phase) + 0.03 * position(1);
|
value(0) = 0.14 * std::sin(position(0) + phase) + 0.03 * position(1);
|
||||||
@@ -176,22 +166,18 @@ make_gravity_gradient_direction(const mean_field::fem::FEM &f,
|
|||||||
mfem::Vector gravityTrue;
|
mfem::Vector gravityTrue;
|
||||||
gravityField.GetTrueDofs(gravityTrue);
|
gravityField.GetTrueDofs(gravityTrue);
|
||||||
return gravityTrue;
|
return gravityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) {
|
||||||
make_displacement_direction(const mean_field::fem::FEM &f) {
|
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.83);
|
||||||
mfem::Vector direction =
|
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.83);
|
|
||||||
|
|
||||||
const mfem::Vector second =
|
const mfem::Vector second = gravity_prepared_test_utils::make_displacement(f, 0.29);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.29);
|
|
||||||
|
|
||||||
direction -= second;
|
direction -= second;
|
||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
||||||
make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
|
||||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||||
densityField = 0.0;
|
densityField = 0.0;
|
||||||
|
|
||||||
@@ -201,8 +187,7 @@ make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
|||||||
int localVacuumElements = 0;
|
int localVacuumElements = 0;
|
||||||
|
|
||||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||||
mfem::ElementTransformation *transformation =
|
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||||
f.mesh->GetElementTransformation(elementId);
|
|
||||||
|
|
||||||
REQUIRE(transformation != nullptr);
|
REQUIRE(transformation != nullptr);
|
||||||
|
|
||||||
@@ -219,69 +204,74 @@ make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int globalVacuumElements = 0;
|
int globalVacuumElements = 0;
|
||||||
MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT,
|
MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT, MPI_SUM, f.mesh->GetComm());
|
||||||
MPI_SUM, f.mesh->GetComm());
|
|
||||||
|
|
||||||
REQUIRE(globalVacuumElements > 0);
|
REQUIRE(globalVacuumElements > 0);
|
||||||
|
|
||||||
mfem::Vector densityTrue;
|
mfem::Vector densityTrue;
|
||||||
densityField.GetTrueDofs(densityTrue);
|
densityField.GetTrueDofs(densityTrue);
|
||||||
return densityTrue;
|
return densityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mean_field::operators::context::gravity_field::
|
[[nodiscard]] mean_field::operators::context::gravity_field::GravityFieldRevisions make_revisions() {
|
||||||
GravityFieldRevisions
|
return {
|
||||||
make_revisions() {
|
.discretization = {.value = 3},
|
||||||
return {.discretization = {.value = 3},
|
|
||||||
.displacement = {.value = 5},
|
.displacement = {.value = 5},
|
||||||
.density = {.value = 7},
|
.density = {.value = 7},
|
||||||
.gravity_gradient = {.value = 11},
|
.gravity_gradient = {.value = 11},
|
||||||
.gravity_potential = {.value = 13}};
|
.gravity_potential = {.value = 13}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void prepare_gravity_context(
|
void prepare_gravity_context(
|
||||||
mean_field::operators::context::gravity_field::
|
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext &context,
|
||||||
GravityFieldLinearizationContext &context,
|
const mfem::Vector &density,
|
||||||
const mfem::Vector &density, const mfem::Vector &displacement,
|
const mfem::Vector &displacement,
|
||||||
const mfem::Vector &gravityGradient, const mfem::Vector &gravityPotential,
|
const mfem::Vector &gravityGradient,
|
||||||
const mean_field::operators::context::gravity_field::GravityFieldRevisions
|
const mfem::Vector &gravityPotential,
|
||||||
&revisions) {
|
const mean_field::operators::context::gravity_field::GravityFieldRevisions &revisions
|
||||||
|
) {
|
||||||
context.Prepare(
|
context.Prepare(
|
||||||
{.density = context.GetDensityMap().gather(density),
|
{.density = context.GetDensityMap().gather(density),
|
||||||
.displacement = context.GetDisplacementMap().gather(displacement),
|
.displacement = context.GetDisplacementMap().gather(displacement),
|
||||||
.gravity_gradient =
|
.gravity_gradient = context.GetGravityGradientMap().gather(gravityGradient),
|
||||||
context.GetGravityGradientMap().gather(gravityGradient),
|
.gravity_potential = context.GetGravityPotentialMap().gather(gravityPotential)},
|
||||||
.gravity_potential =
|
revisions
|
||||||
context.GetGravityPotentialMap().gather(gravityPotential)},
|
);
|
||||||
revisions);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] double relative_difference(const mfem::Vector &left,
|
[[nodiscard]] double relative_difference(
|
||||||
|
const mfem::Vector &left,
|
||||||
const mfem::Vector &right,
|
const mfem::Vector &right,
|
||||||
const MPI_Comm communicator) {
|
const MPI_Comm communicator
|
||||||
MFEM_VERIFY(left.Size() == right.Size(),
|
) {
|
||||||
"Cannot compare gravity-displacement-force vectors with "
|
MFEM_VERIFY(
|
||||||
"different sizes.");
|
left.Size() == right.Size(), "Cannot compare gravity-displacement-force vectors with "
|
||||||
|
"different sizes."
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector difference(left);
|
mfem::Vector difference(left);
|
||||||
difference -= right;
|
difference -= right;
|
||||||
|
|
||||||
const double scale =
|
const double scale = std::max(
|
||||||
std::max({gravity_prepared_test_utils::global_norm(left, communicator),
|
{gravity_prepared_test_utils::global_norm(left, communicator),
|
||||||
gravity_prepared_test_utils::global_norm(right, communicator),
|
gravity_prepared_test_utils::global_norm(right, communicator),
|
||||||
100.0 * std::numeric_limits<double>::epsilon()});
|
100.0 * std::numeric_limits<double>::epsilon()}
|
||||||
|
);
|
||||||
|
|
||||||
return gravity_prepared_test_utils::global_norm(difference, communicator) /
|
return gravity_prepared_test_utils::global_norm(difference, communicator) / scale;
|
||||||
scale;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector centered_difference(
|
[[nodiscard]] mfem::Vector centered_difference(
|
||||||
const mean_field::fem::FEM &f, const mfem::Vector &baseDensity,
|
const mean_field::fem::FEM &f,
|
||||||
|
const mfem::Vector &baseDensity,
|
||||||
const mfem::Vector &densityDirection,
|
const mfem::Vector &densityDirection,
|
||||||
const mfem::Vector &baseGravityGradient,
|
const mfem::Vector &baseGravityGradient,
|
||||||
const mfem::Vector &gravityGradientDirection,
|
const mfem::Vector &gravityGradientDirection,
|
||||||
const mfem::Vector &baseDisplacement,
|
const mfem::Vector &baseDisplacement,
|
||||||
const mfem::Vector &displacementDirection, const double step) {
|
const mfem::Vector &displacementDirection,
|
||||||
|
const double step
|
||||||
|
) {
|
||||||
mfem::Vector plusDensity(baseDensity);
|
mfem::Vector plusDensity(baseDensity);
|
||||||
plusDensity.Add(step, densityDirection);
|
plusDensity.Add(step, densityDirection);
|
||||||
|
|
||||||
@@ -304,23 +294,24 @@ void prepare_gravity_context(
|
|||||||
mfem::Vector minusResidual;
|
mfem::Vector minusResidual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, plusDensity, plusGravity, plusDisplacement,
|
f, *f.domainMapperStateless, plusDensity, plusGravity, plusDisplacement, plusResidual
|
||||||
plusResidual);
|
);
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, minusDensity, minusGravity,
|
f, *f.domainMapperStateless, minusDensity, minusGravity, minusDisplacement, minusResidual
|
||||||
minusDisplacement, minusResidual);
|
);
|
||||||
|
|
||||||
plusResidual -= minusResidual;
|
plusResidual -= minusResidual;
|
||||||
plusResidual /= 2.0 * step;
|
plusResidual /= 2.0 * step;
|
||||||
return plusResidual;
|
return plusResidual;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int index>
|
template <int index>
|
||||||
[[nodiscard]] mfem::Vector copy_residual_block(
|
[[nodiscard]] mfem::Vector copy_residual_block(
|
||||||
const mfem::Vector &action,
|
const mfem::Vector &action,
|
||||||
const mean_field::operators::GravityDisplacementForceLayout &layout,
|
const mean_field::operators::GravityDisplacementForceLayout &layout,
|
||||||
const mean_field::utils::blocks::residual_block<index> block) {
|
const mean_field::utils::blocks::residual_block<index> block
|
||||||
|
) {
|
||||||
mfem::Vector result(layout.size(block));
|
mfem::Vector result(layout.size(block));
|
||||||
const int offset = layout.offset(block);
|
const int offset = layout.offset(block);
|
||||||
|
|
||||||
@@ -329,21 +320,22 @@ template <int index>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} // namespace gravity_displacement_force_test_utils
|
} // namespace gravity_displacement_force_test_utils
|
||||||
|
|
||||||
TEST_CASE("Gravity Displacement Force Query Includes Every Registered Operand",
|
TEST_CASE(
|
||||||
tags::gravity_unit) {
|
"Gravity Displacement Force Query Includes Every Registered Operand",
|
||||||
using DisplacementField =
|
tags::gravity_unit
|
||||||
mean_field::field::Field<mean_field::field::Displacement>;
|
) {
|
||||||
|
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
||||||
|
|
||||||
constexpr int geometryWeightOrder = 4;
|
constexpr int geometryWeightOrder = 4;
|
||||||
|
|
||||||
constexpr mean_field::quadrature::Query query = DisplacementField::make_query<
|
constexpr mean_field::quadrature::Query query =
|
||||||
mean_field::field::Displacement::Form::GravityForce>(
|
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>(
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, {},
|
||||||
geometryWeightOrder, {}, mean_field::utils::DOMAINS::STELLAR,
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
mean_field::quadrature::MappingKind::general);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rho: 2
|
* rho: 2
|
||||||
@@ -356,8 +348,7 @@ TEST_CASE("Gravity Displacement Force Query Includes Every Registered Operand",
|
|||||||
|
|
||||||
STATIC_REQUIRE(query.term == mean_field::quadrature::Term::gravity_force);
|
STATIC_REQUIRE(query.term == mean_field::quadrature::Term::gravity_force);
|
||||||
|
|
||||||
STATIC_REQUIRE(query.role ==
|
STATIC_REQUIRE(query.role == mean_field::quadrature::QuadratureRole::discretization);
|
||||||
mean_field::quadrature::QuadratureRole::discretization);
|
|
||||||
|
|
||||||
STATIC_REQUIRE(query.domain == mean_field::utils::DOMAINS::STELLAR);
|
STATIC_REQUIRE(query.domain == mean_field::utils::DOMAINS::STELLAR);
|
||||||
|
|
||||||
@@ -367,9 +358,11 @@ TEST_CASE("Gravity Displacement Force Query Includes Every Registered Operand",
|
|||||||
STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
|
STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
|
TEST_CASE(
|
||||||
|
"Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
|
||||||
"Vacuum",
|
"Vacuum",
|
||||||
tags::gravity_kernel_accuracy) {
|
tags::gravity_kernel_accuracy
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -391,8 +384,7 @@ TEST_CASE("Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
|
|||||||
value(0) = 1.0;
|
value(0) = 1.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient gravityCoefficient(3,
|
mfem::VectorFunctionCoefficient gravityCoefficient(3, constantGravityFunction);
|
||||||
constantGravityFunction);
|
|
||||||
|
|
||||||
gravityField.ProjectCoefficient(gravityCoefficient);
|
gravityField.ProjectCoefficient(gravityCoefficient);
|
||||||
|
|
||||||
@@ -405,8 +397,8 @@ TEST_CASE("Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
|
|||||||
mfem::Vector residual;
|
mfem::Vector residual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, density, gravityGradient, displacement,
|
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||||
residual);
|
);
|
||||||
|
|
||||||
mfem::ParGridFunction testField(f.displacementFes.get());
|
mfem::ParGridFunction testField(f.displacementFes.get());
|
||||||
testField.ProjectCoefficient(gravityCoefficient);
|
testField.ProjectCoefficient(gravityCoefficient);
|
||||||
@@ -414,57 +406,54 @@ TEST_CASE("Gravity Displacement Force Uses Positive Grad-Phi Sign And Excludes "
|
|||||||
mfem::Vector testDirection;
|
mfem::Vector testDirection;
|
||||||
testField.GetTrueDofs(testDirection);
|
testField.GetTrueDofs(testDirection);
|
||||||
|
|
||||||
const double signedWork = gravity_prepared_test_utils::global_dot(
|
const double signedWork = gravity_prepared_test_utils::global_dot(residual, testDirection, f.mesh->GetComm());
|
||||||
residual, testDirection, f.mesh->GetComm());
|
|
||||||
|
|
||||||
INFO("Constant +x gravity-force work = " << signedWork);
|
INFO("Constant +x gravity-force work = " << signedWork);
|
||||||
CHECK(signedWork > 0.0);
|
CHECK(signedWork > 0.0);
|
||||||
|
|
||||||
const mfem::Vector vacuumDensity =
|
const mfem::Vector vacuumDensity = gravity_displacement_force_test_utils::make_vacuum_only_density(f);
|
||||||
gravity_displacement_force_test_utils::make_vacuum_only_density(f);
|
|
||||||
|
|
||||||
mfem::Vector vacuumResidual;
|
mfem::Vector vacuumResidual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, vacuumDensity, gravityGradient, displacement,
|
f, *f.domainMapperStateless, vacuumDensity, gravityGradient, displacement, vacuumResidual
|
||||||
vacuumResidual);
|
);
|
||||||
|
|
||||||
CHECK(gravity_prepared_test_utils::global_norm(vacuumResidual,
|
CHECK(gravity_prepared_test_utils::global_norm(vacuumResidual, f.mesh->GetComm()) == 0.0);
|
||||||
f.mesh->GetComm()) == 0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
|
TEST_CASE(
|
||||||
tags::gravity_prepared) {
|
"Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
|
||||||
|
tags::gravity_prepared
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
REQUIRE(f.okay());
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
mfem::Vector density =
|
mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.31);
|
||||||
gravity_displacement_force_test_utils::make_density(f, 0.31);
|
|
||||||
|
|
||||||
const mfem::Vector gravityGradient =
|
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.47);
|
||||||
gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.47);
|
|
||||||
|
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.61);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.61);
|
|
||||||
|
|
||||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||||
gravityPotential = 0.0;
|
gravityPotential = 0.0;
|
||||||
|
|
||||||
auto revisions = gravity_displacement_force_test_utils::make_revisions();
|
auto revisions = gravity_displacement_force_test_utils::make_revisions();
|
||||||
|
|
||||||
mean_field::operators::context::gravity_field::
|
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||||
GravityFieldLinearizationContext gravityContext(f,
|
f, *f.domainMapperStateless
|
||||||
*f.domainMapperStateless);
|
);
|
||||||
|
|
||||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||||
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
|
||||||
revisions);
|
);
|
||||||
|
|
||||||
mean_field::operators::PreparedGravityDisplacementForceOperator
|
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
|
||||||
preparedOperator(f, *f.domainMapperStateless, gravityContext);
|
f, *f.domainMapperStateless, gravityContext
|
||||||
|
);
|
||||||
|
|
||||||
const auto initialReport = preparedOperator.Prepare();
|
const auto initialReport = preparedOperator.Prepare();
|
||||||
REQUIRE(initialReport.DidAnyWork());
|
REQUIRE(initialReport.DidAnyWork());
|
||||||
@@ -476,23 +465,24 @@ TEST_CASE("Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
|
|||||||
preparedOperator.BuildResidual(preparedResidual);
|
preparedOperator.BuildResidual(preparedResidual);
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, density, gravityGradient, displacement,
|
f, *f.domainMapperStateless, density, gravityGradient, displacement, kernelResidual
|
||||||
kernelResidual);
|
);
|
||||||
|
|
||||||
const mfem::Vector kernelResidualReduced =
|
const mfem::Vector kernelResidualReduced = gravityContext.GetDisplacementMap().gather(kernelResidual);
|
||||||
gravityContext.GetDisplacementMap().gather(kernelResidual);
|
|
||||||
|
|
||||||
CHECK(gravity_displacement_force_test_utils::relative_difference(
|
CHECK(
|
||||||
preparedResidual, kernelResidualReduced, f.mesh->GetComm()) <
|
gravity_displacement_force_test_utils::relative_difference(
|
||||||
2.0e-12);
|
preparedResidual, kernelResidualReduced, f.mesh->GetComm()
|
||||||
|
) < 2.0e-12
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
|
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
|
||||||
|
|
||||||
++revisions.gravity_potential.value;
|
++revisions.gravity_potential.value;
|
||||||
|
|
||||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||||
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
|
||||||
revisions);
|
);
|
||||||
|
|
||||||
CHECK(preparedOperator.IsPrepared());
|
CHECK(preparedOperator.IsPrepared());
|
||||||
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
|
CHECK_FALSE(preparedOperator.Prepare().DidAnyWork());
|
||||||
@@ -501,8 +491,8 @@ TEST_CASE("Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
|
|||||||
++revisions.density.value;
|
++revisions.density.value;
|
||||||
|
|
||||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||||
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
gravityContext, density, displacement, gravityGradient, gravityPotential, revisions
|
||||||
revisions);
|
);
|
||||||
|
|
||||||
CHECK_FALSE(preparedOperator.IsPrepared());
|
CHECK_FALSE(preparedOperator.IsPrepared());
|
||||||
|
|
||||||
@@ -516,79 +506,73 @@ TEST_CASE("Prepared Gravity Displacement Force Reuses Shared Gravity Revisions",
|
|||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Gravity Displacement Force Jacobian Matches All Columns And Centered "
|
"Gravity Displacement Force Jacobian Matches All Columns And Centered "
|
||||||
"Differences",
|
"Differences",
|
||||||
tags::gravity_prepared_jacobian_accuracy) {
|
tags::gravity_prepared_jacobian_accuracy
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
REQUIRE(f.okay());
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
const mfem::Vector density =
|
const mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.37);
|
||||||
gravity_displacement_force_test_utils::make_density(f, 0.37);
|
|
||||||
|
|
||||||
const mfem::Vector densityDirection =
|
const mfem::Vector densityDirection = gravity_displacement_force_test_utils::make_density_direction(f, 0.53);
|
||||||
gravity_displacement_force_test_utils::make_density_direction(f, 0.53);
|
|
||||||
|
|
||||||
const mfem::Vector gravityGradient =
|
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.67);
|
||||||
gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.67);
|
|
||||||
|
|
||||||
const mfem::Vector gravityGradientDirection =
|
const mfem::Vector gravityGradientDirection =
|
||||||
gravity_displacement_force_test_utils::make_gravity_gradient_direction(
|
gravity_displacement_force_test_utils::make_gravity_gradient_direction(f, 0.71);
|
||||||
f, 0.71);
|
|
||||||
|
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.59);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.59);
|
|
||||||
|
|
||||||
const mfem::Vector displacementDirection =
|
const mfem::Vector displacementDirection = gravity_displacement_force_test_utils::make_displacement_direction(f);
|
||||||
gravity_displacement_force_test_utils::make_displacement_direction(f);
|
|
||||||
|
|
||||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||||
gravityPotential = 0.0;
|
gravityPotential = 0.0;
|
||||||
|
|
||||||
mean_field::operators::context::gravity_field::
|
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||||
GravityFieldLinearizationContext gravityContext(f,
|
f, *f.domainMapperStateless
|
||||||
*f.domainMapperStateless);
|
);
|
||||||
|
|
||||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||||
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
||||||
gravity_displacement_force_test_utils::make_revisions());
|
gravity_displacement_force_test_utils::make_revisions()
|
||||||
|
);
|
||||||
|
|
||||||
mean_field::operators::PreparedGravityDisplacementForceOperator
|
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
|
||||||
preparedOperator(f, *f.domainMapperStateless, gravityContext);
|
f, *f.domainMapperStateless, gravityContext
|
||||||
|
);
|
||||||
|
|
||||||
preparedOperator.Prepare();
|
preparedOperator.Prepare();
|
||||||
|
|
||||||
const mfem::Vector densityDirectionReduced =
|
const mfem::Vector densityDirectionReduced = gravityContext.GetDensityMap().gather(densityDirection);
|
||||||
gravityContext.GetDensityMap().gather(densityDirection);
|
|
||||||
const mfem::Vector gravityGradientDirectionReduced =
|
const mfem::Vector gravityGradientDirectionReduced =
|
||||||
gravityContext.GetGravityGradientMap().gather(gravityGradientDirection);
|
gravityContext.GetGravityGradientMap().gather(gravityGradientDirection);
|
||||||
const mfem::Vector displacementDirectionReduced =
|
const mfem::Vector displacementDirectionReduced = gravityContext.GetDisplacementMap().gather(displacementDirection);
|
||||||
gravityContext.GetDisplacementMap().gather(displacementDirection);
|
|
||||||
|
|
||||||
mfem::Vector densityAction;
|
mfem::Vector densityAction;
|
||||||
mfem::Vector gravityAction;
|
mfem::Vector gravityAction;
|
||||||
mfem::Vector displacementAction;
|
mfem::Vector displacementAction;
|
||||||
mfem::Vector completeAction;
|
mfem::Vector completeAction;
|
||||||
|
|
||||||
preparedOperator.ApplyDensityJacobianAction(densityDirectionReduced,
|
preparedOperator.ApplyDensityJacobianAction(densityDirectionReduced, densityAction);
|
||||||
densityAction);
|
|
||||||
|
|
||||||
preparedOperator.ApplyGravityGradientJacobianAction(
|
preparedOperator.ApplyGravityGradientJacobianAction(gravityGradientDirectionReduced, gravityAction);
|
||||||
gravityGradientDirectionReduced, gravityAction);
|
|
||||||
|
|
||||||
preparedOperator.ApplyDisplacementJacobianAction(displacementDirectionReduced,
|
preparedOperator.ApplyDisplacementJacobianAction(displacementDirectionReduced, displacementAction);
|
||||||
displacementAction);
|
|
||||||
|
|
||||||
preparedOperator.ApplyCompleteJacobianAction(
|
preparedOperator.ApplyCompleteJacobianAction(
|
||||||
densityDirectionReduced, displacementDirectionReduced,
|
densityDirectionReduced, displacementDirectionReduced, gravityGradientDirectionReduced, completeAction
|
||||||
gravityGradientDirectionReduced, completeAction);
|
);
|
||||||
|
|
||||||
mfem::Vector summedColumns(densityAction);
|
mfem::Vector summedColumns(densityAction);
|
||||||
summedColumns += gravityAction;
|
summedColumns += gravityAction;
|
||||||
summedColumns += displacementAction;
|
summedColumns += displacementAction;
|
||||||
|
|
||||||
CHECK(gravity_displacement_force_test_utils::relative_difference(
|
CHECK(
|
||||||
completeAction, summedColumns, f.mesh->GetComm()) < 2.0e-12);
|
gravity_displacement_force_test_utils::relative_difference(completeAction, summedColumns, f.mesh->GetComm()) <
|
||||||
|
2.0e-12
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector zeroDensity(densityDirection.Size());
|
mfem::Vector zeroDensity(densityDirection.Size());
|
||||||
mfem::Vector zeroGravity(gravityGradientDirection.Size());
|
mfem::Vector zeroGravity(gravityGradientDirection.Size());
|
||||||
@@ -599,50 +583,41 @@ TEST_CASE(
|
|||||||
|
|
||||||
constexpr double step = 1.0e-5;
|
constexpr double step = 1.0e-5;
|
||||||
|
|
||||||
const mfem::Vector densityDifferenceTrue =
|
const mfem::Vector densityDifferenceTrue = gravity_displacement_force_test_utils::centered_difference(
|
||||||
gravity_displacement_force_test_utils::centered_difference(
|
f, density, densityDirection, gravityGradient, zeroGravity, displacement, zeroDisplacement, step
|
||||||
f, density, densityDirection, gravityGradient, zeroGravity,
|
);
|
||||||
displacement, zeroDisplacement, step);
|
|
||||||
|
|
||||||
const mfem::Vector gravityDifferenceTrue =
|
const mfem::Vector gravityDifferenceTrue = gravity_displacement_force_test_utils::centered_difference(
|
||||||
gravity_displacement_force_test_utils::centered_difference(
|
f, density, zeroDensity, gravityGradient, gravityGradientDirection, displacement, zeroDisplacement, step
|
||||||
f, density, zeroDensity, gravityGradient, gravityGradientDirection,
|
);
|
||||||
displacement, zeroDisplacement, step);
|
|
||||||
|
|
||||||
const mfem::Vector displacementDifferenceTrue =
|
const mfem::Vector displacementDifferenceTrue = gravity_displacement_force_test_utils::centered_difference(
|
||||||
gravity_displacement_force_test_utils::centered_difference(
|
f, density, zeroDensity, gravityGradient, zeroGravity, displacement, displacementDirection, step
|
||||||
f, density, zeroDensity, gravityGradient, zeroGravity, displacement,
|
);
|
||||||
displacementDirection, step);
|
|
||||||
|
|
||||||
const mfem::Vector completeDifferenceTrue =
|
const mfem::Vector completeDifferenceTrue = gravity_displacement_force_test_utils::centered_difference(
|
||||||
gravity_displacement_force_test_utils::centered_difference(
|
f, density, densityDirection, gravityGradient, gravityGradientDirection, displacement, displacementDirection,
|
||||||
f, density, densityDirection, gravityGradient,
|
step
|
||||||
gravityGradientDirection, displacement, displacementDirection, step);
|
);
|
||||||
|
|
||||||
const mfem::Vector densityDifference =
|
const mfem::Vector densityDifference = gravityContext.GetDisplacementMap().gather(densityDifferenceTrue);
|
||||||
gravityContext.GetDisplacementMap().gather(densityDifferenceTrue);
|
const mfem::Vector gravityDifference = gravityContext.GetDisplacementMap().gather(gravityDifferenceTrue);
|
||||||
const mfem::Vector gravityDifference =
|
const mfem::Vector displacementDifference = gravityContext.GetDisplacementMap().gather(displacementDifferenceTrue);
|
||||||
gravityContext.GetDisplacementMap().gather(gravityDifferenceTrue);
|
const mfem::Vector completeDifference = gravityContext.GetDisplacementMap().gather(completeDifferenceTrue);
|
||||||
const mfem::Vector displacementDifference =
|
|
||||||
gravityContext.GetDisplacementMap().gather(displacementDifferenceTrue);
|
|
||||||
const mfem::Vector completeDifference =
|
|
||||||
gravityContext.GetDisplacementMap().gather(completeDifferenceTrue);
|
|
||||||
|
|
||||||
const double densityError =
|
const double densityError =
|
||||||
gravity_displacement_force_test_utils::relative_difference(
|
gravity_displacement_force_test_utils::relative_difference(densityAction, densityDifference, f.mesh->GetComm());
|
||||||
densityAction, densityDifference, f.mesh->GetComm());
|
|
||||||
|
|
||||||
const double gravityError =
|
const double gravityError =
|
||||||
gravity_displacement_force_test_utils::relative_difference(
|
gravity_displacement_force_test_utils::relative_difference(gravityAction, gravityDifference, f.mesh->GetComm());
|
||||||
gravityAction, gravityDifference, f.mesh->GetComm());
|
|
||||||
|
|
||||||
const double displacementError =
|
const double displacementError = gravity_displacement_force_test_utils::relative_difference(
|
||||||
gravity_displacement_force_test_utils::relative_difference(
|
displacementAction, displacementDifference, f.mesh->GetComm()
|
||||||
displacementAction, displacementDifference, f.mesh->GetComm());
|
);
|
||||||
|
|
||||||
const double completeError =
|
const double completeError = gravity_displacement_force_test_utils::relative_difference(
|
||||||
gravity_displacement_force_test_utils::relative_difference(
|
completeAction, completeDifference, f.mesh->GetComm()
|
||||||
completeAction, completeDifference, f.mesh->GetComm());
|
);
|
||||||
|
|
||||||
INFO("Density-column centered-difference error = " << densityError);
|
INFO("Density-column centered-difference error = " << densityError);
|
||||||
INFO("Gravity-column centered-difference error = " << gravityError);
|
INFO("Gravity-column centered-difference error = " << gravityError);
|
||||||
@@ -655,82 +630,70 @@ TEST_CASE(
|
|||||||
CHECK(completeError < 3.0e-8);
|
CHECK(completeError < 3.0e-8);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Prepared Gravity Displacement Force MFEM Adapter Routes Only R-d",
|
TEST_CASE(
|
||||||
tags::gravity_prepared_unit) {
|
"Prepared Gravity Displacement Force MFEM Adapter Routes Only R-d",
|
||||||
|
tags::gravity_prepared_unit
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
REQUIRE(f.okay());
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
const mfem::Vector density =
|
const mfem::Vector density = gravity_displacement_force_test_utils::make_density(f, 0.41);
|
||||||
gravity_displacement_force_test_utils::make_density(f, 0.41);
|
|
||||||
|
|
||||||
const mfem::Vector densityDirection =
|
const mfem::Vector densityDirection = gravity_displacement_force_test_utils::make_density_direction(f, 0.57);
|
||||||
gravity_displacement_force_test_utils::make_density_direction(f, 0.57);
|
|
||||||
|
|
||||||
const mfem::Vector gravityGradient =
|
const mfem::Vector gravityGradient = gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.63);
|
||||||
gravity_displacement_force_test_utils::make_gravity_gradient(f, 0.63);
|
|
||||||
|
|
||||||
const mfem::Vector gravityGradientDirection =
|
const mfem::Vector gravityGradientDirection =
|
||||||
gravity_displacement_force_test_utils::make_gravity_gradient_direction(
|
gravity_displacement_force_test_utils::make_gravity_gradient_direction(f, 0.77);
|
||||||
f, 0.77);
|
|
||||||
|
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacement = gravity_prepared_test_utils::make_displacement(f, 0.51);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.51);
|
|
||||||
|
|
||||||
const mfem::Vector displacementDirection =
|
const mfem::Vector displacementDirection = gravity_displacement_force_test_utils::make_displacement_direction(f);
|
||||||
gravity_displacement_force_test_utils::make_displacement_direction(f);
|
|
||||||
|
|
||||||
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
mfem::Vector gravityPotential(f.gravityPotentialFes->GetTrueVSize());
|
||||||
gravityPotential = 0.0;
|
gravityPotential = 0.0;
|
||||||
|
|
||||||
mean_field::operators::context::gravity_field::
|
mean_field::operators::context::gravity_field::GravityFieldLinearizationContext gravityContext(
|
||||||
GravityFieldLinearizationContext gravityContext(f,
|
f, *f.domainMapperStateless
|
||||||
*f.domainMapperStateless);
|
);
|
||||||
|
|
||||||
gravity_displacement_force_test_utils::prepare_gravity_context(
|
gravity_displacement_force_test_utils::prepare_gravity_context(
|
||||||
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
gravityContext, density, displacement, gravityGradient, gravityPotential,
|
||||||
gravity_displacement_force_test_utils::make_revisions());
|
gravity_displacement_force_test_utils::make_revisions()
|
||||||
|
);
|
||||||
|
|
||||||
mean_field::operators::PreparedGravityDisplacementForceOperator
|
mean_field::operators::PreparedGravityDisplacementForceOperator preparedOperator(
|
||||||
preparedOperator(f, *f.domainMapperStateless, gravityContext);
|
f, *f.domainMapperStateless, gravityContext
|
||||||
|
);
|
||||||
|
|
||||||
preparedOperator.Prepare();
|
preparedOperator.Prepare();
|
||||||
|
|
||||||
const mfem::Vector densityDirectionReduced =
|
const mfem::Vector densityDirectionReduced = gravityContext.GetDensityMap().gather(densityDirection);
|
||||||
gravityContext.GetDensityMap().gather(densityDirection);
|
|
||||||
const mfem::Vector gravityGradientDirectionReduced =
|
const mfem::Vector gravityGradientDirectionReduced =
|
||||||
gravityContext.GetGravityGradientMap().gather(gravityGradientDirection);
|
gravityContext.GetGravityGradientMap().gather(gravityGradientDirection);
|
||||||
const mfem::Vector displacementDirectionReduced =
|
const mfem::Vector displacementDirectionReduced = gravityContext.GetDisplacementMap().gather(displacementDirection);
|
||||||
gravityContext.GetDisplacementMap().gather(displacementDirection);
|
|
||||||
|
|
||||||
const auto layout = gravity_displacement_force_test_utils::make_layout(f);
|
const auto layout = gravity_displacement_force_test_utils::make_layout(f);
|
||||||
|
|
||||||
mean_field::operators::PreparedGravityDisplacementForceJacobianOperator
|
mean_field::operators::PreparedGravityDisplacementForceJacobianOperator adapter(layout, preparedOperator);
|
||||||
adapter(layout, preparedOperator);
|
|
||||||
|
|
||||||
mfem::BlockVector direction(layout.value_offsets());
|
mfem::BlockVector direction(layout.value_offsets());
|
||||||
direction = 0.0;
|
direction = 0.0;
|
||||||
|
|
||||||
direction.GetBlock(gravity_displacement_force_test_utils::densityValue) =
|
direction.GetBlock(gravity_displacement_force_test_utils::densityValue) = densityDirectionReduced;
|
||||||
densityDirectionReduced;
|
|
||||||
|
|
||||||
direction.GetBlock(gravity_displacement_force_test_utils::displacementValue) =
|
direction.GetBlock(gravity_displacement_force_test_utils::displacementValue) = displacementDirectionReduced;
|
||||||
displacementDirectionReduced;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(gravity_displacement_force_test_utils::gravityGradientValue) = gravityGradientDirectionReduced;
|
||||||
gravity_displacement_force_test_utils::gravityGradientValue) =
|
|
||||||
gravityGradientDirectionReduced;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(gravity_displacement_force_test_utils::gravityPotentialValue) = 0.29;
|
||||||
gravity_displacement_force_test_utils::gravityPotentialValue) = 0.29;
|
|
||||||
|
|
||||||
direction.GetBlock(gravity_displacement_force_test_utils::enthalpyValue) =
|
direction.GetBlock(gravity_displacement_force_test_utils::enthalpyValue) = -0.37;
|
||||||
-0.37;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(gravity_displacement_force_test_utils::barotropicConstantValue) = 0.43;
|
||||||
gravity_displacement_force_test_utils::barotropicConstantValue) = 0.43;
|
|
||||||
|
|
||||||
mfem::Vector action;
|
mfem::Vector action;
|
||||||
adapter.Mult(direction, action);
|
adapter.Mult(direction, action);
|
||||||
@@ -738,36 +701,39 @@ TEST_CASE("Prepared Gravity Displacement Force MFEM Adapter Routes Only R-d",
|
|||||||
mfem::Vector expectedDisplacementAction;
|
mfem::Vector expectedDisplacementAction;
|
||||||
|
|
||||||
preparedOperator.ApplyCompleteJacobianAction(
|
preparedOperator.ApplyCompleteJacobianAction(
|
||||||
densityDirectionReduced, displacementDirectionReduced,
|
densityDirectionReduced, displacementDirectionReduced, gravityGradientDirectionReduced,
|
||||||
gravityGradientDirectionReduced, expectedDisplacementAction);
|
expectedDisplacementAction
|
||||||
|
);
|
||||||
|
|
||||||
const mfem::Vector actualDisplacementAction =
|
const mfem::Vector actualDisplacementAction = gravity_displacement_force_test_utils::copy_residual_block(
|
||||||
gravity_displacement_force_test_utils::copy_residual_block(
|
action, layout, gravity_displacement_force_test_utils::displacementResidual
|
||||||
action, layout,
|
);
|
||||||
gravity_displacement_force_test_utils::displacementResidual);
|
|
||||||
|
|
||||||
CHECK(gravity_displacement_force_test_utils::relative_difference(
|
CHECK(
|
||||||
actualDisplacementAction, expectedDisplacementAction,
|
gravity_displacement_force_test_utils::relative_difference(
|
||||||
f.mesh->GetComm()) < 2.0e-12);
|
actualDisplacementAction, expectedDisplacementAction, f.mesh->GetComm()
|
||||||
|
) < 2.0e-12
|
||||||
|
);
|
||||||
|
|
||||||
const std::array<mfem::Vector, 5> zeroRows{
|
const std::array<mfem::Vector, 5> zeroRows{
|
||||||
gravity_displacement_force_test_utils::copy_residual_block(
|
gravity_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, gravity_displacement_force_test_utils::gravityGradientResidual
|
||||||
gravity_displacement_force_test_utils::gravityGradientResidual),
|
),
|
||||||
gravity_displacement_force_test_utils::copy_residual_block(
|
gravity_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, gravity_displacement_force_test_utils::gravityPotentialResidual
|
||||||
gravity_displacement_force_test_utils::gravityPotentialResidual),
|
),
|
||||||
gravity_displacement_force_test_utils::copy_residual_block(
|
gravity_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, gravity_displacement_force_test_utils::densityResidual
|
||||||
gravity_displacement_force_test_utils::densityResidual),
|
),
|
||||||
gravity_displacement_force_test_utils::copy_residual_block(
|
gravity_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, gravity_displacement_force_test_utils::enthalpyResidual
|
||||||
gravity_displacement_force_test_utils::enthalpyResidual),
|
),
|
||||||
gravity_displacement_force_test_utils::copy_residual_block(
|
gravity_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout, gravity_displacement_force_test_utils::massResidual)};
|
action, layout, gravity_displacement_force_test_utils::massResidual
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
for (const mfem::Vector &row : zeroRows) {
|
for (const mfem::Vector &row : zeroRows) {
|
||||||
CHECK(gravity_prepared_test_utils::global_norm(row, f.mesh->GetComm()) ==
|
CHECK(gravity_prepared_test_utils::global_norm(row, f.mesh->GetComm()) == 0.0);
|
||||||
0.0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,26 +11,34 @@ import mean_field;
|
|||||||
import test_helpers;
|
import test_helpers;
|
||||||
|
|
||||||
namespace gravity_displacement_force_analytic_test_utils {
|
namespace gravity_displacement_force_analytic_test_utils {
|
||||||
struct AffineCase {
|
struct AffineCase {
|
||||||
const char *name;
|
const char *name;
|
||||||
std::array<double, 3> scales;
|
std::array<double, 3> scales;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] double analytic_sphere_volume(const double radius) {
|
[[nodiscard]] double analytic_sphere_volume(const double radius) {
|
||||||
return (4.0 / 3.0) * std::numbers::pi * radius * radius * radius;
|
return (4.0 / 3.0) * std::numbers::pi * radius * radius * radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double determinant(const std::array<double, 3> &scales) {
|
[[nodiscard]] double determinant(
|
||||||
|
const std::array<
|
||||||
|
double,
|
||||||
|
3> &scales
|
||||||
|
) {
|
||||||
return scales[0] * scales[1] * scales[2];
|
return scales[0] * scales[1] * scales[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double relative_scalar_error(const double computed,
|
[[nodiscard]] double relative_scalar_error(
|
||||||
const double expected) {
|
const double computed,
|
||||||
|
const double expected
|
||||||
|
) {
|
||||||
return std::abs(computed - expected) / std::max(std::abs(expected), 1.0e-30);
|
return std::abs(computed - expected) / std::max(std::abs(expected), 1.0e-30);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector make_constant_density(const mean_field::fem::FEM &f,
|
[[nodiscard]] mfem::Vector make_constant_density(
|
||||||
const double densityValue) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double densityValue
|
||||||
|
) {
|
||||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||||
mfem::ConstantCoefficient densityCoefficient(densityValue);
|
mfem::ConstantCoefficient densityCoefficient(densityValue);
|
||||||
densityField.ProjectCoefficient(densityCoefficient);
|
densityField.ProjectCoefficient(densityCoefficient);
|
||||||
@@ -38,133 +46,138 @@ struct AffineCase {
|
|||||||
mfem::Vector densityTrue;
|
mfem::Vector densityTrue;
|
||||||
densityField.GetTrueDofs(densityTrue);
|
densityField.GetTrueDofs(densityTrue);
|
||||||
return densityTrue;
|
return densityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_reference_gravity(
|
||||||
make_reference_gravity(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const std::array<double, 3> &referenceGravity) {
|
const std::array<
|
||||||
|
double,
|
||||||
|
3> &referenceGravity
|
||||||
|
) {
|
||||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient gravityCoefficient(
|
mfem::VectorFunctionCoefficient gravityCoefficient(
|
||||||
f.mesh->Dimension(),
|
f.mesh->Dimension(), [referenceGravity](const mfem::Vector &, mfem::Vector &value) {
|
||||||
[referenceGravity](const mfem::Vector &, mfem::Vector &value) {
|
|
||||||
value.SetSize(3);
|
value.SetSize(3);
|
||||||
|
|
||||||
for (int component = 0; component < 3; ++component) {
|
for (int component = 0; component < 3; ++component) {
|
||||||
value(component) =
|
value(component) = referenceGravity[static_cast<std::size_t>(component)];
|
||||||
referenceGravity[static_cast<std::size_t>(component)];
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
gravityField.ProjectCoefficient(gravityCoefficient);
|
gravityField.ProjectCoefficient(gravityCoefficient);
|
||||||
|
|
||||||
mfem::Vector gravityTrue;
|
mfem::Vector gravityTrue;
|
||||||
gravityField.GetTrueDofs(gravityTrue);
|
gravityField.GetTrueDofs(gravityTrue);
|
||||||
return gravityTrue;
|
return gravityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector make_radial_gravity(const mean_field::fem::FEM &f,
|
[[nodiscard]] mfem::Vector make_radial_gravity(
|
||||||
const double radialCoefficient) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double radialCoefficient
|
||||||
|
) {
|
||||||
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
mfem::ParGridFunction gravityField(f.gravityFluxFes.get());
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient gravityCoefficient(
|
mfem::VectorFunctionCoefficient gravityCoefficient(
|
||||||
f.mesh->Dimension(),
|
f.mesh->Dimension(), [radialCoefficient](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
[radialCoefficient](const mfem::Vector &position, mfem::Vector &value) {
|
|
||||||
value.SetSize(position.Size());
|
value.SetSize(position.Size());
|
||||||
|
|
||||||
for (int component = 0; component < position.Size(); ++component) {
|
for (int component = 0; component < position.Size(); ++component) {
|
||||||
value(component) = radialCoefficient * position(component);
|
value(component) = radialCoefficient * position(component);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
gravityField.ProjectCoefficient(gravityCoefficient);
|
gravityField.ProjectCoefficient(gravityCoefficient);
|
||||||
|
|
||||||
mfem::Vector gravityTrue;
|
mfem::Vector gravityTrue;
|
||||||
gravityField.GetTrueDofs(gravityTrue);
|
gravityField.GetTrueDofs(gravityTrue);
|
||||||
return gravityTrue;
|
return gravityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_affine_displacement(
|
||||||
make_affine_displacement(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const std::array<double, 3> &scales) {
|
const std::array<
|
||||||
|
double,
|
||||||
|
3> &scales
|
||||||
|
) {
|
||||||
mfem::ParGridFunction displacementField(f.displacementFes.get());
|
mfem::ParGridFunction displacementField(f.displacementFes.get());
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient displacementCoefficient(
|
mfem::VectorFunctionCoefficient displacementCoefficient(
|
||||||
f.mesh->Dimension(),
|
f.mesh->Dimension(), [scales](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
[scales](const mfem::Vector &position, mfem::Vector &value) {
|
|
||||||
value.SetSize(position.Size());
|
value.SetSize(position.Size());
|
||||||
|
|
||||||
for (int component = 0; component < position.Size(); ++component) {
|
for (int component = 0; component < position.Size(); ++component) {
|
||||||
value(component) =
|
value(component) = (scales[static_cast<std::size_t>(component)] - 1.0) * position(component);
|
||||||
(scales[static_cast<std::size_t>(component)] - 1.0) *
|
|
||||||
position(component);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
displacementField.ProjectCoefficient(displacementCoefficient);
|
displacementField.ProjectCoefficient(displacementCoefficient);
|
||||||
|
|
||||||
mfem::Vector displacementTrue;
|
mfem::Vector displacementTrue;
|
||||||
displacementField.GetTrueDofs(displacementTrue);
|
displacementField.GetTrueDofs(displacementTrue);
|
||||||
return displacementTrue;
|
return displacementTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_constant_test_direction(
|
||||||
make_constant_test_direction(const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const int selectedComponent) {
|
const int selectedComponent
|
||||||
|
) {
|
||||||
mfem::ParGridFunction testField(f.displacementFes.get());
|
mfem::ParGridFunction testField(f.displacementFes.get());
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient testCoefficient(
|
mfem::VectorFunctionCoefficient testCoefficient(
|
||||||
f.mesh->Dimension(),
|
f.mesh->Dimension(), [selectedComponent](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
[selectedComponent](const mfem::Vector &position, mfem::Vector &value) {
|
|
||||||
value.SetSize(position.Size());
|
value.SetSize(position.Size());
|
||||||
value = 0.0;
|
value = 0.0;
|
||||||
value(selectedComponent) = 1.0;
|
value(selectedComponent) = 1.0;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
testField.ProjectCoefficient(testCoefficient);
|
testField.ProjectCoefficient(testCoefficient);
|
||||||
|
|
||||||
mfem::Vector testTrue;
|
mfem::Vector testTrue;
|
||||||
testField.GetTrueDofs(testTrue);
|
testField.GetTrueDofs(testTrue);
|
||||||
return testTrue;
|
return testTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_dilation_test_direction(const mean_field::fem::FEM &f) {
|
||||||
make_dilation_test_direction(const mean_field::fem::FEM &f) {
|
|
||||||
mfem::ParGridFunction testField(f.displacementFes.get());
|
mfem::ParGridFunction testField(f.displacementFes.get());
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient testCoefficient(
|
mfem::VectorFunctionCoefficient testCoefficient(
|
||||||
f.mesh->Dimension(), [](const mfem::Vector &position,
|
f.mesh->Dimension(), [](const mfem::Vector &position, mfem::Vector &value) { value = position; }
|
||||||
mfem::Vector &value) { value = position; });
|
);
|
||||||
|
|
||||||
testField.ProjectCoefficient(testCoefficient);
|
testField.ProjectCoefficient(testCoefficient);
|
||||||
|
|
||||||
mfem::Vector testTrue;
|
mfem::Vector testTrue;
|
||||||
testField.GetTrueDofs(testTrue);
|
testField.GetTrueDofs(testTrue);
|
||||||
return testTrue;
|
return testTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_mass_normalized_density(mean_field::fem::FEM &f,
|
void set_mass_normalized_density(
|
||||||
|
mean_field::fem::FEM &f,
|
||||||
const double targetMass,
|
const double targetMass,
|
||||||
mfem::ParGridFunction &densityField) {
|
mfem::ParGridFunction &densityField
|
||||||
const mfem::Vector stellarDensityTrue =
|
) {
|
||||||
gravity_prepared_test_utils::make_domain_supported_density(f, true);
|
const mfem::Vector stellarDensityTrue = gravity_prepared_test_utils::make_domain_supported_density(f, true);
|
||||||
|
|
||||||
densityField.SetFromTrueDofs(stellarDensityTrue);
|
densityField.SetFromTrueDofs(stellarDensityTrue);
|
||||||
|
|
||||||
const double unnormalizedMass =
|
const double unnormalizedMass =
|
||||||
mean_field::analysis::domain_integrate_grid_function(
|
mean_field::analysis::domain_integrate_grid_function(f, densityField, mean_field::utils::DOMAINS::STELLAR);
|
||||||
f, densityField, mean_field::utils::DOMAINS::STELLAR);
|
|
||||||
|
|
||||||
MFEM_VERIFY(unnormalizedMass > 0.0,
|
MFEM_VERIFY(unnormalizedMass > 0.0, "The analytic gravity-force test obtained non-positive mass.");
|
||||||
"The analytic gravity-force test obtained non-positive mass.");
|
|
||||||
|
|
||||||
densityField *= targetMass / unnormalizedMass;
|
densityField *= targetMass / unnormalizedMass;
|
||||||
}
|
}
|
||||||
} // namespace gravity_displacement_force_analytic_test_utils
|
} // namespace gravity_displacement_force_analytic_test_utils
|
||||||
|
|
||||||
TEST_CASE("Gravity Displacement Force Matches Analytic Affine Resultants",
|
TEST_CASE(
|
||||||
tags::gravity &tags::accuracy &tags::analytic_comparison
|
"Gravity Displacement Force Matches Analytic Affine Resultants",
|
||||||
&tags::integration) {
|
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -177,30 +190,23 @@ TEST_CASE("Gravity Displacement Force Matches Analytic Affine Resultants",
|
|||||||
|
|
||||||
constexpr std::array<double, 3> physicalGravity{0.31, -0.47, 0.22};
|
constexpr std::array<double, 3> physicalGravity{0.31, -0.47, 0.22};
|
||||||
|
|
||||||
constexpr std::array<
|
constexpr std::array<gravity_displacement_force_analytic_test_utils::AffineCase, 3> affineCases{
|
||||||
gravity_displacement_force_analytic_test_utils::AffineCase, 3>
|
{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}},
|
||||||
affineCases{{{.name = "identity geometry", .scales = {1.0, 1.0, 1.0}},
|
{.name = "volume-preserving affine geometry", .scales = {1.14, 0.93, 1.0 / (1.14 * 0.93)}},
|
||||||
{.name = "volume-preserving affine geometry",
|
{.name = "volume-changing affine geometry", .scales = {1.11, 0.96, 1.07}}}
|
||||||
.scales = {1.14, 0.93, 1.0 / (1.14 * 0.93)}},
|
};
|
||||||
{.name = "volume-changing affine geometry",
|
|
||||||
.scales = {1.11, 0.96, 1.07}}}};
|
|
||||||
|
|
||||||
const mfem::Vector density =
|
const mfem::Vector density = gravity_displacement_force_analytic_test_utils::make_constant_density(f, densityValue);
|
||||||
gravity_displacement_force_analytic_test_utils::make_constant_density(
|
|
||||||
f, densityValue);
|
|
||||||
|
|
||||||
const double referenceVolume =
|
const double referenceVolume =
|
||||||
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(
|
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(mean_field::utils::RADIUS);
|
||||||
mean_field::utils::RADIUS);
|
|
||||||
|
|
||||||
constexpr double relativeTolerance = 5.0e-6;
|
constexpr double relativeTolerance = 5.0e-6;
|
||||||
|
|
||||||
for (const gravity_displacement_force_analytic_test_utils::AffineCase
|
for (const gravity_displacement_force_analytic_test_utils::AffineCase &affineCase : affineCases) {
|
||||||
&affineCase : affineCases) {
|
|
||||||
DYNAMIC_SECTION(affineCase.name) {
|
DYNAMIC_SECTION(affineCase.name) {
|
||||||
const double mapDeterminant =
|
const double mapDeterminant =
|
||||||
gravity_displacement_force_analytic_test_utils::determinant(
|
gravity_displacement_force_analytic_test_utils::determinant(affineCase.scales);
|
||||||
affineCase.scales);
|
|
||||||
|
|
||||||
REQUIRE(mapDeterminant > 0.0);
|
REQUIRE(mapDeterminant > 0.0);
|
||||||
|
|
||||||
@@ -216,42 +222,35 @@ TEST_CASE("Gravity Displacement Force Matches Analytic Affine Resultants",
|
|||||||
*/
|
*/
|
||||||
for (int component = 0; component < 3; ++component) {
|
for (int component = 0; component < 3; ++component) {
|
||||||
referenceGravity[static_cast<std::size_t>(component)] =
|
referenceGravity[static_cast<std::size_t>(component)] =
|
||||||
mapDeterminant *
|
mapDeterminant * physicalGravity[static_cast<std::size_t>(component)] /
|
||||||
physicalGravity[static_cast<std::size_t>(component)] /
|
|
||||||
affineCase.scales[static_cast<std::size_t>(component)];
|
affineCase.scales[static_cast<std::size_t>(component)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::Vector gravityGradient =
|
const mfem::Vector gravityGradient =
|
||||||
gravity_displacement_force_analytic_test_utils::
|
gravity_displacement_force_analytic_test_utils::make_reference_gravity(f, referenceGravity);
|
||||||
make_reference_gravity(f, referenceGravity);
|
|
||||||
|
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacement =
|
||||||
gravity_displacement_force_analytic_test_utils::
|
gravity_displacement_force_analytic_test_utils::make_affine_displacement(f, affineCase.scales);
|
||||||
make_affine_displacement(f, affineCase.scales);
|
|
||||||
|
|
||||||
mfem::Vector residual;
|
mfem::Vector residual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, density, gravityGradient, displacement,
|
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||||
residual);
|
);
|
||||||
|
|
||||||
for (int component = 0; component < 3; ++component) {
|
for (int component = 0; component < 3; ++component) {
|
||||||
const mfem::Vector testDirection =
|
const mfem::Vector testDirection =
|
||||||
gravity_displacement_force_analytic_test_utils::
|
gravity_displacement_force_analytic_test_utils::make_constant_test_direction(f, component);
|
||||||
make_constant_test_direction(f, component);
|
|
||||||
|
|
||||||
const double computedResultant =
|
const double computedResultant =
|
||||||
gravity_prepared_test_utils::global_dot(residual, testDirection,
|
gravity_prepared_test_utils::global_dot(residual, testDirection, f.mesh->GetComm());
|
||||||
f.mesh->GetComm());
|
|
||||||
|
|
||||||
const double expectedResultant =
|
const double expectedResultant = densityValue * physicalGravity[static_cast<std::size_t>(component)] *
|
||||||
densityValue *
|
|
||||||
physicalGravity[static_cast<std::size_t>(component)] *
|
|
||||||
mapDeterminant * referenceVolume;
|
mapDeterminant * referenceVolume;
|
||||||
|
|
||||||
const double relativeError =
|
const double relativeError = gravity_displacement_force_analytic_test_utils::relative_scalar_error(
|
||||||
gravity_displacement_force_analytic_test_utils::
|
computedResultant, expectedResultant
|
||||||
relative_scalar_error(computedResultant, expectedResultant);
|
);
|
||||||
|
|
||||||
CAPTURE(component);
|
CAPTURE(component);
|
||||||
INFO("Map determinant = " << mapDeterminant);
|
INFO("Map determinant = " << mapDeterminant);
|
||||||
@@ -267,8 +266,8 @@ TEST_CASE("Gravity Displacement Force Matches Analytic Affine Resultants",
|
|||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Gravity Displacement Force Reproduces Analytic Homogeneous Sphere Work",
|
"Gravity Displacement Force Reproduces Analytic Homogeneous Sphere Work",
|
||||||
tags::gravity &tags::accuracy &tags::analytic_comparison
|
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration
|
||||||
&tags::integration) {
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -278,21 +277,15 @@ TEST_CASE(
|
|||||||
|
|
||||||
const double radius = mean_field::utils::RADIUS;
|
const double radius = mean_field::utils::RADIUS;
|
||||||
const double mass = mean_field::utils::MASS;
|
const double mass = mean_field::utils::MASS;
|
||||||
const double volume =
|
const double volume = gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(radius);
|
||||||
gravity_displacement_force_analytic_test_utils::analytic_sphere_volume(
|
|
||||||
radius);
|
|
||||||
|
|
||||||
const double densityValue = mass / volume;
|
const double densityValue = mass / volume;
|
||||||
const double radialGravityCoefficient =
|
const double radialGravityCoefficient = mean_field::utils::G * mass / (radius * radius * radius);
|
||||||
mean_field::utils::G * mass / (radius * radius * radius);
|
|
||||||
|
|
||||||
const mfem::Vector density =
|
const mfem::Vector density = gravity_displacement_force_analytic_test_utils::make_constant_density(f, densityValue);
|
||||||
gravity_displacement_force_analytic_test_utils::make_constant_density(
|
|
||||||
f, densityValue);
|
|
||||||
|
|
||||||
const mfem::Vector gravityGradient =
|
const mfem::Vector gravityGradient =
|
||||||
gravity_displacement_force_analytic_test_utils::make_radial_gravity(
|
gravity_displacement_force_analytic_test_utils::make_radial_gravity(f, radialGravityCoefficient);
|
||||||
f, radialGravityCoefficient);
|
|
||||||
|
|
||||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||||
displacement = 0.0;
|
displacement = 0.0;
|
||||||
@@ -300,22 +293,18 @@ TEST_CASE(
|
|||||||
mfem::Vector residual;
|
mfem::Vector residual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, density, gravityGradient, displacement,
|
f, *f.domainMapperStateless, density, gravityGradient, displacement, residual
|
||||||
residual);
|
);
|
||||||
|
|
||||||
const mfem::Vector dilationDirection =
|
const mfem::Vector dilationDirection =
|
||||||
gravity_displacement_force_analytic_test_utils::
|
gravity_displacement_force_analytic_test_utils::make_dilation_test_direction(f);
|
||||||
make_dilation_test_direction(f);
|
|
||||||
|
|
||||||
const double computedWork = gravity_prepared_test_utils::global_dot(
|
const double computedWork = gravity_prepared_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm());
|
||||||
residual, dilationDirection, f.mesh->GetComm());
|
|
||||||
|
|
||||||
const double analyticWork =
|
const double analyticWork = (3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
|
||||||
(3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
|
|
||||||
|
|
||||||
const double relativeError =
|
const double relativeError =
|
||||||
gravity_displacement_force_analytic_test_utils::relative_scalar_error(
|
gravity_displacement_force_analytic_test_utils::relative_scalar_error(computedWork, analyticWork);
|
||||||
computedWork, analyticWork);
|
|
||||||
|
|
||||||
INFO("Computed positive gravity work = " << computedWork);
|
INFO("Computed positive gravity work = " << computedWork);
|
||||||
INFO("Analytic positive gravity work = " << analyticWork);
|
INFO("Analytic positive gravity work = " << analyticWork);
|
||||||
@@ -327,9 +316,10 @@ TEST_CASE(
|
|||||||
CHECK(relativeError < 1.0e-5);
|
CHECK(relativeError < 1.0e-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
|
TEST_CASE(
|
||||||
tags::gravity &tags::accuracy &tags::analytic_comparison
|
"Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
|
||||||
&tags::integration &tags::initialization) {
|
tags::gravity &tags::accuracy &tags::analytic_comparison &tags::integration &tags::initialization
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
args.p.rtol = 1.0e-13;
|
args.p.rtol = 1.0e-13;
|
||||||
args.p.max_iters = std::max(args.p.max_iters, 1000);
|
args.p.max_iters = std::max(args.p.max_iters, 1000);
|
||||||
@@ -350,12 +340,10 @@ TEST_CASE("Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
|
|||||||
|
|
||||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||||
|
|
||||||
gravity_displacement_force_analytic_test_utils::set_mass_normalized_density(
|
gravity_displacement_force_analytic_test_utils::set_mass_normalized_density(f, mass, densityField);
|
||||||
f, mass, densityField);
|
|
||||||
|
|
||||||
const mean_field::physics::GravitySolution gravitySolution =
|
const mean_field::physics::GravitySolution gravitySolution =
|
||||||
mean_field::physics::solve_gravity_field(f, args, densityField,
|
mean_field::physics::solve_gravity_field(f, args, densityField, displacementField);
|
||||||
displacementField);
|
|
||||||
|
|
||||||
mfem::Vector densityTrue;
|
mfem::Vector densityTrue;
|
||||||
mfem::Vector gravityGradientTrue;
|
mfem::Vector gravityGradientTrue;
|
||||||
@@ -368,22 +356,18 @@ TEST_CASE("Solved Homogeneous Sphere Gravity Force Matches Analytic Virial",
|
|||||||
mfem::Vector residual;
|
mfem::Vector residual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
mean_field::operators::kernels::apply_gravity_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, densityTrue, gravityGradientTrue,
|
f, *f.domainMapperStateless, densityTrue, gravityGradientTrue, displacementTrue, residual
|
||||||
displacementTrue, residual);
|
);
|
||||||
|
|
||||||
const mfem::Vector dilationDirection =
|
const mfem::Vector dilationDirection =
|
||||||
gravity_displacement_force_analytic_test_utils::
|
gravity_displacement_force_analytic_test_utils::make_dilation_test_direction(f);
|
||||||
make_dilation_test_direction(f);
|
|
||||||
|
|
||||||
const double computedWork = gravity_prepared_test_utils::global_dot(
|
const double computedWork = gravity_prepared_test_utils::global_dot(residual, dilationDirection, f.mesh->GetComm());
|
||||||
residual, dilationDirection, f.mesh->GetComm());
|
|
||||||
|
|
||||||
const double analyticWork =
|
const double analyticWork = (3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
|
||||||
(3.0 / 5.0) * mean_field::utils::G * mass * mass / radius;
|
|
||||||
|
|
||||||
const double relativeError =
|
const double relativeError =
|
||||||
gravity_displacement_force_analytic_test_utils::relative_scalar_error(
|
gravity_displacement_force_analytic_test_utils::relative_scalar_error(computedWork, analyticWork);
|
||||||
computedWork, analyticWork);
|
|
||||||
|
|
||||||
INFO("Solved-field positive gravity work = " << computedWork);
|
INFO("Solved-field positive gravity work = " << computedWork);
|
||||||
INFO("Analytic positive gravity work = " << analyticWork);
|
INFO("Analytic positive gravity work = " << analyticWork);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -72,7 +72,10 @@ TEST_CASE(
|
|||||||
|
|
||||||
constexpr double enthalpyValue = 0.8;
|
constexpr double enthalpyValue = 0.8;
|
||||||
|
|
||||||
const double densityValue = barotrope.density_from_enthalpy(enthalpyValue);
|
const double densityValue = mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{enthalpyValue}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
const mfem::Vector enthalpy = project_constant(*f.enthalpyFes, enthalpyValue);
|
const mfem::Vector enthalpy = project_constant(*f.enthalpyFes, enthalpyValue);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,48 +10,43 @@ import mean_field;
|
|||||||
import test_helpers;
|
import test_helpers;
|
||||||
|
|
||||||
namespace pressure_force_kernel_test_utils {
|
namespace pressure_force_kernel_test_utils {
|
||||||
[[nodiscard]] mfem::Vector make_deterministic_vector(const int size,
|
[[nodiscard]] mfem::Vector make_deterministic_vector(
|
||||||
const double phase) {
|
const int size,
|
||||||
|
const double phase
|
||||||
|
) {
|
||||||
mfem::Vector vector(size);
|
mfem::Vector vector(size);
|
||||||
|
|
||||||
for (int index = 0; index < size; ++index) {
|
for (int index = 0; index < size; ++index) {
|
||||||
const double position = static_cast<double>(index + 1);
|
const double position = static_cast<double>(index + 1);
|
||||||
|
|
||||||
vector(index) = 0.71 + 0.19 * std::sin(0.31 * position + phase) +
|
vector(index) =
|
||||||
0.08 * std::cos(0.17 * position - 0.5 * phase);
|
0.71 + 0.19 * std::sin(0.31 * position + phase) + 0.08 * std::cos(0.17 * position - 0.5 * phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_zero_displacement(const mean_field::fem::FEM &f) {
|
||||||
make_zero_displacement(const mean_field::fem::FEM &f) {
|
|
||||||
mfem::Vector displacementTrue(f.displacementFes->GetTrueVSize());
|
mfem::Vector displacementTrue(f.displacementFes->GetTrueVSize());
|
||||||
displacementTrue = 0.0;
|
displacementTrue = 0.0;
|
||||||
return displacementTrue;
|
return displacementTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) {
|
||||||
make_vacuum_only_enthalpy(const mean_field::fem::FEM &f) {
|
mfem::Vector enthalpyTrue = make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43);
|
||||||
mfem::Vector enthalpyTrue =
|
|
||||||
make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.43);
|
|
||||||
|
|
||||||
using DomainSchema =
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
||||||
const mean_field::field::FieldDofMap enthalpyMap =
|
const mean_field::field::FieldDofMap enthalpyMap =
|
||||||
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy,
|
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes);
|
||||||
DomainSchema>(*f.enthalpyFes);
|
|
||||||
|
|
||||||
for (int reducedDof = 0; reducedDof < enthalpyMap.reduced_size();
|
for (int reducedDof = 0; reducedDof < enthalpyMap.reduced_size(); ++reducedDof) {
|
||||||
++reducedDof) {
|
|
||||||
enthalpyTrue(enthalpyMap.true_dof(reducedDof)) = 0.0;
|
enthalpyTrue(enthalpyMap.true_dof(reducedDof)) = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return enthalpyTrue;
|
return enthalpyTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) {
|
||||||
make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) {
|
|
||||||
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
|
mfem::FunctionCoefficient coefficient([](const mfem::Vector &position) {
|
||||||
return 1.10 + 0.07 * position(0) - 0.04 * position(1) + 0.03 * position(2);
|
return 1.10 + 0.07 * position(0) - 0.04 * position(1) + 0.03 * position(2);
|
||||||
});
|
});
|
||||||
@@ -64,18 +59,18 @@ make_positive_asymmetric_enthalpy(const mean_field::fem::FEM &f) {
|
|||||||
enthalpyField.GetTrueDofs(enthalpyTrue);
|
enthalpyField.GetTrueDofs(enthalpyTrue);
|
||||||
|
|
||||||
return enthalpyTrue;
|
return enthalpyTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_component_test_field(
|
||||||
make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
const mean_field::fem::FEM &f,
|
||||||
const int coordinate) {
|
const int component,
|
||||||
|
const int coordinate
|
||||||
|
) {
|
||||||
const int dimension = f.mesh->Dimension();
|
const int dimension = f.mesh->Dimension();
|
||||||
|
|
||||||
MFEM_VERIFY(component >= 0 && component < dimension,
|
MFEM_VERIFY(component >= 0 && component < dimension, "The requested vector component is invalid.");
|
||||||
"The requested vector component is invalid.");
|
|
||||||
|
|
||||||
MFEM_VERIFY(coordinate >= -1 && coordinate < dimension,
|
MFEM_VERIFY(coordinate >= -1 && coordinate < dimension, "The requested coordinate is invalid.");
|
||||||
"The requested coordinate is invalid.");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* coordinate == -1 gives the rigid translation e_component.
|
* coordinate == -1 gives the rigid translation e_component.
|
||||||
@@ -85,13 +80,13 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
* w = x_coordinate e_component.
|
* w = x_coordinate e_component.
|
||||||
*/
|
*/
|
||||||
mfem::VectorFunctionCoefficient coefficient(
|
mfem::VectorFunctionCoefficient coefficient(
|
||||||
dimension, [component, coordinate, dimension](
|
dimension, [component, coordinate, dimension](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
const mfem::Vector &position, mfem::Vector &value) {
|
|
||||||
value.SetSize(dimension);
|
value.SetSize(dimension);
|
||||||
value = 0.0;
|
value = 0.0;
|
||||||
|
|
||||||
value(component) = coordinate < 0 ? 1.0 : position(coordinate);
|
value(component) = coordinate < 0 ? 1.0 : position(coordinate);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
mfem::ParGridFunction field(f.displacementFes.get());
|
mfem::ParGridFunction field(f.displacementFes.get());
|
||||||
|
|
||||||
@@ -101,13 +96,14 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
field.GetTrueDofs(fieldTrue);
|
field.GetTrueDofs(fieldTrue);
|
||||||
|
|
||||||
return fieldTrue;
|
return fieldTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double global_dot(const mfem::Vector &left,
|
[[nodiscard]] double global_dot(
|
||||||
|
const mfem::Vector &left,
|
||||||
const mfem::Vector &right,
|
const mfem::Vector &right,
|
||||||
MPI_Comm communicator) {
|
MPI_Comm communicator
|
||||||
MFEM_VERIFY(left.Size() == right.Size(),
|
) {
|
||||||
"The global dot-product vectors have different sizes.");
|
MFEM_VERIFY(left.Size() == right.Size(), "The global dot-product vectors have different sizes.");
|
||||||
|
|
||||||
const double localDot = left * right;
|
const double localDot = left * right;
|
||||||
double globalDot = 0.0;
|
double globalDot = 0.0;
|
||||||
@@ -115,23 +111,28 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||||
|
|
||||||
return globalDot;
|
return globalDot;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double integrate_pressure(
|
[[nodiscard]] double integrate_pressure(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::mapping::DomainMapper &domainMapper,
|
const mean_field::mapping::DomainMapper &domainMapper,
|
||||||
const mean_field::eos::Polytrope &barotrope,
|
const mean_field::eos::Polytrope &barotrope,
|
||||||
const mfem::Vector &enthalpyTrue, const mfem::Vector &displacementTrue) {
|
const mfem::Vector &enthalpyTrue,
|
||||||
MFEM_VERIFY(enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
|
const mfem::Vector &displacementTrue
|
||||||
"The pressure-integral enthalpy vector has the wrong size.");
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
enthalpyTrue.Size() == f.enthalpyFes->GetTrueVSize(),
|
||||||
|
"The pressure-integral enthalpy vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
MFEM_VERIFY(displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
MFEM_VERIFY(
|
||||||
"The pressure-integral displacement vector has the wrong size.");
|
displacementTrue.Size() == f.displacementFes->GetTrueVSize(),
|
||||||
|
"The pressure-integral displacement vector has the wrong size."
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector enthalpyLocal(f.enthalpyFes->GetVSize());
|
mfem::Vector enthalpyLocal(f.enthalpyFes->GetVSize());
|
||||||
|
|
||||||
const mfem::Operator *enthalpyProlongation =
|
const mfem::Operator *enthalpyProlongation = f.enthalpyFes->GetProlongationMatrix();
|
||||||
f.enthalpyFes->GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (enthalpyProlongation != nullptr) {
|
if (enthalpyProlongation != nullptr) {
|
||||||
enthalpyProlongation->Mult(enthalpyTrue, enthalpyLocal);
|
enthalpyProlongation->Mult(enthalpyTrue, enthalpyLocal);
|
||||||
@@ -141,8 +142,7 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
|
|
||||||
mfem::Vector displacementLocal(f.displacementFes->GetVSize());
|
mfem::Vector displacementLocal(f.displacementFes->GetVSize());
|
||||||
|
|
||||||
const mfem::Operator *displacementProlongation =
|
const mfem::Operator *displacementProlongation = f.displacementFes->GetProlongationMatrix();
|
||||||
f.displacementFes->GetProlongationMatrix();
|
|
||||||
|
|
||||||
if (displacementProlongation != nullptr) {
|
if (displacementProlongation != nullptr) {
|
||||||
displacementProlongation->Mult(displacementTrue, displacementLocal);
|
displacementProlongation->Mult(displacementTrue, displacementLocal);
|
||||||
@@ -151,22 +151,19 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const double pressureExtraOrderValue =
|
const double pressureExtraOrderValue =
|
||||||
barotrope.polytropic_index() *
|
barotrope.polytropic_index() * static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
|
||||||
static_cast<double>(mean_field::field::Enthalpy::Scalar::familyOrder);
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(pressureExtraOrderValue) &&
|
MFEM_VERIFY(
|
||||||
pressureExtraOrderValue >= 0.0 &&
|
std::isfinite(pressureExtraOrderValue) && pressureExtraOrderValue >= 0.0 &&
|
||||||
pressureExtraOrderValue <=
|
pressureExtraOrderValue <= static_cast<double>(std::numeric_limits<int>::max()),
|
||||||
static_cast<double>(std::numeric_limits<int>::max()),
|
"The pressure-integral EOS order is invalid."
|
||||||
"The pressure-integral EOS order is invalid.");
|
);
|
||||||
|
|
||||||
const int pressureExtraOrder =
|
const int pressureExtraOrder = static_cast<int>(std::ceil(pressureExtraOrderValue));
|
||||||
static_cast<int>(std::ceil(pressureExtraOrderValue));
|
|
||||||
|
|
||||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||||
|
|
||||||
mean_field::mapping::DomainMapper::Workspace workspace(
|
mean_field::mapping::DomainMapper::Workspace workspace(f.mesh->Dimension());
|
||||||
f.mesh->Dimension());
|
|
||||||
|
|
||||||
mean_field::mapping::VolumeMappingContext mappingContext;
|
mean_field::mapping::VolumeMappingContext mappingContext;
|
||||||
|
|
||||||
@@ -184,28 +181,24 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
|
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
|
||||||
|
|
||||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||||
mfem::ElementTransformation *transformation =
|
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||||
f.mesh->GetElementTransformation(elementId);
|
|
||||||
|
|
||||||
MFEM_VERIFY(transformation != nullptr,
|
MFEM_VERIFY(
|
||||||
"The pressure-integral reference received a null "
|
transformation != nullptr, "The pressure-integral reference received a null "
|
||||||
"element transformation.");
|
"element transformation."
|
||||||
|
);
|
||||||
|
|
||||||
if (transformation->Attribute == vacuumAttribute) {
|
if (transformation->Attribute == vacuumAttribute) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::FiniteElement &enthalpyElement =
|
const mfem::FiniteElement &enthalpyElement = *f.enthalpyFes->GetFE(elementId);
|
||||||
*f.enthalpyFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &displacementElement =
|
const mfem::FiniteElement &displacementElement = *f.displacementFes->GetFE(elementId);
|
||||||
*f.displacementFes->GetFE(elementId);
|
|
||||||
|
|
||||||
const mfem::FiniteElement &compactificationElement =
|
const mfem::FiniteElement &compactificationElement = *f.compactificationFes->GetFE(elementId);
|
||||||
*f.compactificationFes->GetFE(elementId);
|
|
||||||
|
|
||||||
mfem::DofTransformation *enthalpyDofTransformation =
|
mfem::DofTransformation *enthalpyDofTransformation = f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
||||||
f.enthalpyFes->GetElementDofs(elementId, enthalpyDofs);
|
|
||||||
|
|
||||||
mfem::DofTransformation *displacementDofTransformation =
|
mfem::DofTransformation *displacementDofTransformation =
|
||||||
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
f.displacementFes->GetElementVDofs(elementId, displacementDofs);
|
||||||
@@ -217,8 +210,7 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
|
|
||||||
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
displacementLocal.GetSubVector(displacementDofs, elementDisplacement);
|
||||||
|
|
||||||
f.compactificationCoordinate->GetSubVector(compactificationDofs,
|
f.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
|
|
||||||
if (enthalpyDofTransformation != nullptr) {
|
if (enthalpyDofTransformation != nullptr) {
|
||||||
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
|
enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
|
||||||
@@ -229,70 +221,66 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (compactificationDofTransformation != nullptr) {
|
if (compactificationDofTransformation != nullptr) {
|
||||||
compactificationDofTransformation->InvTransformPrimal(
|
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
||||||
elementCompactification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mean_field::mapping::ElementDisplacementData displacementData =
|
const mean_field::mapping::ElementDisplacementData displacementData =
|
||||||
mean_field::mapping::ElementDisplacementDataFromElementVDofs(
|
mean_field::mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementDisplacement);
|
||||||
displacementElement, elementDisplacement);
|
|
||||||
|
|
||||||
const mean_field::mapping::ElementCompactificationData compactificationData(
|
const mean_field::mapping::ElementCompactificationData compactificationData(
|
||||||
compactificationElement, elementCompactification);
|
compactificationElement, elementCompactification
|
||||||
|
);
|
||||||
|
|
||||||
const mean_field::mapping::ElementMappingData mappingData{
|
const mean_field::mapping::ElementMappingData mappingData{
|
||||||
.displacement = displacementData,
|
.displacement = displacementData, .compactification = compactificationData
|
||||||
.compactification = compactificationData};
|
};
|
||||||
|
|
||||||
const mean_field::quadrature::Query query = EnthalpyField::make_query<
|
const mean_field::quadrature::Query query =
|
||||||
mean_field::field::Enthalpy::Form::PressureIntegral>(
|
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
|
||||||
mean_field::quadrature::QuadratureRole::diagnostic,
|
mean_field::quadrature::QuadratureRole::diagnostic, transformation->OrderW(),
|
||||||
transformation->OrderW(), std::array<int, 1>{pressureExtraOrder},
|
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
mean_field::quadrature::MappingKind::general
|
||||||
mean_field::quadrature::MappingKind::general);
|
);
|
||||||
|
|
||||||
const mean_field::quadrature::MfemRule rule =
|
const mean_field::quadrature::MfemRule rule =
|
||||||
f.quadratureFactory->get(query, transformation->GetGeometryType());
|
f.quadratureFactory->get(query, transformation->GetGeometryType());
|
||||||
|
|
||||||
MFEM_VERIFY(rule.integration_rule != nullptr,
|
MFEM_VERIFY(rule.integration_rule != nullptr, "The pressure-integral quadrature rule is null.");
|
||||||
"The pressure-integral quadrature rule is null.");
|
|
||||||
|
|
||||||
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
enthalpyShape.SetSize(enthalpyElement.GetDof());
|
||||||
|
|
||||||
for (int quadratureIndex = 0;
|
for (int quadratureIndex = 0; quadratureIndex < rule.integration_rule->GetNPoints(); ++quadratureIndex) {
|
||||||
quadratureIndex < rule.integration_rule->GetNPoints();
|
const mfem::IntegrationPoint &integrationPoint = rule.integration_rule->IntPoint(quadratureIndex);
|
||||||
++quadratureIndex) {
|
|
||||||
const mfem::IntegrationPoint &integrationPoint =
|
|
||||||
rule.integration_rule->IntPoint(quadratureIndex);
|
|
||||||
|
|
||||||
transformation->SetIntPoint(&integrationPoint);
|
transformation->SetIntPoint(&integrationPoint);
|
||||||
|
|
||||||
const mean_field::mapping::MappingStatus mappingStatus =
|
const mean_field::mapping::MappingStatus mappingStatus = domainMapper.EvaluateVolume(
|
||||||
domainMapper.EvaluateVolume(mappingData, *transformation,
|
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||||
integrationPoint, workspace,
|
);
|
||||||
mappingContext);
|
|
||||||
|
|
||||||
MFEM_VERIFY(mappingStatus == mean_field::mapping::MappingStatus::valid,
|
MFEM_VERIFY(
|
||||||
|
mappingStatus == mean_field::mapping::MappingStatus::valid,
|
||||||
"Stateless mapping failed in the "
|
"Stateless mapping failed in the "
|
||||||
"independent pressure integral. Element: "
|
"independent pressure integral. Element: "
|
||||||
<< elementId
|
<< elementId << ", attribute: " << transformation->Attribute
|
||||||
<< ", attribute: " << transformation->Attribute
|
<< ", quadrature point: " << quadratureIndex << ", status: " << static_cast<int>(mappingStatus)
|
||||||
<< ", quadrature point: " << quadratureIndex
|
);
|
||||||
<< ", status: " << static_cast<int>(mappingStatus));
|
|
||||||
|
|
||||||
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
||||||
|
|
||||||
const double enthalpyValue = elementEnthalpy * enthalpyShape;
|
const double enthalpyValue = elementEnthalpy * enthalpyShape;
|
||||||
|
|
||||||
const double pressureValue =
|
const double pressureValue = mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(
|
||||||
barotrope.pressure_from_enthalpy(enthalpyValue);
|
barotrope, mean_field::eos::SpecificEnthalpyValue{enthalpyValue}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
const double contribution =
|
const double contribution = pressureValue * mappingContext.quadrature.weight;
|
||||||
pressureValue * mappingContext.quadrature.weight;
|
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(pressureValue) && std::isfinite(contribution),
|
MFEM_VERIFY(
|
||||||
"The independent pressure integral "
|
std::isfinite(pressureValue) && std::isfinite(contribution), "The independent pressure integral "
|
||||||
"encountered a non-finite value.");
|
"encountered a non-finite value."
|
||||||
|
);
|
||||||
|
|
||||||
localPressureIntegral += contribution;
|
localPressureIntegral += contribution;
|
||||||
}
|
}
|
||||||
@@ -300,15 +288,16 @@ make_component_test_field(const mean_field::fem::FEM &f, const int component,
|
|||||||
|
|
||||||
double globalPressureIntegral = 0.0;
|
double globalPressureIntegral = 0.0;
|
||||||
|
|
||||||
MPI_Allreduce(&localPressureIntegral, &globalPressureIntegral, 1, MPI_DOUBLE,
|
MPI_Allreduce(&localPressureIntegral, &globalPressureIntegral, 1, MPI_DOUBLE, MPI_SUM, f.mesh->GetComm());
|
||||||
MPI_SUM, f.mesh->GetComm());
|
|
||||||
|
|
||||||
return globalPressureIntegral;
|
return globalPressureIntegral;
|
||||||
}
|
}
|
||||||
} // namespace pressure_force_kernel_test_utils
|
} // namespace pressure_force_kernel_test_utils
|
||||||
|
|
||||||
TEST_CASE("Pressure Force Residual Vanishes For Zero Enthalpy",
|
TEST_CASE(
|
||||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
|
"Pressure Force Residual Vanishes For Zero Enthalpy",
|
||||||
|
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -320,25 +309,25 @@ TEST_CASE("Pressure Force Residual Vanishes For Zero Enthalpy",
|
|||||||
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
|
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
|
||||||
enthalpyTrue = 0.0;
|
enthalpyTrue = 0.0;
|
||||||
|
|
||||||
const mfem::Vector displacementTrue =
|
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
|
||||||
|
|
||||||
mfem::Vector residualTrue;
|
mfem::Vector residualTrue;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||||
residualTrue);
|
);
|
||||||
|
|
||||||
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
||||||
|
|
||||||
const double residualNorm =
|
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
||||||
gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
|
||||||
|
|
||||||
CHECK(residualNorm == 0.0);
|
CHECK(residualNorm == 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Pressure Force Residual Excludes Vacuum Enthalpy Exactly",
|
TEST_CASE(
|
||||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
|
"Pressure Force Residual Excludes Vacuum Enthalpy Exactly",
|
||||||
|
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -347,11 +336,9 @@ TEST_CASE("Pressure Force Residual Excludes Vacuum Enthalpy Exactly",
|
|||||||
|
|
||||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||||
|
|
||||||
const mfem::Vector enthalpyTrue =
|
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_vacuum_only_enthalpy(f);
|
||||||
pressure_force_kernel_test_utils::make_vacuum_only_enthalpy(f);
|
|
||||||
|
|
||||||
const double enthalpyNorm =
|
const double enthalpyNorm = gravity_prepared_test_utils::global_norm(enthalpyTrue, f.mesh->GetComm());
|
||||||
gravity_prepared_test_utils::global_norm(enthalpyTrue, f.mesh->GetComm());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ensure this is a real exclusion test rather than another
|
* Ensure this is a real exclusion test rather than another
|
||||||
@@ -359,25 +346,25 @@ TEST_CASE("Pressure Force Residual Excludes Vacuum Enthalpy Exactly",
|
|||||||
*/
|
*/
|
||||||
REQUIRE(enthalpyNorm > 0.0);
|
REQUIRE(enthalpyNorm > 0.0);
|
||||||
|
|
||||||
const mfem::Vector displacementTrue =
|
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
|
||||||
|
|
||||||
mfem::Vector residualTrue;
|
mfem::Vector residualTrue;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||||
residualTrue);
|
);
|
||||||
|
|
||||||
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
||||||
|
|
||||||
const double residualNorm =
|
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
||||||
gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
|
||||||
|
|
||||||
CHECK(residualNorm == 0.0);
|
CHECK(residualNorm == 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Pressure Force Residual Is Nonzero For Positive Stellar Pressure",
|
TEST_CASE(
|
||||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration) {
|
"Pressure Force Residual Is Nonzero For Positive Stellar Pressure",
|
||||||
|
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -394,17 +381,15 @@ TEST_CASE("Pressure Force Residual Is Nonzero For Positive Stellar Pressure",
|
|||||||
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
|
mfem::Vector enthalpyTrue(f.enthalpyFes->GetTrueVSize());
|
||||||
enthalpyTrue = 1.0;
|
enthalpyTrue = 1.0;
|
||||||
|
|
||||||
const mfem::Vector displacementTrue =
|
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
|
||||||
|
|
||||||
mfem::Vector residualTrue;
|
mfem::Vector residualTrue;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||||
residualTrue);
|
);
|
||||||
|
|
||||||
const double residualNorm =
|
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
||||||
gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
|
||||||
|
|
||||||
INFO("Positive-pressure residual norm = " << residualNorm);
|
INFO("Positive-pressure residual norm = " << residualNorm);
|
||||||
|
|
||||||
@@ -413,9 +398,10 @@ TEST_CASE("Pressure Force Residual Is Nonzero For Positive Stellar Pressure",
|
|||||||
CHECK(residualNorm > 100.0 * std::numeric_limits<double>::epsilon());
|
CHECK(residualNorm > 100.0 * std::numeric_limits<double>::epsilon());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Pressure Force Residual Does No Work Against Rigid Translations",
|
TEST_CASE(
|
||||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
"Pressure Force Residual Does No Work Against Rigid Translations",
|
||||||
&tags::accuracy) {
|
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -426,20 +412,17 @@ TEST_CASE("Pressure Force Residual Does No Work Against Rigid Translations",
|
|||||||
|
|
||||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||||
|
|
||||||
const mfem::Vector enthalpyTrue =
|
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
|
||||||
pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
|
|
||||||
|
|
||||||
const mfem::Vector displacementTrue =
|
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
|
||||||
|
|
||||||
mfem::Vector residualTrue;
|
mfem::Vector residualTrue;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||||
residualTrue);
|
);
|
||||||
|
|
||||||
const double residualNorm =
|
const double residualNorm = gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
||||||
gravity_prepared_test_utils::global_norm(residualTrue, f.mesh->GetComm());
|
|
||||||
|
|
||||||
REQUIRE(residualNorm > 0.0);
|
REQUIRE(residualNorm > 0.0);
|
||||||
|
|
||||||
@@ -447,17 +430,14 @@ TEST_CASE("Pressure Force Residual Does No Work Against Rigid Translations",
|
|||||||
|
|
||||||
for (int component = 0; component < dimension; ++component) {
|
for (int component = 0; component < dimension; ++component) {
|
||||||
const mfem::Vector translationTrue =
|
const mfem::Vector translationTrue =
|
||||||
pressure_force_kernel_test_utils::make_component_test_field(
|
pressure_force_kernel_test_utils::make_component_test_field(f, component, -1);
|
||||||
f, component, -1);
|
|
||||||
|
|
||||||
const double translationNorm = gravity_prepared_test_utils::global_norm(
|
const double translationNorm = gravity_prepared_test_utils::global_norm(translationTrue, f.mesh->GetComm());
|
||||||
translationTrue, f.mesh->GetComm());
|
|
||||||
|
|
||||||
const double translationWork = pressure_force_kernel_test_utils::global_dot(
|
const double translationWork =
|
||||||
translationTrue, residualTrue, f.mesh->GetComm());
|
pressure_force_kernel_test_utils::global_dot(translationTrue, residualTrue, f.mesh->GetComm());
|
||||||
|
|
||||||
const double dotProductScale =
|
const double dotProductScale = std::fmax(residualNorm * translationNorm, 1.0);
|
||||||
std::fmax(residualNorm * translationNorm, 1.0);
|
|
||||||
|
|
||||||
CAPTURE(component, translationWork, dotProductScale);
|
CAPTURE(component, translationWork, dotProductScale);
|
||||||
|
|
||||||
@@ -465,9 +445,10 @@ TEST_CASE("Pressure Force Residual Does No Work Against Rigid Translations",
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Pressure Force Residual Matches Independent Pressure Integral",
|
TEST_CASE(
|
||||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
"Pressure Force Residual Matches Independent Pressure Integral",
|
||||||
&tags::accuracy) {
|
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -478,17 +459,15 @@ TEST_CASE("Pressure Force Residual Matches Independent Pressure Integral",
|
|||||||
|
|
||||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||||
|
|
||||||
const mfem::Vector enthalpyTrue =
|
const mfem::Vector enthalpyTrue = pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
|
||||||
pressure_force_kernel_test_utils::make_positive_asymmetric_enthalpy(f);
|
|
||||||
|
|
||||||
const mfem::Vector displacementTrue =
|
const mfem::Vector displacementTrue = pressure_force_kernel_test_utils::make_zero_displacement(f);
|
||||||
pressure_force_kernel_test_utils::make_zero_displacement(f);
|
|
||||||
|
|
||||||
mfem::Vector residualTrue;
|
mfem::Vector residualTrue;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue,
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue, residualTrue
|
||||||
residualTrue);
|
);
|
||||||
|
|
||||||
const int dimension = f.mesh->Dimension();
|
const int dimension = f.mesh->Dimension();
|
||||||
|
|
||||||
@@ -499,19 +478,16 @@ TEST_CASE("Pressure Force Residual Matches Independent Pressure Integral",
|
|||||||
for (int component = 0; component < dimension; ++component) {
|
for (int component = 0; component < dimension; ++component) {
|
||||||
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
|
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
|
||||||
const mfem::Vector affineTestTrue =
|
const mfem::Vector affineTestTrue =
|
||||||
pressure_force_kernel_test_utils::make_component_test_field(
|
pressure_force_kernel_test_utils::make_component_test_field(f, component, coordinate);
|
||||||
f, component, coordinate);
|
|
||||||
|
|
||||||
virtualWork(component, coordinate) =
|
virtualWork(component, coordinate) =
|
||||||
pressure_force_kernel_test_utils::global_dot(
|
pressure_force_kernel_test_utils::global_dot(affineTestTrue, residualTrue, f.mesh->GetComm());
|
||||||
affineTestTrue, residualTrue, f.mesh->GetComm());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const double pressureIntegral =
|
const double pressureIntegral = pressure_force_kernel_test_utils::integrate_pressure(
|
||||||
pressure_force_kernel_test_utils::integrate_pressure(
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementTrue
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue,
|
);
|
||||||
displacementTrue);
|
|
||||||
|
|
||||||
REQUIRE(std::isfinite(pressureIntegral));
|
REQUIRE(std::isfinite(pressureIntegral));
|
||||||
|
|
||||||
@@ -545,28 +521,25 @@ TEST_CASE("Pressure Force Residual Matches Independent Pressure Integral",
|
|||||||
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
|
for (int coordinate = 0; coordinate < dimension; ++coordinate) {
|
||||||
const double computedWork = virtualWork(component, coordinate);
|
const double computedWork = virtualWork(component, coordinate);
|
||||||
|
|
||||||
const double expectedWork =
|
const double expectedWork = component == coordinate ? -pressureIntegral : 0.0;
|
||||||
component == coordinate ? -pressureIntegral : 0.0;
|
|
||||||
|
|
||||||
CAPTURE(component, coordinate, computedWork, expectedWork,
|
CAPTURE(component, coordinate, computedWork, expectedWork, pressureIntegral, comparisonTolerance);
|
||||||
pressureIntegral, comparisonTolerance);
|
|
||||||
|
|
||||||
CHECK(std::abs(computedWork - expectedWork) <= comparisonTolerance);
|
CHECK(std::abs(computedWork - expectedWork) <= comparisonTolerance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const double relativeMeanError =
|
const double relativeMeanError = std::abs(meanDiagonalWork + pressureIntegral) / std::abs(pressureIntegral);
|
||||||
std::abs(meanDiagonalWork + pressureIntegral) /
|
|
||||||
std::abs(pressureIntegral);
|
|
||||||
|
|
||||||
INFO("Relative mean diagonal error = " << relativeMeanError);
|
INFO("Relative mean diagonal error = " << relativeMeanError);
|
||||||
|
|
||||||
CHECK(relativeMeanError <= 1.0e-6);
|
CHECK(relativeMeanError <= 1.0e-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
TEST_CASE(
|
||||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
"Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
||||||
&tags::accuracy) {
|
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::accuracy
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -580,16 +553,14 @@ TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
|||||||
* exercises a genuinely nonuniform pressure distribution.
|
* exercises a genuinely nonuniform pressure distribution.
|
||||||
*/
|
*/
|
||||||
const mfem::Vector enthalpyTrue =
|
const mfem::Vector enthalpyTrue =
|
||||||
pressure_force_kernel_test_utils::make_deterministic_vector(
|
pressure_force_kernel_test_utils::make_deterministic_vector(f.enthalpyFes->GetTrueVSize(), 0.37);
|
||||||
f.enthalpyFes->GetTrueVSize(), 0.37);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make_displacement() contains anisotropic diagonal terms and
|
* make_displacement() contains anisotropic diagonal terms and
|
||||||
* quadratic cross terms. A scale of 0.67 therefore provides a
|
* quadratic cross terms. A scale of 0.67 therefore provides a
|
||||||
* nonzero, nonspherical, valid base geometry.
|
* nonzero, nonspherical, valid base geometry.
|
||||||
*/
|
*/
|
||||||
const mfem::Vector baseDisplacementTrue =
|
const mfem::Vector baseDisplacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.67);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.67);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Differentiate along the same smooth deformation family. Thus
|
* Differentiate along the same smooth deformation family. Thus
|
||||||
@@ -599,43 +570,38 @@ TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
|||||||
* This gives a controlled geometry path while still evaluating
|
* This gives a controlled geometry path while still evaluating
|
||||||
* the derivative at a genuinely deformed base state.
|
* the derivative at a genuinely deformed base state.
|
||||||
*/
|
*/
|
||||||
const mfem::Vector displacementVariationTrue =
|
const mfem::Vector displacementVariationTrue = gravity_prepared_test_utils::make_displacement(f, 1.0);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 1.0);
|
|
||||||
|
|
||||||
const double baseDisplacementNorm = gravity_prepared_test_utils::global_norm(
|
const double baseDisplacementNorm =
|
||||||
baseDisplacementTrue, f.mesh->GetComm());
|
gravity_prepared_test_utils::global_norm(baseDisplacementTrue, f.mesh->GetComm());
|
||||||
|
|
||||||
const double variationNorm = gravity_prepared_test_utils::global_norm(
|
const double variationNorm = gravity_prepared_test_utils::global_norm(displacementVariationTrue, f.mesh->GetComm());
|
||||||
displacementVariationTrue, f.mesh->GetComm());
|
|
||||||
|
|
||||||
REQUIRE(baseDisplacementNorm >
|
REQUIRE(baseDisplacementNorm > 100.0 * std::numeric_limits<double>::epsilon());
|
||||||
100.0 * std::numeric_limits<double>::epsilon());
|
|
||||||
|
|
||||||
REQUIRE(variationNorm > 100.0 * std::numeric_limits<double>::epsilon());
|
REQUIRE(variationNorm > 100.0 * std::numeric_limits<double>::epsilon());
|
||||||
|
|
||||||
mfem::Vector residualTrue;
|
mfem::Vector residualTrue;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue,
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, baseDisplacementTrue, residualTrue
|
||||||
baseDisplacementTrue, residualTrue);
|
);
|
||||||
|
|
||||||
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
REQUIRE(residualTrue.Size() == f.displacementFes->GetTrueVSize());
|
||||||
|
|
||||||
const double residualWork = pressure_force_kernel_test_utils::global_dot(
|
const double residualWork =
|
||||||
displacementVariationTrue, residualTrue, f.mesh->GetComm());
|
pressure_force_kernel_test_utils::global_dot(displacementVariationTrue, residualTrue, f.mesh->GetComm());
|
||||||
|
|
||||||
REQUIRE(std::isfinite(residualWork));
|
REQUIRE(std::isfinite(residualWork));
|
||||||
|
|
||||||
REQUIRE(std::abs(residualWork) >
|
REQUIRE(std::abs(residualWork) > 100.0 * std::numeric_limits<double>::epsilon());
|
||||||
100.0 * std::numeric_limits<double>::epsilon());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The relatively broad initial sweep lets us see the expected
|
* The relatively broad initial sweep lets us see the expected
|
||||||
* centered-difference convergence before reaching the quadrature
|
* centered-difference convergence before reaching the quadrature
|
||||||
* and representation plateau.
|
* and representation plateau.
|
||||||
*/
|
*/
|
||||||
constexpr std::array<double, 4> differenceSteps{1.0e-2, 5.0e-3, 2.5e-3,
|
constexpr std::array<double, 4> differenceSteps{1.0e-2, 5.0e-3, 2.5e-3, 1.25e-3};
|
||||||
1.25e-3};
|
|
||||||
|
|
||||||
double bestRelativeDiscrepancy = std::numeric_limits<double>::infinity();
|
double bestRelativeDiscrepancy = std::numeric_limits<double>::infinity();
|
||||||
|
|
||||||
@@ -648,21 +614,18 @@ TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
|||||||
|
|
||||||
displacementMinus.Add(-differenceStep, displacementVariationTrue);
|
displacementMinus.Add(-differenceStep, displacementVariationTrue);
|
||||||
|
|
||||||
const double pressureIntegralPlus =
|
const double pressureIntegralPlus = pressure_force_kernel_test_utils::integrate_pressure(
|
||||||
pressure_force_kernel_test_utils::integrate_pressure(
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementPlus
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue,
|
);
|
||||||
displacementPlus);
|
|
||||||
|
|
||||||
const double pressureIntegralMinus =
|
const double pressureIntegralMinus = pressure_force_kernel_test_utils::integrate_pressure(
|
||||||
pressure_force_kernel_test_utils::integrate_pressure(
|
f, *f.domainMapperStateless, barotrope, enthalpyTrue, displacementMinus
|
||||||
f, *f.domainMapperStateless, barotrope, enthalpyTrue,
|
);
|
||||||
displacementMinus);
|
|
||||||
|
|
||||||
REQUIRE(std::isfinite(pressureIntegralPlus));
|
REQUIRE(std::isfinite(pressureIntegralPlus));
|
||||||
REQUIRE(std::isfinite(pressureIntegralMinus));
|
REQUIRE(std::isfinite(pressureIntegralMinus));
|
||||||
|
|
||||||
const double pressureVolumeDerivative =
|
const double pressureVolumeDerivative = (pressureIntegralPlus - pressureIntegralMinus) / (2.0 * differenceStep);
|
||||||
(pressureIntegralPlus - pressureIntegralMinus) / (2.0 * differenceStep);
|
|
||||||
|
|
||||||
REQUIRE(std::isfinite(pressureVolumeDerivative));
|
REQUIRE(std::isfinite(pressureVolumeDerivative));
|
||||||
|
|
||||||
@@ -674,8 +637,7 @@ TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
|||||||
|
|
||||||
REQUIRE(comparisonScale > 100.0 * std::numeric_limits<double>::epsilon());
|
REQUIRE(comparisonScale > 100.0 * std::numeric_limits<double>::epsilon());
|
||||||
|
|
||||||
const double absoluteDiscrepancy =
|
const double absoluteDiscrepancy = std::abs(residualWork + pressureVolumeDerivative);
|
||||||
std::abs(residualWork + pressureVolumeDerivative);
|
|
||||||
|
|
||||||
const double relativeDiscrepancy = absoluteDiscrepancy / comparisonScale;
|
const double relativeDiscrepancy = absoluteDiscrepancy / comparisonScale;
|
||||||
|
|
||||||
@@ -689,8 +651,7 @@ TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
|||||||
|
|
||||||
INFO("Pressure-volume derivative = " << pressureVolumeDerivative);
|
INFO("Pressure-volume derivative = " << pressureVolumeDerivative);
|
||||||
|
|
||||||
INFO("Residual work plus derivative = " << residualWork +
|
INFO("Residual work plus derivative = " << residualWork + pressureVolumeDerivative);
|
||||||
pressureVolumeDerivative);
|
|
||||||
|
|
||||||
INFO("Relative discrepancy = " << relativeDiscrepancy);
|
INFO("Relative discrepancy = " << relativeDiscrepancy);
|
||||||
|
|
||||||
@@ -702,8 +663,7 @@ TEST_CASE("Pressure Force Residual Matches Deformed Pressure Volume Variation",
|
|||||||
CHECK(residualWork * pressureVolumeDerivative < 0.0);
|
CHECK(residualWork * pressureVolumeDerivative < 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO("Best pressure-volume relative discrepancy = "
|
INFO("Best pressure-volume relative discrepancy = " << bestRelativeDiscrepancy);
|
||||||
<< bestRelativeDiscrepancy);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is intentionally a provisional but meaningful threshold.
|
* This is intentionally a provisional but meaningful threshold.
|
||||||
|
|||||||
@@ -243,7 +243,11 @@ namespace prepared_barotropic_closure_test_utils {
|
|||||||
) {
|
) {
|
||||||
mfem::FunctionCoefficient coefficient([&equationOfState, condition](const mfem::Vector &position) {
|
mfem::FunctionCoefficient coefficient([&equationOfState, condition](const mfem::Vector &position) {
|
||||||
const double enthalpy = evaluate_enthalpy(position, condition);
|
const double enthalpy = evaluate_enthalpy(position, condition);
|
||||||
return condition.densityFactor * equationOfState.density_from_enthalpy(enthalpy) + condition.densityOffset +
|
const double equationOfStateDensity = mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
|
||||||
|
equationOfState, mean_field::eos::SpecificEnthalpyValue{enthalpy}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
return condition.densityFactor * equationOfStateDensity + condition.densityOffset +
|
||||||
condition.densityGradient * (0.40 * position(0) + 0.25 * position(1) - 0.15 * position(2));
|
condition.densityGradient * (0.40 * position(0) + 0.25 * position(1) - 0.15 * position(2));
|
||||||
});
|
});
|
||||||
return project_scalar(*f.densityFes, coefficient);
|
return project_scalar(*f.densityFes, coefficient);
|
||||||
@@ -785,7 +789,11 @@ namespace prepared_barotropic_closure_test_utils {
|
|||||||
const mean_field::eos::Polytrope equationOfState(3.0, 1.5);
|
const mean_field::eos::Polytrope equationOfState(3.0, 1.5);
|
||||||
|
|
||||||
constexpr double enthalpyValue = 1.20;
|
constexpr double enthalpyValue = 1.20;
|
||||||
const double equilibriumDensityValue = equationOfState.density_from_enthalpy(enthalpyValue);
|
const double equilibriumDensityValue =
|
||||||
|
mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
|
||||||
|
equationOfState, mean_field::eos::SpecificEnthalpyValue{enthalpyValue}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
const mfem::Vector enthalpy = reduce(maps.enthalpy, make_constant_field(*f.enthalpyFes, enthalpyValue));
|
const mfem::Vector enthalpy = reduce(maps.enthalpy, make_constant_field(*f.enthalpyFes, enthalpyValue));
|
||||||
const mfem::Vector equilibriumDensity =
|
const mfem::Vector equilibriumDensity =
|
||||||
|
|||||||
@@ -10,33 +10,28 @@ using namespace mean_field;
|
|||||||
using Catch::Matchers::WithinAbs;
|
using Catch::Matchers::WithinAbs;
|
||||||
namespace prepared_test = gravity_prepared_test_utils;
|
namespace prepared_test = gravity_prepared_test_utils;
|
||||||
|
|
||||||
TEST_CASE("Prepared Mapped Hdiv Mass Matches Stateless Kernel",
|
TEST_CASE(
|
||||||
tags::gravity_prepared) {
|
"Prepared Mapped Hdiv Mass Matches Stateless Kernel",
|
||||||
|
tags::gravity_prepared
|
||||||
|
) {
|
||||||
auto args = test_utils::setup_args();
|
auto args = test_utils::setup_args();
|
||||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
operators::PreparedMappedHDivMassOperator prepared_operator(
|
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless);
|
||||||
f, *f.domainMapperStateless);
|
REQUIRE(prepared_operator.Width() == prepared_operator.GetFluxMap().reduced_size());
|
||||||
REQUIRE(prepared_operator.Width() ==
|
REQUIRE(prepared_operator.Height() == prepared_operator.GetFluxMap().reduced_size());
|
||||||
prepared_operator.GetFluxMap().reduced_size());
|
|
||||||
REQUIRE(prepared_operator.Height() ==
|
|
||||||
prepared_operator.GetFluxMap().reduced_size());
|
|
||||||
|
|
||||||
const mfem::Vector gravity_gradient_true =
|
const mfem::Vector gravity_gradient_true =
|
||||||
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
|
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.21);
|
||||||
0.21);
|
const mfem::Vector gravity_gradient = prepared_operator.GetFluxMap().gather(gravity_gradient_true);
|
||||||
const mfem::Vector gravity_gradient =
|
|
||||||
prepared_operator.GetFluxMap().gather(gravity_gradient_true);
|
|
||||||
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
|
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
|
||||||
|
|
||||||
mfem::Vector identity_action;
|
mfem::Vector identity_action;
|
||||||
mfem::Vector deformed_action;
|
mfem::Vector deformed_action;
|
||||||
|
|
||||||
for (const double deformation_scale : {0.0, 1.0}) {
|
for (const double deformation_scale : {0.0, 1.0}) {
|
||||||
const mfem::Vector displacement_true =
|
const mfem::Vector displacement_true = prepared_test::make_displacement(f, deformation_scale);
|
||||||
prepared_test::make_displacement(f, deformation_scale);
|
const mfem::Vector displacement = prepared_operator.GetDisplacementMap().gather(displacement_true);
|
||||||
const mfem::Vector displacement =
|
|
||||||
prepared_operator.GetDisplacementMap().gather(displacement_true);
|
|
||||||
|
|
||||||
prepared_operator.Prepare(displacement);
|
prepared_operator.Prepare(displacement);
|
||||||
|
|
||||||
@@ -45,19 +40,15 @@ TEST_CASE("Prepared Mapped Hdiv Mass Matches Stateless Kernel",
|
|||||||
prepared_operator.Mult(gravity_gradient, prepared_action);
|
prepared_operator.Mult(gravity_gradient, prepared_action);
|
||||||
mfem::Vector reference_action_true;
|
mfem::Vector reference_action_true;
|
||||||
operators::kernels::apply_mapped_hdiv_mass(
|
operators::kernels::apply_mapped_hdiv_mass(
|
||||||
f, *f.domainMapperStateless, gravity_gradient_true, displacement_true,
|
f, *f.domainMapperStateless, gravity_gradient_true, displacement_true, reference_action_true
|
||||||
reference_action_true);
|
);
|
||||||
const mfem::Vector reference_action =
|
const mfem::Vector reference_action = prepared_operator.GetFluxMap().gather(reference_action_true);
|
||||||
prepared_operator.GetFluxMap().gather(reference_action_true);
|
|
||||||
|
|
||||||
const double relative_error = prepared_test::relative_error(
|
const double relative_error = prepared_test::relative_error(prepared_action, reference_action, communicator);
|
||||||
prepared_action, reference_action, communicator);
|
|
||||||
|
|
||||||
INFO("Deformation scale = " << deformation_scale);
|
INFO("Deformation scale = " << deformation_scale);
|
||||||
INFO("Prepared action norm = "
|
INFO("Prepared action norm = " << prepared_test::global_norm(prepared_action, communicator));
|
||||||
<< prepared_test::global_norm(prepared_action, communicator));
|
INFO("Reference action norm = " << prepared_test::global_norm(reference_action, communicator));
|
||||||
INFO("Reference action norm = "
|
|
||||||
<< prepared_test::global_norm(reference_action, communicator));
|
|
||||||
INFO("Relative prepared-operator error = " << relative_error);
|
INFO("Relative prepared-operator error = " << relative_error);
|
||||||
|
|
||||||
REQUIRE(prepared_operator.IsPrepared());
|
REQUIRE(prepared_operator.IsPrepared());
|
||||||
@@ -70,8 +61,7 @@ TEST_CASE("Prepared Mapped Hdiv Mass Matches Stateless Kernel",
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const double geometry_change = prepared_test::relative_error(
|
const double geometry_change = prepared_test::relative_error(deformed_action, identity_action, communicator);
|
||||||
deformed_action, identity_action, communicator);
|
|
||||||
|
|
||||||
INFO("Relative action change under deformation = " << geometry_change);
|
INFO("Relative action change under deformation = " << geometry_change);
|
||||||
|
|
||||||
@@ -79,30 +69,27 @@ TEST_CASE("Prepared Mapped Hdiv Mass Matches Stateless Kernel",
|
|||||||
CHECK(geometry_change > 1.0e-5);
|
CHECK(geometry_change > 1.0e-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Prepared Mapped Hdiv Mass Preserves Operator Identities",
|
TEST_CASE(
|
||||||
tags::gravity_prepared) {
|
"Prepared Mapped Hdiv Mass Preserves Operator Identities",
|
||||||
|
tags::gravity_prepared
|
||||||
|
) {
|
||||||
auto args = test_utils::setup_args();
|
auto args = test_utils::setup_args();
|
||||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
operators::PreparedMappedHDivMassOperator prepared_operator(
|
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless);
|
||||||
f, *f.domainMapperStateless);
|
REQUIRE(prepared_operator.Width() == prepared_operator.GetFluxMap().reduced_size());
|
||||||
REQUIRE(prepared_operator.Width() ==
|
REQUIRE(prepared_operator.Height() == prepared_operator.GetFluxMap().reduced_size());
|
||||||
prepared_operator.GetFluxMap().reduced_size());
|
|
||||||
REQUIRE(prepared_operator.Height() ==
|
|
||||||
prepared_operator.GetFluxMap().reduced_size());
|
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacement =
|
||||||
prepared_operator.GetDisplacementMap().gather(
|
prepared_operator.GetDisplacementMap().gather(prepared_test::make_displacement(f, 1.0));
|
||||||
prepared_test::make_displacement(f, 1.0));
|
|
||||||
prepared_operator.Prepare(displacement);
|
prepared_operator.Prepare(displacement);
|
||||||
|
|
||||||
const mfem::Vector first = prepared_operator.GetFluxMap().gather(
|
const mfem::Vector first = prepared_operator.GetFluxMap().gather(
|
||||||
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
|
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.17)
|
||||||
0.17));
|
);
|
||||||
const mfem::Vector second = prepared_operator.GetFluxMap().gather(
|
const mfem::Vector second = prepared_operator.GetFluxMap().gather(
|
||||||
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(),
|
prepared_test::make_deterministic_vector(f.gravityFluxFes->GetTrueVSize(), 0.83)
|
||||||
0.83));
|
);
|
||||||
const mfem::Vector combination =
|
const mfem::Vector combination = prepared_test::linear_combination(first, 1.7, second, -0.4);
|
||||||
prepared_test::linear_combination(first, 1.7, second, -0.4);
|
|
||||||
|
|
||||||
mfem::Vector first_action;
|
mfem::Vector first_action;
|
||||||
mfem::Vector second_action;
|
mfem::Vector second_action;
|
||||||
@@ -113,8 +100,7 @@ TEST_CASE("Prepared Mapped Hdiv Mass Preserves Operator Identities",
|
|||||||
prepared_operator.Mult(second, second_action);
|
prepared_operator.Mult(second, second_action);
|
||||||
prepared_operator.Mult(combination, combination_action);
|
prepared_operator.Mult(combination, combination_action);
|
||||||
|
|
||||||
mfem::Vector expected_combination =
|
mfem::Vector expected_combination = prepared_test::linear_combination(first_action, 1.7, second_action, -0.4);
|
||||||
prepared_test::linear_combination(first_action, 1.7, second_action, -0.4);
|
|
||||||
|
|
||||||
mfem::Vector zero(first.Size());
|
mfem::Vector zero(first.Size());
|
||||||
zero = 0.0;
|
zero = 0.0;
|
||||||
@@ -122,20 +108,14 @@ TEST_CASE("Prepared Mapped Hdiv Mass Preserves Operator Identities",
|
|||||||
|
|
||||||
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
|
const MPI_Comm communicator = f.gravityFluxFes->GetComm();
|
||||||
|
|
||||||
const double first_second_product =
|
const double first_second_product = prepared_test::global_dot(first, second_action, communicator);
|
||||||
prepared_test::global_dot(first, second_action, communicator);
|
const double second_first_product = prepared_test::global_dot(second, first_action, communicator);
|
||||||
const double second_first_product =
|
const double symmetry_error = prepared_test::relative_scalar_error(first_second_product, second_first_product);
|
||||||
prepared_test::global_dot(second, first_action, communicator);
|
const double linearity_error =
|
||||||
const double symmetry_error = prepared_test::relative_scalar_error(
|
prepared_test::relative_error(combination_action, expected_combination, communicator);
|
||||||
first_second_product, second_first_product);
|
const double first_energy = prepared_test::global_dot(first, first_action, communicator);
|
||||||
const double linearity_error = prepared_test::relative_error(
|
const double second_energy = prepared_test::global_dot(second, second_action, communicator);
|
||||||
combination_action, expected_combination, communicator);
|
const std::uint64_t preparation_count = prepared_operator.GetPreparationCount();
|
||||||
const double first_energy =
|
|
||||||
prepared_test::global_dot(first, first_action, communicator);
|
|
||||||
const double second_energy =
|
|
||||||
prepared_test::global_dot(second, second_action, communicator);
|
|
||||||
const std::uint64_t preparation_count =
|
|
||||||
prepared_operator.GetPreparationCount();
|
|
||||||
|
|
||||||
mfem::Vector repeated_action;
|
mfem::Vector repeated_action;
|
||||||
prepared_operator.Mult(first, repeated_action);
|
prepared_operator.Mult(first, repeated_action);
|
||||||
@@ -149,25 +129,23 @@ TEST_CASE("Prepared Mapped Hdiv Mass Preserves Operator Identities",
|
|||||||
|
|
||||||
CHECK_THAT(symmetry_error, WithinAbs(0.0, 2.0e-12));
|
CHECK_THAT(symmetry_error, WithinAbs(0.0, 2.0e-12));
|
||||||
CHECK_THAT(linearity_error, WithinAbs(0.0, 2.0e-12));
|
CHECK_THAT(linearity_error, WithinAbs(0.0, 2.0e-12));
|
||||||
CHECK_THAT(prepared_test::global_norm(zero_action, communicator),
|
CHECK_THAT(prepared_test::global_norm(zero_action, communicator), WithinAbs(0.0, 1.0e-14));
|
||||||
WithinAbs(0.0, 1.0e-14));
|
|
||||||
CHECK(first_energy > 0.0);
|
CHECK(first_energy > 0.0);
|
||||||
CHECK(second_energy > 0.0);
|
CHECK(second_energy > 0.0);
|
||||||
CHECK(prepared_test::relative_error(repeated_action, first_action,
|
CHECK(prepared_test::relative_error(repeated_action, first_action, communicator) < 2.0e-14);
|
||||||
communicator) < 2.0e-14);
|
|
||||||
CHECK(prepared_operator.GetPreparationCount() == preparation_count);
|
CHECK(prepared_operator.GetPreparationCount() == preparation_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Prepared Mapped Hdiv Mass Diagonal Is Positive Across Both Domains",
|
TEST_CASE(
|
||||||
tags::gravity_prepared) {
|
"Prepared Mapped Hdiv Mass Diagonal Is Positive Across Both Domains",
|
||||||
|
tags::gravity_prepared
|
||||||
|
) {
|
||||||
auto args = test_utils::setup_args();
|
auto args = test_utils::setup_args();
|
||||||
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
operators::PreparedMappedHDivMassOperator prepared_operator(
|
operators::PreparedMappedHDivMassOperator prepared_operator(f, *f.domainMapperStateless);
|
||||||
f, *f.domainMapperStateless);
|
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacement =
|
||||||
prepared_operator.GetDisplacementMap().gather(
|
prepared_operator.GetDisplacementMap().gather(prepared_test::make_displacement(f, 1.0));
|
||||||
prepared_test::make_displacement(f, 1.0));
|
|
||||||
prepared_operator.Prepare(displacement);
|
prepared_operator.Prepare(displacement);
|
||||||
|
|
||||||
mfem::Vector diagonal;
|
mfem::Vector diagonal;
|
||||||
@@ -178,13 +156,11 @@ TEST_CASE("Prepared Mapped Hdiv Mass Diagonal Is Positive Across Both Domains",
|
|||||||
REQUIRE(diagonal.Size() == prepared_operator.Height());
|
REQUIRE(diagonal.Size() == prepared_operator.Height());
|
||||||
REQUIRE(true_diagonal.Size() == prepared_operator.GetFluxMap().full_size());
|
REQUIRE(true_diagonal.Size() == prepared_operator.GetFluxMap().full_size());
|
||||||
|
|
||||||
const mfem::Vector gathered_true_diagonal =
|
const mfem::Vector gathered_true_diagonal = prepared_operator.GetFluxMap().gather(true_diagonal);
|
||||||
prepared_operator.GetFluxMap().gather(true_diagonal);
|
|
||||||
|
|
||||||
for (int i = 0; i < diagonal.Size(); ++i) {
|
for (int i = 0; i < diagonal.Size(); ++i) {
|
||||||
REQUIRE(std::isfinite(diagonal(i)));
|
REQUIRE(std::isfinite(diagonal(i)));
|
||||||
CHECK(diagonal(i) > 0.0);
|
CHECK(diagonal(i) > 0.0);
|
||||||
CHECK_THAT(diagonal(i), WithinAbs(gathered_true_diagonal(i),
|
CHECK_THAT(diagonal(i), WithinAbs(gathered_true_diagonal(i), 1.0e-14 * std::abs(diagonal(i))));
|
||||||
1.0e-14 * std::abs(diagonal(i))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,55 +9,67 @@ import mean_field;
|
|||||||
import test_helpers;
|
import test_helpers;
|
||||||
|
|
||||||
namespace prepared_hydrostatic_analytic_solve_test_utils {
|
namespace prepared_hydrostatic_analytic_solve_test_utils {
|
||||||
constexpr double bernoulliConstant = 0.83;
|
constexpr double bernoulliConstant = 0.83;
|
||||||
constexpr double enthalpyAmplitude = 0.61;
|
constexpr double enthalpyAmplitude = 0.61;
|
||||||
|
|
||||||
struct AnalyticCase {
|
struct AnalyticCase {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
std::array<double, 3> deformationScale;
|
std::array<double, 3> deformationScale;
|
||||||
std::array<double, 3> angularVelocity;
|
std::array<double, 3> angularVelocity;
|
||||||
std::array<double, 3> rotationCenter;
|
std::array<double, 3> rotationCenter;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EnthalpyJacobianOperator final : public mfem::Operator {
|
class EnthalpyJacobianOperator final : public mfem::Operator {
|
||||||
public:
|
public:
|
||||||
EnthalpyJacobianOperator(
|
EnthalpyJacobianOperator(
|
||||||
const int enthalpySize,
|
const int enthalpySize,
|
||||||
const mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &preparedOperator
|
||||||
&preparedOperator)
|
)
|
||||||
: mfem::Operator(enthalpySize), m_preparedOperator(preparedOperator) {}
|
: mfem::Operator(enthalpySize),
|
||||||
|
m_preparedOperator(preparedOperator) {
|
||||||
|
}
|
||||||
|
|
||||||
void Mult(const mfem::Vector &direction,
|
void Mult(
|
||||||
mfem::Vector &action) const override {
|
const mfem::Vector &direction,
|
||||||
|
mfem::Vector &action
|
||||||
|
) const override {
|
||||||
m_preparedOperator.ApplyEnthalpyJacobianAction(direction, action);
|
m_preparedOperator.ApplyEnthalpyJacobianAction(direction, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
const mean_field::operators::PreparedHydrostaticEquilibriumOperator &m_preparedOperator;
|
||||||
&m_preparedOperator;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies
|
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumDependencies make_dependencies() {
|
||||||
make_dependencies() {
|
return {
|
||||||
return {.discretization = {.identity = 701, .revision = 2},
|
.discretization = {.identity = 701, .revision = 2},
|
||||||
.enthalpy = {.identity = 709, .revision = 3},
|
.enthalpy = {.identity = 709, .revision = 3},
|
||||||
.gravityPotential = {.identity = 719, .revision = 5},
|
.gravityPotential = {.identity = 719, .revision = 5},
|
||||||
.displacement = {.identity = 727, .revision = 7},
|
.displacement = {.identity = 727, .revision = 7},
|
||||||
.rotation = {.identity = 733, .revision = 11},
|
.rotation = {.identity = 733, .revision = 11},
|
||||||
.bernoulliConstant = {.identity = 739, .revision = 13}};
|
.bernoulliConstant = {.identity = 739, .revision = 13}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView
|
mean_field::operators::context::hydrostatic::HydrostaticEquilibriumStateView make_state(
|
||||||
make_state(const mfem::Vector &enthalpy, const mfem::Vector &gravityPotential,
|
const mfem::Vector &enthalpy,
|
||||||
const mfem::Vector &displacement) {
|
const mfem::Vector &gravityPotential,
|
||||||
return {.enthalpy = enthalpy,
|
const mfem::Vector &displacement
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
.enthalpy = enthalpy,
|
||||||
.gravityPotential = gravityPotential,
|
.gravityPotential = gravityPotential,
|
||||||
.displacement = displacement,
|
.displacement = displacement,
|
||||||
.bernoulliConstant = bernoulliConstant};
|
.bernoulliConstant = bernoulliConstant
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
mfem::Vector make_vector(const std::array<double, 3> &values) {
|
mfem::Vector make_vector(
|
||||||
|
const std::array<
|
||||||
|
double,
|
||||||
|
3> &values
|
||||||
|
) {
|
||||||
mfem::Vector vector(3);
|
mfem::Vector vector(3);
|
||||||
|
|
||||||
for (int component = 0; component < 3; ++component) {
|
for (int component = 0; component < 3; ++component) {
|
||||||
@@ -65,44 +77,44 @@ mfem::Vector make_vector(const std::array<double, 3> &values) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
mean_field::physics::RigidRotation
|
mean_field::physics::RigidRotation make_rotation(const AnalyticCase &analyticCase) {
|
||||||
make_rotation(const AnalyticCase &analyticCase) {
|
|
||||||
return mean_field::physics::RigidRotation(
|
return mean_field::physics::RigidRotation(
|
||||||
make_vector(analyticCase.angularVelocity),
|
make_vector(analyticCase.angularVelocity), make_vector(analyticCase.rotationCenter)
|
||||||
make_vector(analyticCase.rotationCenter));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void map_to_physical(const mfem::Vector &referencePosition,
|
void map_to_physical(
|
||||||
|
const mfem::Vector &referencePosition,
|
||||||
const AnalyticCase &analyticCase,
|
const AnalyticCase &analyticCase,
|
||||||
mfem::Vector &physicalPosition) {
|
mfem::Vector &physicalPosition
|
||||||
|
) {
|
||||||
physicalPosition.SetSize(3);
|
physicalPosition.SetSize(3);
|
||||||
|
|
||||||
for (int component = 0; component < 3; ++component) {
|
for (int component = 0; component < 3; ++component) {
|
||||||
physicalPosition(component) =
|
physicalPosition(component) =
|
||||||
analyticCase.deformationScale[static_cast<std::size_t>(component)] *
|
analyticCase.deformationScale[static_cast<std::size_t>(component)] * referencePosition(component);
|
||||||
referencePosition(component);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
double exact_enthalpy_value(const mfem::Vector &referencePosition) {
|
double exact_enthalpy_value(const mfem::Vector &referencePosition) {
|
||||||
double normalizedRadiusSquared = 0.0;
|
double normalizedRadiusSquared = 0.0;
|
||||||
|
|
||||||
for (int component = 0; component < 3; ++component) {
|
for (int component = 0; component < 3; ++component) {
|
||||||
const double normalizedCoordinate =
|
const double normalizedCoordinate = referencePosition(component) / mean_field::utils::RADIUS;
|
||||||
referencePosition(component) / mean_field::utils::RADIUS;
|
|
||||||
|
|
||||||
normalizedRadiusSquared += normalizedCoordinate * normalizedCoordinate;
|
normalizedRadiusSquared += normalizedCoordinate * normalizedCoordinate;
|
||||||
}
|
}
|
||||||
|
|
||||||
return enthalpyAmplitude * std::max(0.0, 1.0 - normalizedRadiusSquared);
|
return enthalpyAmplitude * std::max(0.0, 1.0 - normalizedRadiusSquared);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double exact_potential_value(
|
||||||
exact_potential_value(const mfem::Vector &referencePosition,
|
const mfem::Vector &referencePosition,
|
||||||
const AnalyticCase &analyticCase,
|
const AnalyticCase &analyticCase,
|
||||||
const mean_field::physics::RigidRotation &rotation) {
|
const mean_field::physics::RigidRotation &rotation
|
||||||
|
) {
|
||||||
mfem::Vector physicalPosition;
|
mfem::Vector physicalPosition;
|
||||||
|
|
||||||
map_to_physical(referencePosition, analyticCase, physicalPosition);
|
map_to_physical(referencePosition, analyticCase, physicalPosition);
|
||||||
@@ -114,27 +126,26 @@ exact_potential_value(const mfem::Vector &referencePosition,
|
|||||||
*
|
*
|
||||||
* analytically.
|
* analytically.
|
||||||
*/
|
*/
|
||||||
return bernoulliConstant + rotation.potential(physicalPosition) -
|
return bernoulliConstant + rotation.potential(physicalPosition) - exact_enthalpy_value(referencePosition);
|
||||||
exact_enthalpy_value(referencePosition);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mfem::Array<int> make_stellar_element_marker(const mean_field::fem::FEM &f) {
|
mfem::Array<int> make_stellar_element_marker(const mean_field::fem::FEM &f) {
|
||||||
mfem::Array<int> stellarElementMarker(f.mesh->GetNE());
|
mfem::Array<int> stellarElementMarker(f.mesh->GetNE());
|
||||||
|
|
||||||
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
|
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
|
||||||
|
|
||||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||||
stellarElementMarker[elementId] =
|
stellarElementMarker[elementId] = f.mesh->GetAttribute(elementId) != vacuumAttribute;
|
||||||
f.mesh->GetAttribute(elementId) != vacuumAttribute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return stellarElementMarker;
|
return stellarElementMarker;
|
||||||
}
|
}
|
||||||
} // namespace prepared_hydrostatic_analytic_solve_test_utils
|
} // namespace prepared_hydrostatic_analytic_solve_test_utils
|
||||||
|
|
||||||
TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
TEST_CASE(
|
||||||
tags::barotrope_hydrostatic_prepared_analytic &tags::convergence
|
"Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
||||||
&tags::accuracy) {
|
tags::barotrope_hydrostatic_prepared_analytic &tags::convergence &tags::accuracy
|
||||||
|
) {
|
||||||
using prepared_hydrostatic_analytic_solve_test_utils::AnalyticCase;
|
using prepared_hydrostatic_analytic_solve_test_utils::AnalyticCase;
|
||||||
|
|
||||||
constexpr double deformationX = 1.08;
|
constexpr double deformationX = 1.08;
|
||||||
@@ -160,7 +171,8 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
{.name = "volume-preserving deformed rotating equilibrium",
|
{.name = "volume-preserving deformed rotating equilibrium",
|
||||||
.deformationScale = {deformationX, deformationY, deformationZ},
|
.deformationScale = {deformationX, deformationY, deformationZ},
|
||||||
.angularVelocity = {0.17, -0.12, 0.43},
|
.angularVelocity = {0.17, -0.12, 0.43},
|
||||||
.rotationCenter = {0.031, -0.024, 0.018}}}};
|
.rotationCenter = {0.031, -0.024, 0.018}}}
|
||||||
|
};
|
||||||
|
|
||||||
auto args = test_utils::setup_args();
|
auto args = test_utils::setup_args();
|
||||||
|
|
||||||
@@ -169,59 +181,51 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
const MPI_Comm communicator = f.mesh->GetComm();
|
const MPI_Comm communicator = f.mesh->GetComm();
|
||||||
|
|
||||||
const mean_field::field::FieldDofMap enthalpyMap =
|
const mean_field::field::FieldDofMap enthalpyMap =
|
||||||
field_dof_test_utils::make_map<mean_field::field::Enthalpy>(
|
field_dof_test_utils::make_map<mean_field::field::Enthalpy>(*f.enthalpyFes);
|
||||||
*f.enthalpyFes);
|
|
||||||
|
|
||||||
const mean_field::field::FieldDofMap gravityPotentialMap =
|
const mean_field::field::FieldDofMap gravityPotentialMap =
|
||||||
field_dof_test_utils::make_map<mean_field::field::Gravity>(
|
field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityPotentialFes);
|
||||||
*f.gravityPotentialFes);
|
|
||||||
|
|
||||||
const mean_field::field::FieldDofMap displacementMap =
|
const mean_field::field::FieldDofMap displacementMap =
|
||||||
field_dof_test_utils::make_map<mean_field::field::Displacement>(
|
field_dof_test_utils::make_map<mean_field::field::Displacement>(*f.displacementFes);
|
||||||
*f.displacementFes);
|
|
||||||
|
|
||||||
const mfem::Array<int> stellarElementMarker =
|
const mfem::Array<int> stellarElementMarker =
|
||||||
prepared_hydrostatic_analytic_solve_test_utils::
|
prepared_hydrostatic_analytic_solve_test_utils::make_stellar_element_marker(f);
|
||||||
make_stellar_element_marker(f);
|
|
||||||
|
|
||||||
for (const AnalyticCase &analyticCase : analyticCases) {
|
for (const AnalyticCase &analyticCase : analyticCases) {
|
||||||
DYNAMIC_SECTION(analyticCase.name) {
|
DYNAMIC_SECTION(analyticCase.name) {
|
||||||
const double deformationDeterminant = analyticCase.deformationScale[0] *
|
const double deformationDeterminant =
|
||||||
analyticCase.deformationScale[1] *
|
analyticCase.deformationScale[0] * analyticCase.deformationScale[1] * analyticCase.deformationScale[2];
|
||||||
analyticCase.deformationScale[2];
|
|
||||||
|
|
||||||
REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14);
|
REQUIRE(std::abs(deformationDeterminant - 1.0) < 2.0e-14);
|
||||||
|
|
||||||
const mean_field::physics::RigidRotation rotation =
|
const mean_field::physics::RigidRotation rotation =
|
||||||
prepared_hydrostatic_analytic_solve_test_utils::make_rotation(
|
prepared_hydrostatic_analytic_solve_test_utils::make_rotation(analyticCase);
|
||||||
analyticCase);
|
|
||||||
|
|
||||||
auto displacementFunction =
|
auto displacementFunction =
|
||||||
[&analyticCase](const mfem::Vector &referencePosition,
|
[&analyticCase](const mfem::Vector &referencePosition, mfem::Vector &displacementValue) {
|
||||||
mfem::Vector &displacementValue) {
|
|
||||||
mfem::Vector physicalPosition;
|
mfem::Vector physicalPosition;
|
||||||
|
|
||||||
prepared_hydrostatic_analytic_solve_test_utils::map_to_physical(
|
prepared_hydrostatic_analytic_solve_test_utils::map_to_physical(
|
||||||
referencePosition, analyticCase, physicalPosition);
|
referencePosition, analyticCase, physicalPosition
|
||||||
|
);
|
||||||
|
|
||||||
displacementValue.SetSize(3);
|
displacementValue.SetSize(3);
|
||||||
displacementValue = physicalPosition;
|
displacementValue = physicalPosition;
|
||||||
displacementValue -= referencePosition;
|
displacementValue -= referencePosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto potentialFunction = [&analyticCase, &rotation](
|
auto potentialFunction = [&analyticCase, &rotation](const mfem::Vector &referencePosition) {
|
||||||
const mfem::Vector &referencePosition) {
|
return prepared_hydrostatic_analytic_solve_test_utils::exact_potential_value(
|
||||||
return prepared_hydrostatic_analytic_solve_test_utils::
|
referencePosition, analyticCase, rotation
|
||||||
exact_potential_value(referencePosition, analyticCase, rotation);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto enthalpyFunction = [](const mfem::Vector &referencePosition) {
|
auto enthalpyFunction = [](const mfem::Vector &referencePosition) {
|
||||||
return prepared_hydrostatic_analytic_solve_test_utils::
|
return prepared_hydrostatic_analytic_solve_test_utils::exact_enthalpy_value(referencePosition);
|
||||||
exact_enthalpy_value(referencePosition);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient displacementCoefficient(
|
mfem::VectorFunctionCoefficient displacementCoefficient(f.mesh->Dimension(), displacementFunction);
|
||||||
f.mesh->Dimension(), displacementFunction);
|
|
||||||
|
|
||||||
mfem::FunctionCoefficient potentialCoefficient(potentialFunction);
|
mfem::FunctionCoefficient potentialCoefficient(potentialFunction);
|
||||||
|
|
||||||
@@ -244,10 +248,8 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
displacementField.GetTrueDofs(displacementTrue);
|
displacementField.GetTrueDofs(displacementTrue);
|
||||||
potentialField.GetTrueDofs(gravityPotentialTrue);
|
potentialField.GetTrueDofs(gravityPotentialTrue);
|
||||||
|
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacement = displacementMap.gather(displacementTrue);
|
||||||
displacementMap.gather(displacementTrue);
|
const mfem::Vector gravityPotential = gravityPotentialMap.gather(gravityPotentialTrue);
|
||||||
const mfem::Vector gravityPotential =
|
|
||||||
gravityPotentialMap.gather(gravityPotentialTrue);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This projection is not used as the solution. It gives
|
* This projection is not used as the solution. It gives
|
||||||
@@ -262,16 +264,15 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
|
|
||||||
zeroEnthalpyField = 0.0;
|
zeroEnthalpyField = 0.0;
|
||||||
|
|
||||||
const double exactEnthalpyNorm = zeroEnthalpyField.ComputeL2Error(
|
const double exactEnthalpyNorm =
|
||||||
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
zeroEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
||||||
|
|
||||||
const double projectionError = projectedEnthalpyField.ComputeL2Error(
|
const double projectionError =
|
||||||
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
projectedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
||||||
|
|
||||||
REQUIRE(exactEnthalpyNorm > 0.0);
|
REQUIRE(exactEnthalpyNorm > 0.0);
|
||||||
|
|
||||||
const double relativeProjectionError =
|
const double relativeProjectionError = projectionError / exactEnthalpyNorm;
|
||||||
projectionError / exactEnthalpyNorm;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Begin deliberately far from equilibrium.
|
* Begin deliberately far from equilibrium.
|
||||||
@@ -280,16 +281,14 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
|
|
||||||
enthalpy = 0.0;
|
enthalpy = 0.0;
|
||||||
|
|
||||||
auto dependencies =
|
auto dependencies = prepared_hydrostatic_analytic_solve_test_utils::make_dependencies();
|
||||||
prepared_hydrostatic_analytic_solve_test_utils::make_dependencies();
|
|
||||||
|
|
||||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator
|
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||||
preparedOperator(f, *f.domainMapperStateless);
|
|
||||||
|
|
||||||
const auto initialReport = preparedOperator.Prepare(
|
const auto initialReport = preparedOperator.Prepare(
|
||||||
prepared_hydrostatic_analytic_solve_test_utils::make_state(
|
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement),
|
||||||
enthalpy, gravityPotential, displacement),
|
dependencies, rotation
|
||||||
dependencies, rotation);
|
);
|
||||||
|
|
||||||
REQUIRE(initialReport.preparedResidual);
|
REQUIRE(initialReport.preparedResidual);
|
||||||
REQUIRE(initialReport.preparedAlgebraicJacobianBlocks);
|
REQUIRE(initialReport.preparedAlgebraicJacobianBlocks);
|
||||||
@@ -298,9 +297,7 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
|
|
||||||
preparedOperator.BuildResidual(initialResidual);
|
preparedOperator.BuildResidual(initialResidual);
|
||||||
|
|
||||||
const double initialResidualNorm =
|
const double initialResidualNorm = gravity_prepared_test_utils::global_norm(initialResidual, communicator);
|
||||||
gravity_prepared_test_utils::global_norm(initialResidual,
|
|
||||||
communicator);
|
|
||||||
|
|
||||||
REQUIRE(initialResidualNorm > 1.0e-12);
|
REQUIRE(initialResidualNorm > 1.0e-12);
|
||||||
|
|
||||||
@@ -314,8 +311,9 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
* rotation, and displacement makes this a well-defined
|
* rotation, and displacement makes this a well-defined
|
||||||
* enthalpy solve.
|
* enthalpy solve.
|
||||||
*/
|
*/
|
||||||
prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator
|
prepared_hydrostatic_analytic_solve_test_utils::EnthalpyJacobianOperator enthalpyJacobian(
|
||||||
enthalpyJacobian(enthalpyMap.reduced_size(), preparedOperator);
|
enthalpyMap.reduced_size(), preparedOperator
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector rightHandSide(initialResidual);
|
mfem::Vector rightHandSide(initialResidual);
|
||||||
rightHandSide *= -1.0;
|
rightHandSide *= -1.0;
|
||||||
@@ -357,9 +355,9 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
++dependencies.enthalpy.revision;
|
++dependencies.enthalpy.revision;
|
||||||
|
|
||||||
const auto solvedReport = preparedOperator.Prepare(
|
const auto solvedReport = preparedOperator.Prepare(
|
||||||
prepared_hydrostatic_analytic_solve_test_utils::make_state(
|
prepared_hydrostatic_analytic_solve_test_utils::make_state(enthalpy, gravityPotential, displacement),
|
||||||
enthalpy, gravityPotential, displacement),
|
dependencies, rotation
|
||||||
dependencies, rotation);
|
);
|
||||||
|
|
||||||
CHECK(solvedReport.contextReport.updatedEnthalpy);
|
CHECK(solvedReport.contextReport.updatedEnthalpy);
|
||||||
|
|
||||||
@@ -373,9 +371,7 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
|
|
||||||
preparedOperator.BuildResidual(solvedResidual);
|
preparedOperator.BuildResidual(solvedResidual);
|
||||||
|
|
||||||
const double solvedResidualNorm =
|
const double solvedResidualNorm = gravity_prepared_test_utils::global_norm(solvedResidual, communicator);
|
||||||
gravity_prepared_test_utils::global_norm(solvedResidual,
|
|
||||||
communicator);
|
|
||||||
|
|
||||||
const double residualReduction = solvedResidualNorm / initialResidualNorm;
|
const double residualReduction = solvedResidualNorm / initialResidualNorm;
|
||||||
|
|
||||||
@@ -393,11 +389,10 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
enthalpyMap.scatter(enthalpy, enthalpyTrue);
|
enthalpyMap.scatter(enthalpy, enthalpyTrue);
|
||||||
solvedEnthalpyField.SetFromTrueDofs(enthalpyTrue);
|
solvedEnthalpyField.SetFromTrueDofs(enthalpyTrue);
|
||||||
|
|
||||||
const double solvedAnalyticError = solvedEnthalpyField.ComputeL2Error(
|
const double solvedAnalyticError =
|
||||||
exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
solvedEnthalpyField.ComputeL2Error(exactEnthalpyCoefficient, nullptr, &stellarElementMarker);
|
||||||
|
|
||||||
const double relativeSolvedAnalyticError =
|
const double relativeSolvedAnalyticError = solvedAnalyticError / exactEnthalpyNorm;
|
||||||
solvedAnalyticError / exactEnthalpyNorm;
|
|
||||||
|
|
||||||
INFO("Deformation determinant = " << deformationDeterminant);
|
INFO("Deformation determinant = " << deformationDeterminant);
|
||||||
|
|
||||||
@@ -409,8 +404,7 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
|
|
||||||
INFO("Relative analytic projection floor = " << relativeProjectionError);
|
INFO("Relative analytic projection floor = " << relativeProjectionError);
|
||||||
|
|
||||||
INFO("Relative solved analytic L2 error = "
|
INFO("Relative solved analytic L2 error = " << relativeSolvedAnalyticError);
|
||||||
<< relativeSolvedAnalyticError);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The discrete Bernoulli equation must be solved essentially
|
* The discrete Bernoulli equation must be solved essentially
|
||||||
@@ -425,8 +419,7 @@ TEST_CASE("Prepared Hydrostatic Operator Solves Analytic Bernoulli Equilibria",
|
|||||||
* also contains potential-projection and mapped-space
|
* also contains potential-projection and mapped-space
|
||||||
* compatibility errors.
|
* compatibility errors.
|
||||||
*/
|
*/
|
||||||
CHECK(relativeSolvedAnalyticError <
|
CHECK(relativeSolvedAnalyticError < std::max(5.0 * relativeProjectionError, 1.25e-4));
|
||||||
std::max(5.0 * relativeProjectionError, 1.25e-4));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Record that the analytic error remains within one order of
|
* Record that the analytic error remains within one order of
|
||||||
|
|||||||
@@ -138,9 +138,7 @@ TEST_CASE(
|
|||||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||||
|
|
||||||
const mfem::Vector enthalpy =
|
const mfem::Vector enthalpy =
|
||||||
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(
|
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(*f.enthalpyFes, 0.34);
|
||||||
*f.enthalpyFes, 0.34
|
|
||||||
);
|
|
||||||
|
|
||||||
const mfem::Vector gravityPotential =
|
const mfem::Vector gravityPotential =
|
||||||
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
|
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
|
||||||
@@ -161,9 +159,7 @@ TEST_CASE(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const mfem::Vector enthalpyVariation =
|
const mfem::Vector enthalpyVariation =
|
||||||
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(
|
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(*f.enthalpyFes, 1.07);
|
||||||
*f.enthalpyFes, 1.07
|
|
||||||
);
|
|
||||||
|
|
||||||
const mfem::Vector gravityPotentialVariation =
|
const mfem::Vector gravityPotentialVariation =
|
||||||
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
|
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
|
||||||
@@ -236,9 +232,7 @@ TEST_CASE(
|
|||||||
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
mean_field::operators::PreparedHydrostaticEquilibriumOperator preparedOperator(f, *f.domainMapperStateless);
|
||||||
|
|
||||||
const mfem::Vector enthalpy =
|
const mfem::Vector enthalpy =
|
||||||
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(
|
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(*f.enthalpyFes, 0.41);
|
||||||
*f.enthalpyFes, 0.41
|
|
||||||
);
|
|
||||||
|
|
||||||
const mfem::Vector gravityPotential =
|
const mfem::Vector gravityPotential =
|
||||||
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
|
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
|
||||||
@@ -277,9 +271,7 @@ TEST_CASE(
|
|||||||
CHECK(preparedOperator.GetEnthalpyMap().inactive_size() > 0);
|
CHECK(preparedOperator.GetEnthalpyMap().inactive_size() > 0);
|
||||||
|
|
||||||
const mfem::Vector enthalpyVariation =
|
const mfem::Vector enthalpyVariation =
|
||||||
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(
|
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Enthalpy>(*f.enthalpyFes, 1.12);
|
||||||
*f.enthalpyFes, 1.12
|
|
||||||
);
|
|
||||||
|
|
||||||
const mfem::Vector gravityPotentialVariation =
|
const mfem::Vector gravityPotentialVariation =
|
||||||
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
|
field_dof_test_utils::make_deterministic_supported_vector<mean_field::field::Gravity>(
|
||||||
|
|||||||
@@ -26,16 +26,13 @@ namespace mass_normalization_test_utils {
|
|||||||
);
|
);
|
||||||
|
|
||||||
[[nodiscard]] mean_field::operators::MassNormalizationLayout make_layout(const mean_field::fem::FEM &f) {
|
[[nodiscard]] mean_field::operators::MassNormalizationLayout make_layout(const mean_field::fem::FEM &f) {
|
||||||
const auto densityMap =
|
const auto densityMap = field_dof_test_utils::make_map<mean_field::field::Density>(*f.densityFes);
|
||||||
field_dof_test_utils::make_map<mean_field::field::Density>(*f.densityFes);
|
|
||||||
const auto displacementMap =
|
const auto displacementMap =
|
||||||
field_dof_test_utils::make_map<mean_field::field::Displacement>(*f.displacementFes);
|
field_dof_test_utils::make_map<mean_field::field::Displacement>(*f.displacementFes);
|
||||||
const auto gravityFluxMap =
|
const auto gravityFluxMap = field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityFluxFes);
|
||||||
field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityFluxFes);
|
|
||||||
const auto gravityPotentialMap =
|
const auto gravityPotentialMap =
|
||||||
field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityPotentialFes);
|
field_dof_test_utils::make_map<mean_field::field::Gravity>(*f.gravityPotentialFes);
|
||||||
const auto enthalpyMap =
|
const auto enthalpyMap = field_dof_test_utils::make_map<mean_field::field::Enthalpy>(*f.enthalpyFes);
|
||||||
field_dof_test_utils::make_map<mean_field::field::Enthalpy>(*f.enthalpyFes);
|
|
||||||
|
|
||||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||||
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
|
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
|
||||||
@@ -345,8 +342,7 @@ TEST_CASE(
|
|||||||
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
|
mean_field::operators::PreparedMassNormalizationOperator massOperator(f, *f.domainMapperStateless, gravityContext);
|
||||||
massOperator.Prepare({.targetMass = 1.11}, dependencies);
|
massOperator.Prepare({.targetMass = 1.11}, dependencies);
|
||||||
|
|
||||||
const mfem::Vector reducedDisplacementDirection =
|
const mfem::Vector reducedDisplacementDirection = gravityContext.GetDisplacementMap().gather(displacementDirection);
|
||||||
gravityContext.GetDisplacementMap().gather(displacementDirection);
|
|
||||||
|
|
||||||
mfem::Vector analyticAction;
|
mfem::Vector analyticAction;
|
||||||
massOperator.ApplyDisplacementJacobianAction(reducedDisplacementDirection, analyticAction);
|
massOperator.ApplyDisplacementJacobianAction(reducedDisplacementDirection, analyticAction);
|
||||||
@@ -477,8 +473,7 @@ TEST_CASE(
|
|||||||
massOperator.Prepare({.targetMass = 1.19}, dependencies);
|
massOperator.Prepare({.targetMass = 1.19}, dependencies);
|
||||||
|
|
||||||
const mfem::Vector reducedDensityDirection = gravityContext.GetDensityMap().gather(densityDirection);
|
const mfem::Vector reducedDensityDirection = gravityContext.GetDensityMap().gather(densityDirection);
|
||||||
const mfem::Vector reducedDisplacementDirection =
|
const mfem::Vector reducedDisplacementDirection = gravityContext.GetDisplacementMap().gather(displacementDirection);
|
||||||
gravityContext.GetDisplacementMap().gather(displacementDirection);
|
|
||||||
|
|
||||||
mfem::Vector densityAction;
|
mfem::Vector densityAction;
|
||||||
mfem::Vector displacementAction;
|
mfem::Vector displacementAction;
|
||||||
@@ -486,9 +481,7 @@ TEST_CASE(
|
|||||||
|
|
||||||
massOperator.ApplyDensityJacobianAction(reducedDensityDirection, densityAction);
|
massOperator.ApplyDensityJacobianAction(reducedDensityDirection, densityAction);
|
||||||
massOperator.ApplyDisplacementJacobianAction(reducedDisplacementDirection, displacementAction);
|
massOperator.ApplyDisplacementJacobianAction(reducedDisplacementDirection, displacementAction);
|
||||||
massOperator.ApplyCompleteJacobianAction(
|
massOperator.ApplyCompleteJacobianAction(reducedDensityDirection, reducedDisplacementDirection, completeAction);
|
||||||
reducedDensityDirection, reducedDisplacementDirection, completeAction
|
|
||||||
);
|
|
||||||
|
|
||||||
CHECK(
|
CHECK(
|
||||||
mass_normalization_test_utils::relative_error(completeAction(0), densityAction(0) + displacementAction(0)) <
|
mass_normalization_test_utils::relative_error(completeAction(0), densityAction(0) + displacementAction(0)) <
|
||||||
@@ -498,10 +491,7 @@ TEST_CASE(
|
|||||||
const auto layout = mass_normalization_test_utils::make_layout(f);
|
const auto layout = mass_normalization_test_utils::make_layout(f);
|
||||||
mean_field::operators::PreparedMassNormalizationJacobianOperator adapter(layout, massOperator);
|
mean_field::operators::PreparedMassNormalizationJacobianOperator adapter(layout, massOperator);
|
||||||
|
|
||||||
CHECK(
|
CHECK(layout.size(mass_normalization_test_utils::densityValue) == gravityContext.GetDensityMap().reduced_size());
|
||||||
layout.size(mass_normalization_test_utils::densityValue) ==
|
|
||||||
gravityContext.GetDensityMap().reduced_size()
|
|
||||||
);
|
|
||||||
CHECK(
|
CHECK(
|
||||||
layout.size(mass_normalization_test_utils::displacementValue) ==
|
layout.size(mass_normalization_test_utils::displacementValue) ==
|
||||||
gravityContext.GetDisplacementMap().reduced_size()
|
gravityContext.GetDisplacementMap().reduced_size()
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import mean_field;
|
|||||||
import test_helpers;
|
import test_helpers;
|
||||||
|
|
||||||
namespace prepared_pressure_force_test_utils {
|
namespace prepared_pressure_force_test_utils {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||||
|
|
||||||
struct Maps final {
|
struct Maps final {
|
||||||
mean_field::field::FieldDofMap density;
|
mean_field::field::FieldDofMap density;
|
||||||
mean_field::field::FieldDofMap displacement;
|
mean_field::field::FieldDofMap displacement;
|
||||||
mean_field::field::FieldDofMap gravityFlux;
|
mean_field::field::FieldDofMap gravityFlux;
|
||||||
@@ -24,59 +24,76 @@ struct Maps final {
|
|||||||
|
|
||||||
explicit Maps(const mean_field::fem::FEM &f)
|
explicit Maps(const mean_field::fem::FEM &f)
|
||||||
: density(
|
: density(
|
||||||
mean_field::field::make_field_dof_map<mean_field::field::Density,
|
mean_field::field::make_field_dof_map<
|
||||||
DomainSchema>(*f.densityFes)),
|
mean_field::field::Density,
|
||||||
displacement(mean_field::field::make_field_dof_map<
|
DomainSchema>(*f.densityFes)
|
||||||
mean_field::field::Displacement, DomainSchema>(
|
),
|
||||||
*f.displacementFes)),
|
displacement(
|
||||||
|
mean_field::field::make_field_dof_map<
|
||||||
|
mean_field::field::Displacement,
|
||||||
|
DomainSchema>(*f.displacementFes)
|
||||||
|
),
|
||||||
gravityFlux(
|
gravityFlux(
|
||||||
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
|
mean_field::field::make_field_dof_map<
|
||||||
DomainSchema>(
|
mean_field::field::Gravity,
|
||||||
*f.gravityFluxFes)),
|
DomainSchema>(*f.gravityFluxFes)
|
||||||
|
),
|
||||||
gravityPotential(
|
gravityPotential(
|
||||||
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
|
mean_field::field::make_field_dof_map<
|
||||||
DomainSchema>(
|
mean_field::field::Gravity,
|
||||||
*f.gravityPotentialFes)),
|
DomainSchema>(*f.gravityPotentialFes)
|
||||||
enthalpy(mean_field::field::make_field_dof_map<
|
),
|
||||||
mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes)) {}
|
enthalpy(
|
||||||
};
|
mean_field::field::make_field_dof_map<
|
||||||
|
mean_field::field::Enthalpy,
|
||||||
|
DomainSchema>(*f.enthalpyFes)
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
mfem::Vector make_positive_enthalpy_true(const mean_field::fem::FEM &f,
|
mfem::Vector make_positive_enthalpy_true(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double phase
|
||||||
|
) {
|
||||||
mfem::Vector enthalpy(f.enthalpyFes->GetTrueVSize());
|
mfem::Vector enthalpy(f.enthalpyFes->GetTrueVSize());
|
||||||
|
|
||||||
for (int index = 0; index < enthalpy.Size(); ++index) {
|
for (int index = 0; index < enthalpy.Size(); ++index) {
|
||||||
const double position = static_cast<double>(index + 1);
|
const double position = static_cast<double>(index + 1);
|
||||||
|
|
||||||
enthalpy(index) = 0.93 + 0.09 * std::sin(0.23 * position + phase) +
|
enthalpy(index) =
|
||||||
0.04 * std::cos(0.17 * position - 0.5 * phase);
|
0.93 + 0.09 * std::sin(0.23 * position + phase) + 0.04 * std::cos(0.17 * position - 0.5 * phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
return enthalpy;
|
return enthalpy;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
mfem::Vector make_enthalpy_direction_true(const mean_field::fem::FEM &f,
|
mfem::Vector make_enthalpy_direction_true(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double phase
|
||||||
|
) {
|
||||||
mfem::Vector direction(f.enthalpyFes->GetTrueVSize());
|
mfem::Vector direction(f.enthalpyFes->GetTrueVSize());
|
||||||
|
|
||||||
for (int index = 0; index < direction.Size(); ++index) {
|
for (int index = 0; index < direction.Size(); ++index) {
|
||||||
const double position = static_cast<double>(index + 1);
|
const double position = static_cast<double>(index + 1);
|
||||||
|
|
||||||
direction(index) = 0.27 * std::sin(0.19 * position + phase) +
|
direction(index) =
|
||||||
0.14 * std::cos(0.13 * position - 0.5 * phase);
|
0.27 * std::sin(0.19 * position + phase) + 0.14 * std::cos(0.13 * position - 0.5 * phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
mfem::Vector make_displacement_direction_true(const mean_field::fem::FEM &f,
|
mfem::Vector make_displacement_direction_true(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
MFEM_VERIFY(f.mesh->Dimension() == 3,
|
const double phase
|
||||||
"The prepared pressure-force test requires a "
|
) {
|
||||||
"three-dimensional mesh.");
|
MFEM_VERIFY(
|
||||||
|
f.mesh->Dimension() == 3, "The prepared pressure-force test requires a "
|
||||||
|
"three-dimensional mesh."
|
||||||
|
);
|
||||||
|
|
||||||
mfem::ParGridFunction directionField(f.displacementFes.get());
|
mfem::ParGridFunction directionField(f.displacementFes.get());
|
||||||
|
|
||||||
@@ -90,15 +107,13 @@ mfem::Vector make_displacement_direction_true(const mean_field::fem::FEM &f,
|
|||||||
|
|
||||||
value.SetSize(3);
|
value.SetSize(3);
|
||||||
|
|
||||||
value(0) =
|
value(0) = 0.019 * x + 0.011 * y * z - 0.006 * z * z + 0.004 * phase * y;
|
||||||
0.019 * x + 0.011 * y * z - 0.006 * z * z + 0.004 * phase * y;
|
|
||||||
|
|
||||||
value(1) =
|
value(1) = -0.016 * y + 0.008 * x * z + 0.005 * x * x - 0.003 * phase * z;
|
||||||
-0.016 * y + 0.008 * x * z + 0.005 * x * x - 0.003 * phase * z;
|
|
||||||
|
|
||||||
value(2) =
|
value(2) = 0.013 * z - 0.010 * x * y + 0.006 * y * y + 0.004 * phase * x;
|
||||||
0.013 * z - 0.010 * x * y + 0.006 * y * y + 0.004 * phase * x;
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
directionField.ProjectCoefficient(directionCoefficient);
|
directionField.ProjectCoefficient(directionCoefficient);
|
||||||
|
|
||||||
@@ -107,108 +122,107 @@ mfem::Vector make_displacement_direction_true(const mean_field::fem::FEM &f,
|
|||||||
directionField.GetTrueDofs(directionTrue);
|
directionField.GetTrueDofs(directionTrue);
|
||||||
|
|
||||||
return directionTrue;
|
return directionTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
double relative_difference(const mfem::Vector &left, const mfem::Vector &right,
|
double relative_difference(
|
||||||
const MPI_Comm communicator) {
|
const mfem::Vector &left,
|
||||||
MFEM_VERIFY(left.Size() == right.Size(),
|
const mfem::Vector &right,
|
||||||
"Cannot compare prepared pressure-force vectors with "
|
const MPI_Comm communicator
|
||||||
"different sizes.");
|
) {
|
||||||
|
MFEM_VERIFY(
|
||||||
|
left.Size() == right.Size(), "Cannot compare prepared pressure-force vectors with "
|
||||||
|
"different sizes."
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector difference(left);
|
mfem::Vector difference(left);
|
||||||
|
|
||||||
difference -= right;
|
difference -= right;
|
||||||
|
|
||||||
const double scale =
|
const double scale = std::max(
|
||||||
std::max({gravity_prepared_test_utils::global_norm(left, communicator),
|
{gravity_prepared_test_utils::global_norm(left, communicator),
|
||||||
gravity_prepared_test_utils::global_norm(right, communicator),
|
gravity_prepared_test_utils::global_norm(right, communicator),
|
||||||
100.0 * std::numeric_limits<double>::epsilon()});
|
100.0 * std::numeric_limits<double>::epsilon()}
|
||||||
|
);
|
||||||
|
|
||||||
return gravity_prepared_test_utils::global_norm(difference, communicator) /
|
return gravity_prepared_test_utils::global_norm(difference, communicator) / scale;
|
||||||
scale;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
mean_field::operators::context::pressure_force::PressureForceDependencies
|
mean_field::operators::context::pressure_force::PressureForceDependencies make_dependencies() {
|
||||||
make_dependencies() {
|
return {
|
||||||
return {.discretization = {.identity = 1201, .revision = 3},
|
.discretization = {.identity = 1201, .revision = 3},
|
||||||
.enthalpy = {.identity = 1213, .revision = 5},
|
.enthalpy = {.identity = 1213, .revision = 5},
|
||||||
.displacement = {.identity = 1217, .revision = 7}};
|
.displacement = {.identity = 1217, .revision = 7}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
constexpr auto densityValue =
|
constexpr auto densityValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||||
mean_field::utils::blocks::density_field.mass_term);
|
|
||||||
|
|
||||||
constexpr auto displacementValue =
|
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::displacement_field.geometry_term
|
||||||
mean_field::utils::blocks::displacement_field.geometry_term);
|
);
|
||||||
|
|
||||||
constexpr auto gravityGradientValue =
|
constexpr auto gravityGradientValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||||
mean_field::utils::blocks::gravity_field.gradient_term);
|
|
||||||
|
|
||||||
constexpr auto gravityPotentialValue =
|
constexpr auto gravityPotentialValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||||
mean_field::utils::blocks::gravity_field.poisson_term);
|
|
||||||
|
|
||||||
constexpr auto enthalpyValue =
|
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||||
mean_field::utils::blocks::enthalpy_field.specific_term);
|
);
|
||||||
|
|
||||||
constexpr auto barotropicConstantValue =
|
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||||
mean_field::utils::blocks::barotropic_constant_field
|
);
|
||||||
.mass_normalization_term);
|
|
||||||
|
|
||||||
constexpr auto gravityGradientResidual =
|
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::gravity_field.gradient_term
|
||||||
mean_field::utils::blocks::gravity_field.gradient_term);
|
);
|
||||||
|
|
||||||
constexpr auto gravityPotentialResidual =
|
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::gravity_field.poisson_term
|
||||||
mean_field::utils::blocks::gravity_field.poisson_term);
|
);
|
||||||
|
|
||||||
constexpr auto densityResidual =
|
constexpr auto densityResidual =
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||||
mean_field::utils::blocks::density_field.mass_term);
|
|
||||||
|
|
||||||
constexpr auto displacementResidual =
|
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::displacement_field.geometry_term
|
||||||
mean_field::utils::blocks::displacement_field.geometry_term);
|
);
|
||||||
|
|
||||||
constexpr auto enthalpyResidual =
|
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||||
mean_field::utils::blocks::enthalpy_field.specific_term);
|
);
|
||||||
|
|
||||||
constexpr auto massResidual =
|
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||||
mean_field::utils::blocks::barotropic_constant_field
|
);
|
||||||
.mass_normalization_term);
|
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
mean_field::operators::BarotropicEquilibriumLayout
|
mean_field::operators::BarotropicEquilibriumLayout make_coupled_layout(const Maps &maps) {
|
||||||
make_coupled_layout(const Maps &maps) {
|
|
||||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||||
maps.density.reduced_size(), maps.displacement.reduced_size(),
|
maps.density.reduced_size(), maps.displacement.reduced_size(), maps.gravityFlux.reduced_size(),
|
||||||
maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(),
|
maps.gravityPotential.reduced_size(), maps.enthalpy.reduced_size(), 1
|
||||||
maps.enthalpy.reduced_size(), 1};
|
};
|
||||||
|
|
||||||
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
||||||
maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(),
|
maps.gravityFlux.reduced_size(), maps.gravityPotential.reduced_size(), maps.density.reduced_size(),
|
||||||
maps.density.reduced_size(), maps.displacement.reduced_size(),
|
maps.displacement.reduced_size(), maps.enthalpy.reduced_size(), 1
|
||||||
maps.enthalpy.reduced_size(), 1};
|
};
|
||||||
|
|
||||||
return {valueSizes, residualSizes};
|
return {valueSizes, residualSizes};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int index>
|
template <int index>
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
mfem::Vector copy_residual_block(
|
mfem::Vector copy_residual_block(
|
||||||
const mfem::Vector &action,
|
const mfem::Vector &action,
|
||||||
const mean_field::operators::BarotropicEquilibriumLayout &layout,
|
const mean_field::operators::BarotropicEquilibriumLayout &layout,
|
||||||
const mean_field::utils::blocks::residual_block<index> block) {
|
const mean_field::utils::blocks::residual_block<index> block
|
||||||
|
) {
|
||||||
mfem::Vector result(layout.size(block));
|
mfem::Vector result(layout.size(block));
|
||||||
|
|
||||||
const int offset = layout.offset(block);
|
const int offset = layout.offset(block);
|
||||||
@@ -218,13 +232,14 @@ mfem::Vector copy_residual_block(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} // namespace prepared_pressure_force_test_utils
|
} // namespace prepared_pressure_force_test_utils
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Prepared Pressure Force Uses FieldDof Supported Dimensions And Owns Its "
|
"Prepared Pressure Force Uses FieldDof Supported Dimensions And Owns Its "
|
||||||
"Context",
|
"Context",
|
||||||
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit) {
|
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::unit
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -235,8 +250,7 @@ TEST_CASE(
|
|||||||
|
|
||||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||||
|
|
||||||
mean_field::operators::PreparedPressureForceOperator preparedOperator(
|
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||||
f, *f.domainMapperStateless, equationOfState);
|
|
||||||
|
|
||||||
REQUIRE(maps.enthalpy.reduced_size() < maps.enthalpy.full_size());
|
REQUIRE(maps.enthalpy.reduced_size() < maps.enthalpy.full_size());
|
||||||
|
|
||||||
@@ -244,17 +258,18 @@ TEST_CASE(
|
|||||||
|
|
||||||
CHECK(preparedOperator.GetEnthalpySize() == maps.enthalpy.reduced_size());
|
CHECK(preparedOperator.GetEnthalpySize() == maps.enthalpy.reduced_size());
|
||||||
|
|
||||||
CHECK(preparedOperator.GetDisplacementSize() ==
|
CHECK(preparedOperator.GetDisplacementSize() == maps.displacement.reduced_size());
|
||||||
maps.displacement.reduced_size());
|
|
||||||
|
|
||||||
CHECK(&preparedOperator.GetContext().GetPreparationStatistics() ==
|
CHECK(
|
||||||
&preparedOperator.GetContextPreparationStatistics());
|
&preparedOperator.GetContext().GetPreparationStatistics() == &preparedOperator.GetContextPreparationStatistics()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Prepared Pressure Force Jacobian Matches Full Stateless Columns "
|
TEST_CASE(
|
||||||
|
"Prepared Pressure Force Jacobian Matches Full Stateless Columns "
|
||||||
"Through FieldDof Restriction",
|
"Through FieldDof Restriction",
|
||||||
tags::barotrope &tags::pressure &tags::prepared &tags::field
|
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::integration &tags::accuracy
|
||||||
&tags::integration &tags::accuracy) {
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -265,90 +280,87 @@ TEST_CASE("Prepared Pressure Force Jacobian Matches Full Stateless Columns "
|
|||||||
|
|
||||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||||
|
|
||||||
const mfem::Vector enthalpy = maps.enthalpy.gather(
|
const mfem::Vector enthalpy =
|
||||||
prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.47));
|
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.47));
|
||||||
|
|
||||||
const mfem::Vector displacement = maps.displacement.gather(
|
const mfem::Vector displacement = maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.69));
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.69));
|
|
||||||
|
|
||||||
const mfem::Vector enthalpyDirection = maps.enthalpy.gather(
|
const mfem::Vector enthalpyDirection =
|
||||||
prepared_pressure_force_test_utils::make_enthalpy_direction_true(f,
|
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_enthalpy_direction_true(f, 0.73));
|
||||||
0.73));
|
|
||||||
|
|
||||||
const mfem::Vector displacementDirection = maps.displacement.gather(
|
const mfem::Vector displacementDirection =
|
||||||
prepared_pressure_force_test_utils::make_displacement_direction_true(
|
maps.displacement.gather(prepared_pressure_force_test_utils::make_displacement_direction_true(f, 0.83));
|
||||||
f, 0.83));
|
|
||||||
|
|
||||||
mean_field::operators::PreparedPressureForceOperator preparedOperator(
|
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||||
f, *f.domainMapperStateless, equationOfState);
|
|
||||||
|
|
||||||
preparedOperator.Prepare(
|
preparedOperator.Prepare(
|
||||||
{.enthalpy = enthalpy, .displacement = displacement},
|
{.enthalpy = enthalpy, .displacement = displacement}, prepared_pressure_force_test_utils::make_dependencies()
|
||||||
prepared_pressure_force_test_utils::make_dependencies());
|
);
|
||||||
|
|
||||||
const mfem::Vector enthalpyTrue = maps.enthalpy.scatter(enthalpy);
|
const mfem::Vector enthalpyTrue = maps.enthalpy.scatter(enthalpy);
|
||||||
|
|
||||||
const mfem::Vector displacementTrue = maps.displacement.scatter(displacement);
|
const mfem::Vector displacementTrue = maps.displacement.scatter(displacement);
|
||||||
|
|
||||||
const mfem::Vector enthalpyDirectionTrue =
|
const mfem::Vector enthalpyDirectionTrue = maps.enthalpy.scatter(enthalpyDirection);
|
||||||
maps.enthalpy.scatter(enthalpyDirection);
|
|
||||||
|
|
||||||
const mfem::Vector displacementDirectionTrue =
|
const mfem::Vector displacementDirectionTrue = maps.displacement.scatter(displacementDirection);
|
||||||
maps.displacement.scatter(displacementDirection);
|
|
||||||
|
|
||||||
mfem::Vector preparedEnthalpyAction;
|
mfem::Vector preparedEnthalpyAction;
|
||||||
mfem::Vector kernelEnthalpyActionTrue;
|
mfem::Vector kernelEnthalpyActionTrue;
|
||||||
|
|
||||||
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyDirection,
|
preparedOperator.ApplyEnthalpyJacobianAction(enthalpyDirection, preparedEnthalpyAction);
|
||||||
preparedEnthalpyAction);
|
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_enthalpy_action(
|
mean_field::operators::kernels::apply_pressure_force_enthalpy_action(
|
||||||
f, *f.domainMapperStateless, equationOfState, enthalpyTrue,
|
f, *f.domainMapperStateless, equationOfState, enthalpyTrue, enthalpyDirectionTrue, displacementTrue,
|
||||||
enthalpyDirectionTrue, displacementTrue, kernelEnthalpyActionTrue);
|
kernelEnthalpyActionTrue
|
||||||
|
);
|
||||||
|
|
||||||
const mfem::Vector kernelEnthalpyAction =
|
const mfem::Vector kernelEnthalpyAction = maps.displacement.gather(kernelEnthalpyActionTrue);
|
||||||
maps.displacement.gather(kernelEnthalpyActionTrue);
|
|
||||||
|
|
||||||
CHECK(prepared_pressure_force_test_utils::relative_difference(
|
CHECK(
|
||||||
preparedEnthalpyAction, kernelEnthalpyAction, f.mesh->GetComm()) <
|
prepared_pressure_force_test_utils::relative_difference(
|
||||||
2.0e-12);
|
preparedEnthalpyAction, kernelEnthalpyAction, f.mesh->GetComm()
|
||||||
|
) < 2.0e-12
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector preparedDisplacementAction;
|
mfem::Vector preparedDisplacementAction;
|
||||||
mfem::Vector kernelDisplacementActionTrue;
|
mfem::Vector kernelDisplacementActionTrue;
|
||||||
|
|
||||||
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection,
|
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, preparedDisplacementAction);
|
||||||
preparedDisplacementAction);
|
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_displacement_action(
|
mean_field::operators::kernels::apply_pressure_force_displacement_action(
|
||||||
f, *f.domainMapperStateless, equationOfState, enthalpyTrue,
|
f, *f.domainMapperStateless, equationOfState, enthalpyTrue, displacementDirectionTrue, displacementTrue,
|
||||||
displacementDirectionTrue, displacementTrue,
|
kernelDisplacementActionTrue
|
||||||
kernelDisplacementActionTrue);
|
);
|
||||||
|
|
||||||
const mfem::Vector kernelDisplacementAction =
|
const mfem::Vector kernelDisplacementAction = maps.displacement.gather(kernelDisplacementActionTrue);
|
||||||
maps.displacement.gather(kernelDisplacementActionTrue);
|
|
||||||
|
|
||||||
CHECK(prepared_pressure_force_test_utils::relative_difference(
|
CHECK(
|
||||||
preparedDisplacementAction, kernelDisplacementAction,
|
prepared_pressure_force_test_utils::relative_difference(
|
||||||
f.mesh->GetComm()) < 2.0e-12);
|
preparedDisplacementAction, kernelDisplacementAction, f.mesh->GetComm()
|
||||||
|
) < 2.0e-12
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector fusedAction;
|
mfem::Vector fusedAction;
|
||||||
|
|
||||||
preparedOperator.ApplyCompleteJacobianAction(
|
preparedOperator.ApplyCompleteJacobianAction(enthalpyDirection, displacementDirection, fusedAction);
|
||||||
enthalpyDirection, displacementDirection, fusedAction);
|
|
||||||
|
|
||||||
mfem::Vector expectedFusedAction(kernelEnthalpyAction);
|
mfem::Vector expectedFusedAction(kernelEnthalpyAction);
|
||||||
|
|
||||||
expectedFusedAction += kernelDisplacementAction;
|
expectedFusedAction += kernelDisplacementAction;
|
||||||
|
|
||||||
CHECK(prepared_pressure_force_test_utils::relative_difference(
|
CHECK(
|
||||||
fusedAction, expectedFusedAction, f.mesh->GetComm()) < 2.0e-12);
|
prepared_pressure_force_test_utils::relative_difference(fusedAction, expectedFusedAction, f.mesh->GetComm()) <
|
||||||
|
2.0e-12
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Prepared Pressure Force MFEM Adapter Routes Reduced Coupled FieldDof "
|
"Prepared Pressure Force MFEM Adapter Routes Reduced Coupled FieldDof "
|
||||||
"Blocks",
|
"Blocks",
|
||||||
tags::barotrope &tags::pressure &tags::prepared &tags::field
|
tags::barotrope &tags::pressure &tags::prepared &tags::field &tags::integration &tags::jacobian
|
||||||
&tags::integration &tags::jacobian &tags::mfem_operators &tags::unit) {
|
&tags::mfem_operators &tags::unit
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
@@ -359,38 +371,31 @@ TEST_CASE(
|
|||||||
|
|
||||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
||||||
|
|
||||||
const mfem::Vector enthalpy = maps.enthalpy.gather(
|
const mfem::Vector enthalpy =
|
||||||
prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.53));
|
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_positive_enthalpy_true(f, 0.53));
|
||||||
|
|
||||||
const mfem::Vector displacement = maps.displacement.gather(
|
const mfem::Vector displacement = maps.displacement.gather(gravity_prepared_test_utils::make_displacement(f, 0.71));
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.71));
|
|
||||||
|
|
||||||
const mfem::Vector enthalpyDirection = maps.enthalpy.gather(
|
const mfem::Vector enthalpyDirection =
|
||||||
prepared_pressure_force_test_utils::make_enthalpy_direction_true(f,
|
maps.enthalpy.gather(prepared_pressure_force_test_utils::make_enthalpy_direction_true(f, 0.89));
|
||||||
0.89));
|
|
||||||
|
|
||||||
const mfem::Vector displacementDirection = maps.displacement.gather(
|
const mfem::Vector displacementDirection =
|
||||||
prepared_pressure_force_test_utils::make_displacement_direction_true(
|
maps.displacement.gather(prepared_pressure_force_test_utils::make_displacement_direction_true(f, 0.97));
|
||||||
f, 0.97));
|
|
||||||
|
|
||||||
mean_field::operators::PreparedPressureForceOperator preparedOperator(
|
mean_field::operators::PreparedPressureForceOperator preparedOperator(f, *f.domainMapperStateless, equationOfState);
|
||||||
f, *f.domainMapperStateless, equationOfState);
|
|
||||||
|
|
||||||
preparedOperator.Prepare(
|
preparedOperator.Prepare(
|
||||||
{.enthalpy = enthalpy, .displacement = displacement},
|
{.enthalpy = enthalpy, .displacement = displacement}, prepared_pressure_force_test_utils::make_dependencies()
|
||||||
prepared_pressure_force_test_utils::make_dependencies());
|
);
|
||||||
|
|
||||||
const mean_field::operators::BarotropicEquilibriumLayout layout =
|
const mean_field::operators::BarotropicEquilibriumLayout layout =
|
||||||
prepared_pressure_force_test_utils::make_coupled_layout(maps);
|
prepared_pressure_force_test_utils::make_coupled_layout(maps);
|
||||||
|
|
||||||
mean_field::operators::PreparedPressureForceJacobianOperator adapter(
|
mean_field::operators::PreparedPressureForceJacobianOperator adapter(layout, preparedOperator);
|
||||||
layout, preparedOperator);
|
|
||||||
|
|
||||||
CHECK(layout.size(prepared_pressure_force_test_utils::enthalpyValue) ==
|
CHECK(layout.size(prepared_pressure_force_test_utils::enthalpyValue) == maps.enthalpy.reduced_size());
|
||||||
maps.enthalpy.reduced_size());
|
|
||||||
|
|
||||||
CHECK(layout.size(prepared_pressure_force_test_utils::densityValue) ==
|
CHECK(layout.size(prepared_pressure_force_test_utils::densityValue) == maps.density.reduced_size());
|
||||||
maps.density.reduced_size());
|
|
||||||
|
|
||||||
mfem::BlockVector direction(layout.value_offsets());
|
mfem::BlockVector direction(layout.value_offsets());
|
||||||
|
|
||||||
@@ -401,66 +406,74 @@ TEST_CASE(
|
|||||||
*/
|
*/
|
||||||
direction.GetBlock(prepared_pressure_force_test_utils::densityValue) = 0.37;
|
direction.GetBlock(prepared_pressure_force_test_utils::densityValue) = 0.37;
|
||||||
|
|
||||||
direction.GetBlock(prepared_pressure_force_test_utils::gravityGradientValue) =
|
direction.GetBlock(prepared_pressure_force_test_utils::gravityGradientValue) = -0.41;
|
||||||
-0.41;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(prepared_pressure_force_test_utils::gravityPotentialValue) = 0.59;
|
||||||
prepared_pressure_force_test_utils::gravityPotentialValue) = 0.59;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(prepared_pressure_force_test_utils::barotropicConstantValue) = -0.73;
|
||||||
prepared_pressure_force_test_utils::barotropicConstantValue) = -0.73;
|
|
||||||
|
|
||||||
direction.GetBlock(prepared_pressure_force_test_utils::displacementValue) =
|
direction.GetBlock(prepared_pressure_force_test_utils::displacementValue) = displacementDirection;
|
||||||
displacementDirection;
|
|
||||||
|
|
||||||
direction.GetBlock(prepared_pressure_force_test_utils::enthalpyValue) =
|
direction.GetBlock(prepared_pressure_force_test_utils::enthalpyValue) = enthalpyDirection;
|
||||||
enthalpyDirection;
|
|
||||||
|
|
||||||
mfem::Vector expectedDisplacementAction;
|
mfem::Vector expectedDisplacementAction;
|
||||||
|
|
||||||
preparedOperator.ApplyCompleteJacobianAction(
|
preparedOperator.ApplyCompleteJacobianAction(enthalpyDirection, displacementDirection, expectedDisplacementAction);
|
||||||
enthalpyDirection, displacementDirection, expectedDisplacementAction);
|
|
||||||
|
|
||||||
mfem::Vector action;
|
mfem::Vector action;
|
||||||
|
|
||||||
adapter.Mult(direction, action);
|
adapter.Mult(direction, action);
|
||||||
|
|
||||||
const mfem::Vector displacementResidualAction =
|
const mfem::Vector displacementResidualAction = prepared_pressure_force_test_utils::copy_residual_block(
|
||||||
prepared_pressure_force_test_utils::copy_residual_block(
|
action, layout, prepared_pressure_force_test_utils::displacementResidual
|
||||||
action, layout,
|
);
|
||||||
prepared_pressure_force_test_utils::displacementResidual);
|
|
||||||
|
|
||||||
CHECK(prepared_pressure_force_test_utils::relative_difference(
|
CHECK(
|
||||||
displacementResidualAction, expectedDisplacementAction,
|
prepared_pressure_force_test_utils::relative_difference(
|
||||||
f.mesh->GetComm()) < 2.0e-14);
|
displacementResidualAction, expectedDisplacementAction, f.mesh->GetComm()
|
||||||
|
) < 2.0e-14
|
||||||
CHECK(prepared_pressure_force_test_utils::copy_residual_block(
|
);
|
||||||
action, layout,
|
|
||||||
prepared_pressure_force_test_utils::gravityGradientResidual)
|
|
||||||
.Norml2() == 0.0);
|
|
||||||
|
|
||||||
CHECK(prepared_pressure_force_test_utils::copy_residual_block(
|
|
||||||
action, layout,
|
|
||||||
prepared_pressure_force_test_utils::gravityPotentialResidual)
|
|
||||||
.Norml2() == 0.0);
|
|
||||||
|
|
||||||
CHECK(prepared_pressure_force_test_utils::copy_residual_block(
|
|
||||||
action, layout, prepared_pressure_force_test_utils::densityResidual)
|
|
||||||
.Norml2() == 0.0);
|
|
||||||
|
|
||||||
CHECK(
|
CHECK(
|
||||||
prepared_pressure_force_test_utils::copy_residual_block(
|
prepared_pressure_force_test_utils::copy_residual_block(
|
||||||
action, layout, prepared_pressure_force_test_utils::enthalpyResidual)
|
action, layout, prepared_pressure_force_test_utils::gravityGradientResidual
|
||||||
.Norml2() == 0.0);
|
)
|
||||||
|
.Norml2() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(prepared_pressure_force_test_utils::copy_residual_block(
|
CHECK(
|
||||||
action, layout, prepared_pressure_force_test_utils::massResidual)
|
prepared_pressure_force_test_utils::copy_residual_block(
|
||||||
.Norml2() == 0.0);
|
action, layout, prepared_pressure_force_test_utils::gravityPotentialResidual
|
||||||
|
)
|
||||||
|
.Norml2() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
prepared_pressure_force_test_utils::copy_residual_block(
|
||||||
|
action, layout, prepared_pressure_force_test_utils::densityResidual
|
||||||
|
)
|
||||||
|
.Norml2() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
prepared_pressure_force_test_utils::copy_residual_block(
|
||||||
|
action, layout, prepared_pressure_force_test_utils::enthalpyResidual
|
||||||
|
)
|
||||||
|
.Norml2() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
prepared_pressure_force_test_utils::copy_residual_block(
|
||||||
|
action, layout, prepared_pressure_force_test_utils::massResidual
|
||||||
|
)
|
||||||
|
.Norml2() == 0.0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
TEST_CASE(
|
||||||
tags::barotrope &tags::pressure &tags::kernels &tags::integration
|
"Pressure Force Residual Converges To A Manufactured Analytic Force",
|
||||||
&tags::convergence &tags::h_refinement &tags::analytic_comparison
|
tags::barotrope &tags::pressure &tags::kernels &tags::integration &tags::convergence &tags::h_refinement
|
||||||
&tags::accuracy) {
|
&tags::analytic_comparison &tags::accuracy
|
||||||
|
) {
|
||||||
constexpr int dimension = 3;
|
constexpr int dimension = 3;
|
||||||
|
|
||||||
constexpr std::array<int, 2> refinementLevels{0, 1};
|
constexpr std::array<int, 2> refinementLevels{0, 1};
|
||||||
@@ -474,12 +487,10 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
|
|
||||||
std::array<double, refinementLevels.size()> relativeErrors{};
|
std::array<double, refinementLevels.size()> relativeErrors{};
|
||||||
|
|
||||||
for (std::size_t levelIndex = 0; levelIndex < refinementLevels.size();
|
for (std::size_t levelIndex = 0; levelIndex < refinementLevels.size(); ++levelIndex) {
|
||||||
++levelIndex) {
|
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, refinementLevels[levelIndex]);
|
||||||
args.mesh_file, args, refinementLevels[levelIndex]);
|
|
||||||
|
|
||||||
REQUIRE(f.okay());
|
REQUIRE(f.okay());
|
||||||
REQUIRE(f.mesh->Dimension() == dimension);
|
REQUIRE(f.mesh->Dimension() == dimension);
|
||||||
@@ -487,15 +498,12 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
|
|
||||||
const MPI_Comm communicator = f.mesh->GetComm();
|
const MPI_Comm communicator = f.mesh->GetComm();
|
||||||
|
|
||||||
constexpr double supportRadius =
|
constexpr double supportRadius = supportRadiusFraction * mean_field::utils::RADIUS;
|
||||||
supportRadiusFraction * mean_field::utils::RADIUS;
|
|
||||||
|
|
||||||
constexpr double supportRadiusSquared = supportRadius * supportRadius;
|
constexpr double supportRadiusSquared = supportRadius * supportRadius;
|
||||||
|
|
||||||
auto analyticEnthalpyFunction = [supportRadiusSquared](
|
auto analyticEnthalpyFunction = [supportRadiusSquared](const mfem::Vector &position) {
|
||||||
const mfem::Vector &position) {
|
const double normalizedRadiusSquared = (position * position) / supportRadiusSquared;
|
||||||
const double normalizedRadiusSquared =
|
|
||||||
(position * position) / supportRadiusSquared;
|
|
||||||
|
|
||||||
if (normalizedRadiusSquared >= 1.0) {
|
if (normalizedRadiusSquared >= 1.0) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
@@ -503,18 +511,14 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
|
|
||||||
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
|
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
|
||||||
|
|
||||||
return amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared /
|
return amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared / distanceToSupportBoundary);
|
||||||
distanceToSupportBoundary);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto analyticPressureForceFunction = [supportRadiusSquared](
|
auto analyticPressureForceFunction = [supportRadiusSquared](const mfem::Vector &position, mfem::Vector &force) {
|
||||||
const mfem::Vector &position,
|
|
||||||
mfem::Vector &force) {
|
|
||||||
force.SetSize(dimension);
|
force.SetSize(dimension);
|
||||||
force = 0.0;
|
force = 0.0;
|
||||||
|
|
||||||
const double normalizedRadiusSquared =
|
const double normalizedRadiusSquared = (position * position) / supportRadiusSquared;
|
||||||
(position * position) / supportRadiusSquared;
|
|
||||||
|
|
||||||
if (normalizedRadiusSquared >= 1.0) {
|
if (normalizedRadiusSquared >= 1.0) {
|
||||||
return;
|
return;
|
||||||
@@ -523,24 +527,20 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
|
const double distanceToSupportBoundary = 1.0 - normalizedRadiusSquared;
|
||||||
|
|
||||||
const double enthalpy =
|
const double enthalpy =
|
||||||
amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared /
|
amplitude * std::exp(-bumpSharpness * normalizedRadiusSquared / distanceToSupportBoundary);
|
||||||
distanceToSupportBoundary);
|
|
||||||
|
|
||||||
const double pressureGradientScale =
|
const double pressureGradientScale =
|
||||||
-2.0 * bumpSharpness * std::pow(enthalpy, 4.0) /
|
-2.0 * bumpSharpness * std::pow(enthalpy, 4.0) /
|
||||||
(supportRadiusSquared * distanceToSupportBoundary *
|
(supportRadiusSquared * distanceToSupportBoundary * distanceToSupportBoundary);
|
||||||
distanceToSupportBoundary);
|
|
||||||
|
|
||||||
for (int component = 0; component < dimension; ++component) {
|
for (int component = 0; component < dimension; ++component) {
|
||||||
force(component) = pressureGradientScale * position(component);
|
force(component) = pressureGradientScale * position(component);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mfem::FunctionCoefficient analyticEnthalpyCoefficient(
|
mfem::FunctionCoefficient analyticEnthalpyCoefficient(analyticEnthalpyFunction);
|
||||||
analyticEnthalpyFunction);
|
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient analyticPressureForceCoefficient(
|
mfem::VectorFunctionCoefficient analyticPressureForceCoefficient(dimension, analyticPressureForceFunction);
|
||||||
dimension, analyticPressureForceFunction);
|
|
||||||
|
|
||||||
mfem::ParGridFunction discreteEnthalpyField(f.enthalpyFes.get());
|
mfem::ParGridFunction discreteEnthalpyField(f.enthalpyFes.get());
|
||||||
|
|
||||||
@@ -558,8 +558,8 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
mfem::Vector discreteResidual;
|
mfem::Vector discreteResidual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_pressure_force_residual(
|
mean_field::operators::kernels::apply_pressure_force_residual(
|
||||||
f, *f.domainMapperStateless, barotrope, discreteEnthalpyTrue,
|
f, *f.domainMapperStateless, barotrope, discreteEnthalpyTrue, zeroDisplacement, discreteResidual
|
||||||
zeroDisplacement, discreteResidual);
|
);
|
||||||
|
|
||||||
REQUIRE(discreteResidual.Size() == f.displacementFes->GetTrueVSize());
|
REQUIRE(discreteResidual.Size() == f.displacementFes->GetTrueVSize());
|
||||||
|
|
||||||
@@ -569,8 +569,7 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
|
|
||||||
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
|
const int vacuumAttribute = field_dof_test_utils::vacuum_material_attribute;
|
||||||
|
|
||||||
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size();
|
for (int attributeIndex = 0; attributeIndex < f.mesh->attributes.Size(); ++attributeIndex) {
|
||||||
++attributeIndex) {
|
|
||||||
const int attribute = f.mesh->attributes[attributeIndex];
|
const int attribute = f.mesh->attributes[attributeIndex];
|
||||||
|
|
||||||
if (attribute != vacuumAttribute) {
|
if (attribute != vacuumAttribute) {
|
||||||
@@ -578,34 +577,28 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mfem::Geometry::Type elementGeometry =
|
const mfem::Geometry::Type elementGeometry = f.displacementFes->GetFE(0)->GetGeomType();
|
||||||
f.displacementFes->GetFE(0)->GetGeomType();
|
|
||||||
|
|
||||||
for (int element = 1; element < f.mesh->GetNE(); ++element) {
|
for (int element = 1; element < f.mesh->GetNE(); ++element) {
|
||||||
REQUIRE(f.displacementFes->GetFE(element)->GetGeomType() ==
|
REQUIRE(f.displacementFes->GetFE(element)->GetGeomType() == elementGeometry);
|
||||||
elementGeometry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int referenceQuadratureOrder =
|
const int referenceQuadratureOrder = 2 * f.displacementFes->GetMaxElementOrder() + 16;
|
||||||
2 * f.displacementFes->GetMaxElementOrder() + 16;
|
|
||||||
|
|
||||||
const mfem::IntegrationRule &referenceQuadrature =
|
const mfem::IntegrationRule &referenceQuadrature =
|
||||||
mfem::IntRules.Get(elementGeometry, referenceQuadratureOrder);
|
mfem::IntRules.Get(elementGeometry, referenceQuadratureOrder);
|
||||||
|
|
||||||
auto *analyticForceIntegrator =
|
auto *analyticForceIntegrator = new mfem::VectorDomainLFIntegrator(analyticPressureForceCoefficient);
|
||||||
new mfem::VectorDomainLFIntegrator(analyticPressureForceCoefficient);
|
|
||||||
|
|
||||||
analyticForceIntegrator->SetIntRule(&referenceQuadrature);
|
analyticForceIntegrator->SetIntRule(&referenceQuadrature);
|
||||||
|
|
||||||
mfem::ParLinearForm analyticForceLoad(f.displacementFes.get());
|
mfem::ParLinearForm analyticForceLoad(f.displacementFes.get());
|
||||||
|
|
||||||
analyticForceLoad.AddDomainIntegrator(analyticForceIntegrator,
|
analyticForceLoad.AddDomainIntegrator(analyticForceIntegrator, stellarMarker);
|
||||||
stellarMarker);
|
|
||||||
|
|
||||||
analyticForceLoad.Assemble();
|
analyticForceLoad.Assemble();
|
||||||
|
|
||||||
std::unique_ptr<mfem::HypreParVector> analyticForceHypreVector(
|
std::unique_ptr<mfem::HypreParVector> analyticForceHypreVector(analyticForceLoad.ParallelAssemble());
|
||||||
analyticForceLoad.ParallelAssemble());
|
|
||||||
|
|
||||||
REQUIRE(analyticForceHypreVector != nullptr);
|
REQUIRE(analyticForceHypreVector != nullptr);
|
||||||
|
|
||||||
@@ -613,8 +606,7 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
|
|
||||||
REQUIRE(analyticForceTrue.Size() == discreteResidual.Size());
|
REQUIRE(analyticForceTrue.Size() == discreteResidual.Size());
|
||||||
|
|
||||||
const double analyticForceNorm = gravity_prepared_test_utils::global_norm(
|
const double analyticForceNorm = gravity_prepared_test_utils::global_norm(analyticForceTrue, communicator);
|
||||||
analyticForceTrue, communicator);
|
|
||||||
|
|
||||||
REQUIRE(std::isfinite(analyticForceNorm));
|
REQUIRE(std::isfinite(analyticForceNorm));
|
||||||
REQUIRE(analyticForceNorm > 0.0);
|
REQUIRE(analyticForceNorm > 0.0);
|
||||||
@@ -631,8 +623,7 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
rieszForm.Assemble();
|
rieszForm.Assemble();
|
||||||
rieszForm.Finalize();
|
rieszForm.Finalize();
|
||||||
|
|
||||||
std::unique_ptr<mfem::HypreParMatrix> rieszMatrix(
|
std::unique_ptr<mfem::HypreParMatrix> rieszMatrix(rieszForm.ParallelAssemble());
|
||||||
rieszForm.ParallelAssemble());
|
|
||||||
|
|
||||||
REQUIRE(rieszMatrix != nullptr);
|
REQUIRE(rieszMatrix != nullptr);
|
||||||
REQUIRE(rieszMatrix->Height() == discreteResidual.Size());
|
REQUIRE(rieszMatrix->Height() == discreteResidual.Size());
|
||||||
@@ -651,28 +642,28 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
rieszSolver.SetMaxIter(5000);
|
rieszSolver.SetMaxIter(5000);
|
||||||
rieszSolver.SetPrintLevel(0);
|
rieszSolver.SetPrintLevel(0);
|
||||||
|
|
||||||
auto calculateDualNorm = [&rieszSolver,
|
auto calculateDualNorm = [&rieszSolver, communicator](const mfem::Vector &functional) {
|
||||||
communicator](const mfem::Vector &functional) {
|
|
||||||
mfem::Vector rieszRepresentative(functional.Size());
|
mfem::Vector rieszRepresentative(functional.Size());
|
||||||
|
|
||||||
rieszRepresentative = 0.0;
|
rieszRepresentative = 0.0;
|
||||||
|
|
||||||
rieszSolver.Mult(functional, rieszRepresentative);
|
rieszSolver.Mult(functional, rieszRepresentative);
|
||||||
|
|
||||||
MFEM_VERIFY(rieszSolver.GetConverged(),
|
MFEM_VERIFY(
|
||||||
"The pressure-force convergence-test Riesz solve "
|
rieszSolver.GetConverged(), "The pressure-force convergence-test Riesz solve "
|
||||||
"did not converge.");
|
"did not converge."
|
||||||
|
);
|
||||||
|
|
||||||
const double dualNormSquared = gravity_prepared_test_utils::global_dot(
|
const double dualNormSquared =
|
||||||
functional, rieszRepresentative, communicator);
|
gravity_prepared_test_utils::global_dot(functional, rieszRepresentative, communicator);
|
||||||
|
|
||||||
MFEM_VERIFY(std::isfinite(dualNormSquared),
|
MFEM_VERIFY(std::isfinite(dualNormSquared), "The pressure-force dual norm is not finite.");
|
||||||
"The pressure-force dual norm is not finite.");
|
|
||||||
|
|
||||||
MFEM_VERIFY(dualNormSquared >=
|
MFEM_VERIFY(
|
||||||
-100.0 * std::numeric_limits<double>::epsilon(),
|
dualNormSquared >= -100.0 * std::numeric_limits<double>::epsilon(),
|
||||||
"The pressure-force Riesz operator produced a "
|
"The pressure-force Riesz operator produced a "
|
||||||
"negative dual norm.");
|
"negative dual norm."
|
||||||
|
);
|
||||||
|
|
||||||
return std::sqrt(std::max(dualNormSquared, 0.0));
|
return std::sqrt(std::max(dualNormSquared, 0.0));
|
||||||
};
|
};
|
||||||
@@ -700,12 +691,9 @@ TEST_CASE("Pressure Force Residual Converges To A Manufactured Analytic Force",
|
|||||||
REQUIRE(relativeError > 0.0);
|
REQUIRE(relativeError > 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(
|
static_assert(refinementLevels.size() == 2, "This reduced convergence test expects exactly two refinement levels.");
|
||||||
refinementLevels.size() == 2,
|
|
||||||
"This reduced convergence test expects exactly two refinement levels.");
|
|
||||||
|
|
||||||
const double observedRate =
|
const double observedRate = std::log(relativeErrors[0] / relativeErrors[1]) / std::log(2.0);
|
||||||
std::log(relativeErrors[0] / relativeErrors[1]) / std::log(2.0);
|
|
||||||
|
|
||||||
INFO("Level 0 pressure-force relative dual error = " << relativeErrors[0]);
|
INFO("Level 0 pressure-force relative dual error = " << relativeErrors[0]);
|
||||||
|
|
||||||
|
|||||||
@@ -11,97 +11,85 @@ import mean_field;
|
|||||||
import test_helpers;
|
import test_helpers;
|
||||||
|
|
||||||
namespace rotational_displacement_force_test_utils {
|
namespace rotational_displacement_force_test_utils {
|
||||||
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
using CoupledForm = mean_field::utils::blocks::barotropic_equilibrium_form;
|
||||||
|
|
||||||
constexpr auto densityValue =
|
constexpr auto densityValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||||
mean_field::utils::blocks::density_field.mass_term);
|
|
||||||
|
|
||||||
constexpr auto displacementValue =
|
constexpr auto displacementValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::displacement_field.geometry_term
|
||||||
mean_field::utils::blocks::displacement_field.geometry_term);
|
);
|
||||||
|
|
||||||
constexpr auto gravityGradientValue =
|
constexpr auto gravityGradientValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.gradient_term);
|
||||||
mean_field::utils::blocks::gravity_field.gradient_term);
|
|
||||||
|
|
||||||
constexpr auto gravityPotentialValue =
|
constexpr auto gravityPotentialValue =
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::get_value_block<CoupledForm>(mean_field::utils::blocks::gravity_field.poisson_term);
|
||||||
mean_field::utils::blocks::gravity_field.poisson_term);
|
|
||||||
|
|
||||||
constexpr auto enthalpyValue =
|
constexpr auto enthalpyValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||||
mean_field::utils::blocks::enthalpy_field.specific_term);
|
);
|
||||||
|
|
||||||
constexpr auto barotropicConstantValue =
|
constexpr auto barotropicConstantValue = mean_field::utils::blocks::get_value_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_value_block<CoupledForm>(
|
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||||
mean_field::utils::blocks::barotropic_constant_field
|
);
|
||||||
.mass_normalization_term);
|
|
||||||
|
|
||||||
constexpr auto gravityGradientResidual =
|
constexpr auto gravityGradientResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::gravity_field.gradient_term
|
||||||
mean_field::utils::blocks::gravity_field.gradient_term);
|
);
|
||||||
|
|
||||||
constexpr auto gravityPotentialResidual =
|
constexpr auto gravityPotentialResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::gravity_field.poisson_term
|
||||||
mean_field::utils::blocks::gravity_field.poisson_term);
|
);
|
||||||
|
|
||||||
constexpr auto densityResidual =
|
constexpr auto densityResidual =
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::get_residual_block<CoupledForm>(mean_field::utils::blocks::density_field.mass_term);
|
||||||
mean_field::utils::blocks::density_field.mass_term);
|
|
||||||
|
|
||||||
constexpr auto displacementResidual =
|
constexpr auto displacementResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::displacement_field.geometry_term
|
||||||
mean_field::utils::blocks::displacement_field.geometry_term);
|
);
|
||||||
|
|
||||||
constexpr auto enthalpyResidual =
|
constexpr auto enthalpyResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::enthalpy_field.specific_term
|
||||||
mean_field::utils::blocks::enthalpy_field.specific_term);
|
);
|
||||||
|
|
||||||
constexpr auto massResidual =
|
constexpr auto massResidual = mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
||||||
mean_field::utils::blocks::get_residual_block<CoupledForm>(
|
mean_field::utils::blocks::barotropic_constant_field.mass_normalization_term
|
||||||
mean_field::utils::blocks::barotropic_constant_field
|
);
|
||||||
.mass_normalization_term);
|
|
||||||
|
|
||||||
[[nodiscard]] mean_field::operators::RotationalDisplacementForceLayout
|
[[nodiscard]] mean_field::operators::RotationalDisplacementForceLayout make_layout(const mean_field::fem::FEM &f) {
|
||||||
make_layout(const mean_field::fem::FEM &f) {
|
|
||||||
using DomainSchema = gravity_prepared_test_utils::DomainSchema;
|
using DomainSchema = gravity_prepared_test_utils::DomainSchema;
|
||||||
|
|
||||||
const auto densityMap =
|
const auto densityMap = gravity_prepared_test_utils::make_field_map<mean_field::field::Density>(f);
|
||||||
gravity_prepared_test_utils::make_field_map<mean_field::field::Density>(
|
const auto displacementMap = gravity_prepared_test_utils::make_field_map<mean_field::field::Displacement>(f);
|
||||||
f);
|
|
||||||
const auto displacementMap = gravity_prepared_test_utils::make_field_map<
|
|
||||||
mean_field::field::Displacement>(f);
|
|
||||||
const auto gravityFluxMap =
|
const auto gravityFluxMap =
|
||||||
mean_field::field::make_field_dof_map<mean_field::field::Gravity,
|
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityFluxFes);
|
||||||
DomainSchema>(*f.gravityFluxFes);
|
const auto gravityPotentialMap =
|
||||||
const auto gravityPotentialMap = mean_field::field::make_field_dof_map<
|
mean_field::field::make_field_dof_map<mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
|
||||||
mean_field::field::Gravity, DomainSchema>(*f.gravityPotentialFes);
|
|
||||||
const auto enthalpyMap =
|
const auto enthalpyMap =
|
||||||
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy,
|
mean_field::field::make_field_dof_map<mean_field::field::Enthalpy, DomainSchema>(*f.enthalpyFes);
|
||||||
DomainSchema>(*f.enthalpyFes);
|
|
||||||
|
|
||||||
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
const std::array<int, CoupledForm::value_block_count> valueSizes{
|
||||||
densityMap.reduced_size(), displacementMap.reduced_size(),
|
densityMap.reduced_size(), displacementMap.reduced_size(), gravityFluxMap.reduced_size(),
|
||||||
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
|
gravityPotentialMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||||
enthalpyMap.reduced_size(), 1};
|
};
|
||||||
|
|
||||||
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
const std::array<int, CoupledForm::residual_block_count> residualSizes{
|
||||||
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(),
|
gravityFluxMap.reduced_size(), gravityPotentialMap.reduced_size(), densityMap.reduced_size(),
|
||||||
densityMap.reduced_size(), displacementMap.reduced_size(),
|
displacementMap.reduced_size(), enthalpyMap.reduced_size(), 1
|
||||||
enthalpyMap.reduced_size(), 1};
|
};
|
||||||
|
|
||||||
return {valueSizes, residualSizes};
|
return {valueSizes, residualSizes};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector make_density(const mean_field::fem::FEM &f,
|
[[nodiscard]] mfem::Vector make_density(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double phase
|
||||||
|
) {
|
||||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||||
|
|
||||||
mfem::FunctionCoefficient densityCoefficient(
|
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
|
||||||
[phase](const mfem::Vector &position) {
|
return 0.88 + 0.06 * std::sin(0.7 * position(0) + phase) + 0.04 * std::cos(0.6 * position(1) - phase) +
|
||||||
return 0.88 + 0.06 * std::sin(0.7 * position(0) + phase) +
|
|
||||||
0.04 * std::cos(0.6 * position(1) - phase) +
|
|
||||||
0.025 * position(2) * position(2);
|
0.025 * position(2) * position(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -110,16 +98,17 @@ make_layout(const mean_field::fem::FEM &f) {
|
|||||||
mfem::Vector densityTrue;
|
mfem::Vector densityTrue;
|
||||||
densityField.GetTrueDofs(densityTrue);
|
densityField.GetTrueDofs(densityTrue);
|
||||||
return densityTrue;
|
return densityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector make_density_direction(const mean_field::fem::FEM &f,
|
[[nodiscard]] mfem::Vector make_density_direction(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double phase
|
||||||
|
) {
|
||||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||||
|
|
||||||
mfem::FunctionCoefficient densityCoefficient(
|
mfem::FunctionCoefficient densityCoefficient([phase](const mfem::Vector &position) {
|
||||||
[phase](const mfem::Vector &position) {
|
return 0.17 * std::sin(0.9 * position(0) + phase) - 0.12 * std::cos(0.8 * position(1) - phase) +
|
||||||
return 0.17 * std::sin(0.9 * position(0) + phase) -
|
0.07 * position(2);
|
||||||
0.12 * std::cos(0.8 * position(1) - phase) + 0.07 * position(2);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
densityField.ProjectCoefficient(densityCoefficient);
|
densityField.ProjectCoefficient(densityCoefficient);
|
||||||
@@ -127,22 +116,18 @@ make_layout(const mean_field::fem::FEM &f) {
|
|||||||
mfem::Vector densityTrue;
|
mfem::Vector densityTrue;
|
||||||
densityField.GetTrueDofs(densityTrue);
|
densityField.GetTrueDofs(densityTrue);
|
||||||
return densityTrue;
|
return densityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_displacement_direction(const mean_field::fem::FEM &f) {
|
||||||
make_displacement_direction(const mean_field::fem::FEM &f) {
|
mfem::Vector direction = gravity_prepared_test_utils::make_displacement(f, 0.91);
|
||||||
mfem::Vector direction =
|
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.91);
|
|
||||||
|
|
||||||
const mfem::Vector second =
|
const mfem::Vector second = gravity_prepared_test_utils::make_displacement(f, 0.27);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.27);
|
|
||||||
|
|
||||||
direction -= second;
|
direction -= second;
|
||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mean_field::physics::RigidRotation
|
[[nodiscard]] mean_field::physics::RigidRotation make_rotation(const double scale = 1.0) {
|
||||||
make_rotation(const double scale = 1.0) {
|
|
||||||
mfem::Vector angularVelocity(3);
|
mfem::Vector angularVelocity(3);
|
||||||
angularVelocity(0) = scale * 0.17;
|
angularVelocity(0) = scale * 0.17;
|
||||||
angularVelocity(1) = scale * -0.09;
|
angularVelocity(1) = scale * -0.09;
|
||||||
@@ -154,19 +139,19 @@ make_rotation(const double scale = 1.0) {
|
|||||||
center(2) = 0.02;
|
center(2) = 0.02;
|
||||||
|
|
||||||
return mean_field::physics::RigidRotation(angularVelocity, center);
|
return mean_field::physics::RigidRotation(angularVelocity, center);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] mean_field::operators::context::rotational_displacement_force::
|
[[nodiscard]] mean_field::operators::context::rotational_displacement_force::RotationalDisplacementForceDependencies
|
||||||
RotationalDisplacementForceDependencies
|
|
||||||
make_dependencies() {
|
make_dependencies() {
|
||||||
return {.discretization = {.identity = 211, .revision = 3},
|
return {
|
||||||
|
.discretization = {.identity = 211, .revision = 3},
|
||||||
.density = {.identity = 223, .revision = 5},
|
.density = {.identity = 223, .revision = 5},
|
||||||
.displacement = {.identity = 227, .revision = 7},
|
.displacement = {.identity = 227, .revision = 7},
|
||||||
.rotation = {.identity = 229, .revision = 11}};
|
.rotation = {.identity = 229, .revision = 11}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector
|
[[nodiscard]] mfem::Vector make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
||||||
make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
|
||||||
mfem::ParGridFunction densityField(f.densityFes.get());
|
mfem::ParGridFunction densityField(f.densityFes.get());
|
||||||
densityField = 0.0;
|
densityField = 0.0;
|
||||||
|
|
||||||
@@ -176,8 +161,7 @@ make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
|||||||
int localVacuumElements = 0;
|
int localVacuumElements = 0;
|
||||||
|
|
||||||
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
for (int elementId = 0; elementId < f.mesh->GetNE(); ++elementId) {
|
||||||
mfem::ElementTransformation *transformation =
|
mfem::ElementTransformation *transformation = f.mesh->GetElementTransformation(elementId);
|
||||||
f.mesh->GetElementTransformation(elementId);
|
|
||||||
|
|
||||||
REQUIRE(transformation != nullptr);
|
REQUIRE(transformation != nullptr);
|
||||||
|
|
||||||
@@ -195,30 +179,32 @@ make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
|||||||
|
|
||||||
int globalVacuumElements = 0;
|
int globalVacuumElements = 0;
|
||||||
|
|
||||||
MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT,
|
MPI_Allreduce(&localVacuumElements, &globalVacuumElements, 1, MPI_INT, MPI_SUM, f.mesh->GetComm());
|
||||||
MPI_SUM, f.mesh->GetComm());
|
|
||||||
|
|
||||||
REQUIRE(globalVacuumElements > 0);
|
REQUIRE(globalVacuumElements > 0);
|
||||||
|
|
||||||
mfem::Vector densityTrue;
|
mfem::Vector densityTrue;
|
||||||
densityField.GetTrueDofs(densityTrue);
|
densityField.GetTrueDofs(densityTrue);
|
||||||
return densityTrue;
|
return densityTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double global_norm(const mfem::Vector &vector,
|
[[nodiscard]] double global_norm(
|
||||||
MPI_Comm communicator) {
|
const mfem::Vector &vector,
|
||||||
|
MPI_Comm communicator
|
||||||
|
) {
|
||||||
const double localSquaredNorm = vector * vector;
|
const double localSquaredNorm = vector * vector;
|
||||||
double globalSquaredNorm = 0.0;
|
double globalSquaredNorm = 0.0;
|
||||||
|
|
||||||
MPI_Allreduce(&localSquaredNorm, &globalSquaredNorm, 1, MPI_DOUBLE, MPI_SUM,
|
MPI_Allreduce(&localSquaredNorm, &globalSquaredNorm, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||||
communicator);
|
|
||||||
|
|
||||||
return std::sqrt(globalSquaredNorm);
|
return std::sqrt(globalSquaredNorm);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double global_dot(const mfem::Vector &left,
|
[[nodiscard]] double global_dot(
|
||||||
|
const mfem::Vector &left,
|
||||||
const mfem::Vector &right,
|
const mfem::Vector &right,
|
||||||
MPI_Comm communicator) {
|
MPI_Comm communicator
|
||||||
|
) {
|
||||||
REQUIRE(left.Size() == right.Size());
|
REQUIRE(left.Size() == right.Size());
|
||||||
|
|
||||||
const double localDot = left * right;
|
const double localDot = left * right;
|
||||||
@@ -227,27 +213,31 @@ make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
|||||||
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
MPI_Allreduce(&localDot, &globalDot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||||
|
|
||||||
return globalDot;
|
return globalDot;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double relative_difference(const mfem::Vector &computed,
|
[[nodiscard]] double relative_difference(
|
||||||
|
const mfem::Vector &computed,
|
||||||
const mfem::Vector &reference,
|
const mfem::Vector &reference,
|
||||||
MPI_Comm communicator) {
|
MPI_Comm communicator
|
||||||
|
) {
|
||||||
REQUIRE(computed.Size() == reference.Size());
|
REQUIRE(computed.Size() == reference.Size());
|
||||||
|
|
||||||
mfem::Vector difference(computed);
|
mfem::Vector difference(computed);
|
||||||
difference -= reference;
|
difference -= reference;
|
||||||
|
|
||||||
return global_norm(difference, communicator) /
|
return global_norm(difference, communicator) /
|
||||||
std::max(global_norm(reference, communicator),
|
std::max(global_norm(reference, communicator), std::numeric_limits<double>::epsilon());
|
||||||
std::numeric_limits<double>::epsilon());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] mfem::Vector centered_difference(
|
[[nodiscard]] mfem::Vector centered_difference(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::physics::RigidRotation &rotation,
|
const mean_field::physics::RigidRotation &rotation,
|
||||||
const mfem::Vector &baseDensity, const mfem::Vector &densityDirection,
|
const mfem::Vector &baseDensity,
|
||||||
|
const mfem::Vector &densityDirection,
|
||||||
const mfem::Vector &baseDisplacement,
|
const mfem::Vector &baseDisplacement,
|
||||||
const mfem::Vector &displacementDirection, const double step) {
|
const mfem::Vector &displacementDirection,
|
||||||
|
const double step
|
||||||
|
) {
|
||||||
mfem::Vector plusDensity(baseDensity);
|
mfem::Vector plusDensity(baseDensity);
|
||||||
plusDensity.Add(step, densityDirection);
|
plusDensity.Add(step, densityDirection);
|
||||||
|
|
||||||
@@ -264,23 +254,24 @@ make_vacuum_only_density(const mean_field::fem::FEM &f) {
|
|||||||
mfem::Vector minusResidual;
|
mfem::Vector minusResidual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, rotation, plusDensity, plusDisplacement,
|
f, *f.domainMapperStateless, rotation, plusDensity, plusDisplacement, plusResidual
|
||||||
plusResidual);
|
);
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, rotation, minusDensity, minusDisplacement,
|
f, *f.domainMapperStateless, rotation, minusDensity, minusDisplacement, minusResidual
|
||||||
minusResidual);
|
);
|
||||||
|
|
||||||
plusResidual -= minusResidual;
|
plusResidual -= minusResidual;
|
||||||
plusResidual /= 2.0 * step;
|
plusResidual /= 2.0 * step;
|
||||||
return plusResidual;
|
return plusResidual;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int index>
|
template <int index>
|
||||||
[[nodiscard]] mfem::Vector copy_residual_block(
|
[[nodiscard]] mfem::Vector copy_residual_block(
|
||||||
const mfem::Vector &action,
|
const mfem::Vector &action,
|
||||||
const mean_field::operators::RotationalDisplacementForceLayout &layout,
|
const mean_field::operators::RotationalDisplacementForceLayout &layout,
|
||||||
const mean_field::utils::blocks::residual_block<index> block) {
|
const mean_field::utils::blocks::residual_block<index> block
|
||||||
|
) {
|
||||||
mfem::Vector result(layout.size(block));
|
mfem::Vector result(layout.size(block));
|
||||||
const int offset = layout.offset(block);
|
const int offset = layout.offset(block);
|
||||||
|
|
||||||
@@ -289,24 +280,23 @@ template <int index>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} // namespace rotational_displacement_force_test_utils
|
} // namespace rotational_displacement_force_test_utils
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE(
|
||||||
"Rotational Displacement Force Query Includes Density Test And Linear "
|
"Rotational Displacement Force Query Includes Density Test And Linear "
|
||||||
"Position",
|
"Position",
|
||||||
tags::rotation_prepared_unit) {
|
tags::rotation_prepared_unit
|
||||||
using DisplacementField =
|
) {
|
||||||
mean_field::field::Field<mean_field::field::Displacement>;
|
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
||||||
|
|
||||||
constexpr int geometryWeightOrder = 4;
|
constexpr int geometryWeightOrder = 4;
|
||||||
|
|
||||||
constexpr mean_field::quadrature::Query query = DisplacementField::make_query<
|
constexpr mean_field::quadrature::Query query =
|
||||||
mean_field::field::Displacement::Form::CentrifugalForce>(
|
DisplacementField::make_query<mean_field::field::Displacement::Form::CentrifugalForce>(
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder, std::array<int, 1>{1},
|
||||||
geometryWeightOrder, std::array<int, 1>{1},
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
);
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
|
||||||
/* density: 2, displacement test: 3, position: 1, geometry: 4 */
|
/* density: 2, displacement test: 3, position: 1, geometry: 4 */
|
||||||
constexpr int expectedBaseOrder = 2 + 3 + 1 + 4;
|
constexpr int expectedBaseOrder = 2 + 3 + 1 + 4;
|
||||||
@@ -318,33 +308,33 @@ TEST_CASE(
|
|||||||
STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
|
STATIC_REQUIRE(*query.base_order == expectedBaseOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Rotational Displacement Force Uses Negative Rotation-Potential "
|
TEST_CASE(
|
||||||
|
"Rotational Displacement Force Uses Negative Rotation-Potential "
|
||||||
"Gradient And Excludes Vacuum",
|
"Gradient And Excludes Vacuum",
|
||||||
tags::rotation_kernel_accuracy) {
|
tags::rotation_kernel_accuracy
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
REQUIRE(f.okay());
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
const mfem::Vector density =
|
const mfem::Vector density = rotational_displacement_force_test_utils::make_density(f, 0.31);
|
||||||
rotational_displacement_force_test_utils::make_density(f, 0.31);
|
|
||||||
|
|
||||||
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
mfem::Vector displacement(f.displacementFes->GetTrueVSize());
|
||||||
displacement = 0.0;
|
displacement = 0.0;
|
||||||
|
|
||||||
const mean_field::physics::RigidRotation rotation =
|
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation();
|
||||||
rotational_displacement_force_test_utils::make_rotation();
|
|
||||||
|
|
||||||
mfem::Vector residual;
|
mfem::Vector residual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, rotation, density, displacement, residual);
|
f, *f.domainMapperStateless, rotation, density, displacement, residual
|
||||||
|
);
|
||||||
|
|
||||||
mfem::ParGridFunction gradientTestField(f.displacementFes.get());
|
mfem::ParGridFunction gradientTestField(f.displacementFes.get());
|
||||||
|
|
||||||
auto gradientFunction = [&rotation](const mfem::Vector &position,
|
auto gradientFunction = [&rotation](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
mfem::Vector &value) {
|
|
||||||
rotation.potential_gradient(position, value);
|
rotation.potential_gradient(position, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -356,73 +346,63 @@ TEST_CASE("Rotational Displacement Force Uses Negative Rotation-Potential "
|
|||||||
gradientTestField.GetTrueDofs(gradientTestDirection);
|
gradientTestField.GetTrueDofs(gradientTestDirection);
|
||||||
|
|
||||||
const double signedWork =
|
const double signedWork =
|
||||||
rotational_displacement_force_test_utils::global_dot(
|
rotational_displacement_force_test_utils::global_dot(residual, gradientTestDirection, f.mesh->GetComm());
|
||||||
residual, gradientTestDirection, f.mesh->GetComm());
|
|
||||||
|
|
||||||
INFO("Rotation-force work against grad(Psi) = " << signedWork);
|
INFO("Rotation-force work against grad(Psi) = " << signedWork);
|
||||||
CHECK(signedWork < 0.0);
|
CHECK(signedWork < 0.0);
|
||||||
|
|
||||||
const mfem::Vector vacuumDensity =
|
const mfem::Vector vacuumDensity = rotational_displacement_force_test_utils::make_vacuum_only_density(f);
|
||||||
rotational_displacement_force_test_utils::make_vacuum_only_density(f);
|
|
||||||
|
|
||||||
mfem::Vector vacuumResidual;
|
mfem::Vector vacuumResidual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, rotation, vacuumDensity, displacement,
|
f, *f.domainMapperStateless, rotation, vacuumDensity, displacement, vacuumResidual
|
||||||
vacuumResidual);
|
);
|
||||||
|
|
||||||
CHECK(rotational_displacement_force_test_utils::global_norm(
|
CHECK(rotational_displacement_force_test_utils::global_norm(vacuumResidual, f.mesh->GetComm()) == 0.0);
|
||||||
vacuumResidual, f.mesh->GetComm()) == 0.0);
|
|
||||||
|
|
||||||
mfem::Vector zeroAngularVelocity(3);
|
mfem::Vector zeroAngularVelocity(3);
|
||||||
mfem::Vector zeroCenter(3);
|
mfem::Vector zeroCenter(3);
|
||||||
zeroAngularVelocity = 0.0;
|
zeroAngularVelocity = 0.0;
|
||||||
zeroCenter = 0.0;
|
zeroCenter = 0.0;
|
||||||
|
|
||||||
const mean_field::physics::RigidRotation zeroRotation(zeroAngularVelocity,
|
const mean_field::physics::RigidRotation zeroRotation(zeroAngularVelocity, zeroCenter);
|
||||||
zeroCenter);
|
|
||||||
|
|
||||||
mfem::Vector zeroRotationResidual;
|
mfem::Vector zeroRotationResidual;
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, zeroRotation, density, displacement,
|
f, *f.domainMapperStateless, zeroRotation, density, displacement, zeroRotationResidual
|
||||||
zeroRotationResidual);
|
);
|
||||||
|
|
||||||
CHECK(rotational_displacement_force_test_utils::global_norm(
|
CHECK(rotational_displacement_force_test_utils::global_norm(zeroRotationResidual, f.mesh->GetComm()) == 0.0);
|
||||||
zeroRotationResidual, f.mesh->GetComm()) == 0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Prepared Rotational Displacement Force Reprepares Selectively",
|
TEST_CASE(
|
||||||
tags::rotation_prepared) {
|
"Prepared Rotational Displacement Force Reprepares Selectively",
|
||||||
|
tags::rotation_prepared
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
REQUIRE(f.okay());
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
mfem::Vector densityTrue =
|
mfem::Vector densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.37);
|
||||||
rotational_displacement_force_test_utils::make_density(f, 0.37);
|
|
||||||
|
|
||||||
const mfem::Vector displacementTrue =
|
const mfem::Vector displacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.53);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.53);
|
|
||||||
|
|
||||||
mean_field::physics::RigidRotation rotation =
|
mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.81);
|
||||||
rotational_displacement_force_test_utils::make_rotation(0.81);
|
|
||||||
|
|
||||||
auto dependencies =
|
auto dependencies = rotational_displacement_force_test_utils::make_dependencies();
|
||||||
rotational_displacement_force_test_utils::make_dependencies();
|
|
||||||
|
|
||||||
mean_field::operators::PreparedRotationalDisplacementForceOperator
|
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless);
|
||||||
preparedOperator(f, *f.domainMapperStateless);
|
|
||||||
|
|
||||||
const auto &context = preparedOperator.GetContext();
|
const auto &context = preparedOperator.GetContext();
|
||||||
mfem::Vector density = context.GetDensityMap().gather(densityTrue);
|
mfem::Vector density = context.GetDensityMap().gather(densityTrue);
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacement = context.GetDisplacementMap().gather(displacementTrue);
|
||||||
context.GetDisplacementMap().gather(displacementTrue);
|
|
||||||
|
|
||||||
const auto initialReport = preparedOperator.Prepare(
|
const auto initialReport =
|
||||||
{.density = density, .displacement = displacement}, dependencies,
|
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
|
||||||
rotation);
|
|
||||||
|
|
||||||
REQUIRE(initialReport.DidAnyWork());
|
REQUIRE(initialReport.DidAnyWork());
|
||||||
REQUIRE(initialReport.updatedRotation);
|
REQUIRE(initialReport.updatedRotation);
|
||||||
@@ -435,19 +415,18 @@ TEST_CASE("Prepared Rotational Displacement Force Reprepares Selectively",
|
|||||||
preparedOperator.BuildResidual(preparedResidual);
|
preparedOperator.BuildResidual(preparedResidual);
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
|
||||||
f, *f.domainMapperStateless, rotation, densityTrue, displacementTrue,
|
f, *f.domainMapperStateless, rotation, densityTrue, displacementTrue, kernelResidual
|
||||||
kernelResidual);
|
);
|
||||||
|
|
||||||
const mfem::Vector kernelResidualReduced =
|
const mfem::Vector kernelResidualReduced = context.GetDisplacementMap().gather(kernelResidual);
|
||||||
context.GetDisplacementMap().gather(kernelResidual);
|
|
||||||
|
|
||||||
CHECK(rotational_displacement_force_test_utils::relative_difference(
|
CHECK(
|
||||||
preparedResidual, kernelResidualReduced, f.mesh->GetComm()) <
|
rotational_displacement_force_test_utils::relative_difference(
|
||||||
2.0e-12);
|
preparedResidual, kernelResidualReduced, f.mesh->GetComm()
|
||||||
|
) < 2.0e-12
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_FALSE(preparedOperator
|
CHECK_FALSE(preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation)
|
||||||
.Prepare({.density = density, .displacement = displacement},
|
|
||||||
dependencies, rotation)
|
|
||||||
.DidAnyWork());
|
.DidAnyWork());
|
||||||
|
|
||||||
densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.79);
|
densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.79);
|
||||||
@@ -455,9 +434,8 @@ TEST_CASE("Prepared Rotational Displacement Force Reprepares Selectively",
|
|||||||
|
|
||||||
++dependencies.density.revision;
|
++dependencies.density.revision;
|
||||||
|
|
||||||
const auto densityReport = preparedOperator.Prepare(
|
const auto densityReport =
|
||||||
{.density = density, .displacement = displacement}, dependencies,
|
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
|
||||||
rotation);
|
|
||||||
|
|
||||||
CHECK(densityReport.preparedResidual);
|
CHECK(densityReport.preparedResidual);
|
||||||
CHECK_FALSE(densityReport.updatedRotation);
|
CHECK_FALSE(densityReport.updatedRotation);
|
||||||
@@ -466,9 +444,8 @@ TEST_CASE("Prepared Rotational Displacement Force Reprepares Selectively",
|
|||||||
|
|
||||||
++dependencies.rotation.revision;
|
++dependencies.rotation.revision;
|
||||||
|
|
||||||
const auto rotationReport = preparedOperator.Prepare(
|
const auto rotationReport =
|
||||||
{.density = density, .displacement = displacement}, dependencies,
|
preparedOperator.Prepare({.density = density, .displacement = displacement}, dependencies, rotation);
|
||||||
rotation);
|
|
||||||
|
|
||||||
CHECK(rotationReport.updatedRotation);
|
CHECK(rotationReport.updatedRotation);
|
||||||
CHECK(rotationReport.preparedResidual);
|
CHECK(rotationReport.preparedResidual);
|
||||||
@@ -476,45 +453,40 @@ TEST_CASE("Prepared Rotational Displacement Force Reprepares Selectively",
|
|||||||
CHECK(preparedOperator.GetResidualApplicationCount() == 1);
|
CHECK(preparedOperator.GetResidualApplicationCount() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Rotational Displacement Force Jacobian Matches Both Columns And "
|
TEST_CASE(
|
||||||
|
"Rotational Displacement Force Jacobian Matches Both Columns And "
|
||||||
"Centered Differences",
|
"Centered Differences",
|
||||||
tags::rotation_prepared_jacobian_accuracy) {
|
tags::rotation_prepared_jacobian_accuracy
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
REQUIRE(f.okay());
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
const mfem::Vector densityTrue =
|
const mfem::Vector densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.43);
|
||||||
rotational_displacement_force_test_utils::make_density(f, 0.43);
|
|
||||||
|
|
||||||
const mfem::Vector densityDirectionTrue =
|
const mfem::Vector densityDirectionTrue = rotational_displacement_force_test_utils::make_density_direction(f, 0.59);
|
||||||
rotational_displacement_force_test_utils::make_density_direction(f, 0.59);
|
|
||||||
|
|
||||||
const mfem::Vector displacementTrue =
|
const mfem::Vector displacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.61);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.61);
|
|
||||||
|
|
||||||
const mfem::Vector displacementDirectionTrue =
|
const mfem::Vector displacementDirectionTrue =
|
||||||
rotational_displacement_force_test_utils::make_displacement_direction(f);
|
rotational_displacement_force_test_utils::make_displacement_direction(f);
|
||||||
|
|
||||||
const mean_field::physics::RigidRotation rotation =
|
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.93);
|
||||||
rotational_displacement_force_test_utils::make_rotation(0.93);
|
|
||||||
|
|
||||||
mean_field::operators::PreparedRotationalDisplacementForceOperator
|
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless);
|
||||||
preparedOperator(f, *f.domainMapperStateless);
|
|
||||||
|
|
||||||
const auto &context = preparedOperator.GetContext();
|
const auto &context = preparedOperator.GetContext();
|
||||||
const mfem::Vector density = context.GetDensityMap().gather(densityTrue);
|
const mfem::Vector density = context.GetDensityMap().gather(densityTrue);
|
||||||
const mfem::Vector densityDirection =
|
const mfem::Vector densityDirection = context.GetDensityMap().gather(densityDirectionTrue);
|
||||||
context.GetDensityMap().gather(densityDirectionTrue);
|
const mfem::Vector displacement = context.GetDisplacementMap().gather(displacementTrue);
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacementDirection = context.GetDisplacementMap().gather(displacementDirectionTrue);
|
||||||
context.GetDisplacementMap().gather(displacementTrue);
|
|
||||||
const mfem::Vector displacementDirection =
|
|
||||||
context.GetDisplacementMap().gather(displacementDirectionTrue);
|
|
||||||
|
|
||||||
preparedOperator.Prepare(
|
preparedOperator.Prepare(
|
||||||
{.density = density, .displacement = displacement},
|
{.density = density, .displacement = displacement},
|
||||||
rotational_displacement_force_test_utils::make_dependencies(), rotation);
|
rotational_displacement_force_test_utils::make_dependencies(), rotation
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector densityAction;
|
mfem::Vector densityAction;
|
||||||
mfem::Vector displacementAction;
|
mfem::Vector displacementAction;
|
||||||
@@ -522,17 +494,18 @@ TEST_CASE("Rotational Displacement Force Jacobian Matches Both Columns And "
|
|||||||
|
|
||||||
preparedOperator.ApplyDensityJacobianAction(densityDirection, densityAction);
|
preparedOperator.ApplyDensityJacobianAction(densityDirection, densityAction);
|
||||||
|
|
||||||
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection,
|
preparedOperator.ApplyDisplacementJacobianAction(displacementDirection, displacementAction);
|
||||||
displacementAction);
|
|
||||||
|
|
||||||
preparedOperator.ApplyCompleteJacobianAction(
|
preparedOperator.ApplyCompleteJacobianAction(densityDirection, displacementDirection, completeAction);
|
||||||
densityDirection, displacementDirection, completeAction);
|
|
||||||
|
|
||||||
mfem::Vector summedColumns(densityAction);
|
mfem::Vector summedColumns(densityAction);
|
||||||
summedColumns += displacementAction;
|
summedColumns += displacementAction;
|
||||||
|
|
||||||
CHECK(rotational_displacement_force_test_utils::relative_difference(
|
CHECK(
|
||||||
completeAction, summedColumns, f.mesh->GetComm()) < 2.0e-12);
|
rotational_displacement_force_test_utils::relative_difference(
|
||||||
|
completeAction, summedColumns, f.mesh->GetComm()
|
||||||
|
) < 2.0e-12
|
||||||
|
);
|
||||||
|
|
||||||
mfem::Vector zeroDensityTrue(densityDirectionTrue.Size());
|
mfem::Vector zeroDensityTrue(densityDirectionTrue.Size());
|
||||||
mfem::Vector zeroDisplacementTrue(displacementDirectionTrue.Size());
|
mfem::Vector zeroDisplacementTrue(displacementDirectionTrue.Size());
|
||||||
@@ -541,39 +514,33 @@ TEST_CASE("Rotational Displacement Force Jacobian Matches Both Columns And "
|
|||||||
|
|
||||||
constexpr double step = 1.0e-5;
|
constexpr double step = 1.0e-5;
|
||||||
|
|
||||||
const mfem::Vector densityDifferenceTrue =
|
const mfem::Vector densityDifferenceTrue = rotational_displacement_force_test_utils::centered_difference(
|
||||||
rotational_displacement_force_test_utils::centered_difference(
|
f, rotation, densityTrue, densityDirectionTrue, displacementTrue, zeroDisplacementTrue, step
|
||||||
f, rotation, densityTrue, densityDirectionTrue, displacementTrue,
|
);
|
||||||
zeroDisplacementTrue, step);
|
|
||||||
|
|
||||||
const mfem::Vector displacementDifferenceTrue =
|
const mfem::Vector displacementDifferenceTrue = rotational_displacement_force_test_utils::centered_difference(
|
||||||
rotational_displacement_force_test_utils::centered_difference(
|
f, rotation, densityTrue, zeroDensityTrue, displacementTrue, displacementDirectionTrue, step
|
||||||
f, rotation, densityTrue, zeroDensityTrue, displacementTrue,
|
);
|
||||||
displacementDirectionTrue, step);
|
|
||||||
|
|
||||||
const mfem::Vector completeDifferenceTrue =
|
const mfem::Vector completeDifferenceTrue = rotational_displacement_force_test_utils::centered_difference(
|
||||||
rotational_displacement_force_test_utils::centered_difference(
|
f, rotation, densityTrue, densityDirectionTrue, displacementTrue, displacementDirectionTrue, step
|
||||||
f, rotation, densityTrue, densityDirectionTrue, displacementTrue,
|
);
|
||||||
displacementDirectionTrue, step);
|
|
||||||
|
|
||||||
const mfem::Vector densityDifference =
|
const mfem::Vector densityDifference = context.GetDisplacementMap().gather(densityDifferenceTrue);
|
||||||
context.GetDisplacementMap().gather(densityDifferenceTrue);
|
const mfem::Vector displacementDifference = context.GetDisplacementMap().gather(displacementDifferenceTrue);
|
||||||
const mfem::Vector displacementDifference =
|
const mfem::Vector completeDifference = context.GetDisplacementMap().gather(completeDifferenceTrue);
|
||||||
context.GetDisplacementMap().gather(displacementDifferenceTrue);
|
|
||||||
const mfem::Vector completeDifference =
|
|
||||||
context.GetDisplacementMap().gather(completeDifferenceTrue);
|
|
||||||
|
|
||||||
const double densityError =
|
const double densityError = rotational_displacement_force_test_utils::relative_difference(
|
||||||
rotational_displacement_force_test_utils::relative_difference(
|
densityAction, densityDifference, f.mesh->GetComm()
|
||||||
densityAction, densityDifference, f.mesh->GetComm());
|
);
|
||||||
|
|
||||||
const double displacementError =
|
const double displacementError = rotational_displacement_force_test_utils::relative_difference(
|
||||||
rotational_displacement_force_test_utils::relative_difference(
|
displacementAction, displacementDifference, f.mesh->GetComm()
|
||||||
displacementAction, displacementDifference, f.mesh->GetComm());
|
);
|
||||||
|
|
||||||
const double completeError =
|
const double completeError = rotational_displacement_force_test_utils::relative_difference(
|
||||||
rotational_displacement_force_test_utils::relative_difference(
|
completeAction, completeDifference, f.mesh->GetComm()
|
||||||
completeAction, completeDifference, f.mesh->GetComm());
|
);
|
||||||
|
|
||||||
INFO("Density-column centered-difference error = " << densityError);
|
INFO("Density-column centered-difference error = " << densityError);
|
||||||
INFO("Displacement-column centered-difference error = " << displacementError);
|
INFO("Displacement-column centered-difference error = " << displacementError);
|
||||||
@@ -584,109 +551,95 @@ TEST_CASE("Rotational Displacement Force Jacobian Matches Both Columns And "
|
|||||||
CHECK(completeError < 4.0e-8);
|
CHECK(completeError < 4.0e-8);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Prepared Rotational Displacement Force MFEM Adapter Routes Only R-d",
|
TEST_CASE(
|
||||||
tags::rotation_prepared_unit) {
|
"Prepared Rotational Displacement Force MFEM Adapter Routes Only R-d",
|
||||||
|
tags::rotation_prepared_unit
|
||||||
|
) {
|
||||||
mean_field::utils::Args args = test_utils::setup_args();
|
mean_field::utils::Args args = test_utils::setup_args();
|
||||||
|
|
||||||
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
|
||||||
|
|
||||||
REQUIRE(f.okay());
|
REQUIRE(f.okay());
|
||||||
|
|
||||||
const mfem::Vector densityTrue =
|
const mfem::Vector densityTrue = rotational_displacement_force_test_utils::make_density(f, 0.47);
|
||||||
rotational_displacement_force_test_utils::make_density(f, 0.47);
|
|
||||||
|
|
||||||
const mfem::Vector densityDirectionTrue =
|
const mfem::Vector densityDirectionTrue = rotational_displacement_force_test_utils::make_density_direction(f, 0.63);
|
||||||
rotational_displacement_force_test_utils::make_density_direction(f, 0.63);
|
|
||||||
|
|
||||||
const mfem::Vector displacementTrue =
|
const mfem::Vector displacementTrue = gravity_prepared_test_utils::make_displacement(f, 0.57);
|
||||||
gravity_prepared_test_utils::make_displacement(f, 0.57);
|
|
||||||
|
|
||||||
const mfem::Vector displacementDirectionTrue =
|
const mfem::Vector displacementDirectionTrue =
|
||||||
rotational_displacement_force_test_utils::make_displacement_direction(f);
|
rotational_displacement_force_test_utils::make_displacement_direction(f);
|
||||||
|
|
||||||
const mean_field::physics::RigidRotation rotation =
|
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.87);
|
||||||
rotational_displacement_force_test_utils::make_rotation(0.87);
|
|
||||||
|
|
||||||
mean_field::operators::PreparedRotationalDisplacementForceOperator
|
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless);
|
||||||
preparedOperator(f, *f.domainMapperStateless);
|
|
||||||
|
|
||||||
const auto &context = preparedOperator.GetContext();
|
const auto &context = preparedOperator.GetContext();
|
||||||
const mfem::Vector density = context.GetDensityMap().gather(densityTrue);
|
const mfem::Vector density = context.GetDensityMap().gather(densityTrue);
|
||||||
const mfem::Vector densityDirection =
|
const mfem::Vector densityDirection = context.GetDensityMap().gather(densityDirectionTrue);
|
||||||
context.GetDensityMap().gather(densityDirectionTrue);
|
const mfem::Vector displacement = context.GetDisplacementMap().gather(displacementTrue);
|
||||||
const mfem::Vector displacement =
|
const mfem::Vector displacementDirection = context.GetDisplacementMap().gather(displacementDirectionTrue);
|
||||||
context.GetDisplacementMap().gather(displacementTrue);
|
|
||||||
const mfem::Vector displacementDirection =
|
|
||||||
context.GetDisplacementMap().gather(displacementDirectionTrue);
|
|
||||||
|
|
||||||
preparedOperator.Prepare(
|
preparedOperator.Prepare(
|
||||||
{.density = density, .displacement = displacement},
|
{.density = density, .displacement = displacement},
|
||||||
rotational_displacement_force_test_utils::make_dependencies(), rotation);
|
rotational_displacement_force_test_utils::make_dependencies(), rotation
|
||||||
|
);
|
||||||
|
|
||||||
const auto layout = rotational_displacement_force_test_utils::make_layout(f);
|
const auto layout = rotational_displacement_force_test_utils::make_layout(f);
|
||||||
|
|
||||||
mean_field::operators::PreparedRotationalDisplacementForceJacobianOperator
|
mean_field::operators::PreparedRotationalDisplacementForceJacobianOperator adapter(layout, preparedOperator);
|
||||||
adapter(layout, preparedOperator);
|
|
||||||
|
|
||||||
mfem::BlockVector direction(layout.value_offsets());
|
mfem::BlockVector direction(layout.value_offsets());
|
||||||
direction = 0.0;
|
direction = 0.0;
|
||||||
|
|
||||||
direction.GetBlock(rotational_displacement_force_test_utils::densityValue) =
|
direction.GetBlock(rotational_displacement_force_test_utils::densityValue) = densityDirection;
|
||||||
densityDirection;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(rotational_displacement_force_test_utils::displacementValue) = displacementDirection;
|
||||||
rotational_displacement_force_test_utils::displacementValue) =
|
|
||||||
displacementDirection;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(rotational_displacement_force_test_utils::gravityGradientValue) = 0.23;
|
||||||
rotational_displacement_force_test_utils::gravityGradientValue) = 0.23;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(rotational_displacement_force_test_utils::gravityPotentialValue) = -0.31;
|
||||||
rotational_displacement_force_test_utils::gravityPotentialValue) = -0.31;
|
|
||||||
|
|
||||||
direction.GetBlock(rotational_displacement_force_test_utils::enthalpyValue) =
|
direction.GetBlock(rotational_displacement_force_test_utils::enthalpyValue) = 0.37;
|
||||||
0.37;
|
|
||||||
|
|
||||||
direction.GetBlock(
|
direction.GetBlock(rotational_displacement_force_test_utils::barotropicConstantValue) = -0.41;
|
||||||
rotational_displacement_force_test_utils::barotropicConstantValue) =
|
|
||||||
-0.41;
|
|
||||||
|
|
||||||
mfem::Vector action;
|
mfem::Vector action;
|
||||||
adapter.Mult(direction, action);
|
adapter.Mult(direction, action);
|
||||||
|
|
||||||
mfem::Vector expectedDisplacementAction;
|
mfem::Vector expectedDisplacementAction;
|
||||||
|
|
||||||
preparedOperator.ApplyCompleteJacobianAction(
|
preparedOperator.ApplyCompleteJacobianAction(densityDirection, displacementDirection, expectedDisplacementAction);
|
||||||
densityDirection, displacementDirection, expectedDisplacementAction);
|
|
||||||
|
|
||||||
const mfem::Vector actualDisplacementAction =
|
const mfem::Vector actualDisplacementAction = rotational_displacement_force_test_utils::copy_residual_block(
|
||||||
rotational_displacement_force_test_utils::copy_residual_block(
|
action, layout, rotational_displacement_force_test_utils::displacementResidual
|
||||||
action, layout,
|
);
|
||||||
rotational_displacement_force_test_utils::displacementResidual);
|
|
||||||
|
|
||||||
CHECK(rotational_displacement_force_test_utils::relative_difference(
|
CHECK(
|
||||||
actualDisplacementAction, expectedDisplacementAction,
|
rotational_displacement_force_test_utils::relative_difference(
|
||||||
f.mesh->GetComm()) < 2.0e-12);
|
actualDisplacementAction, expectedDisplacementAction, f.mesh->GetComm()
|
||||||
|
) < 2.0e-12
|
||||||
|
);
|
||||||
|
|
||||||
const std::array<mfem::Vector, 5> zeroRows{
|
const std::array<mfem::Vector, 5> zeroRows{
|
||||||
rotational_displacement_force_test_utils::copy_residual_block(
|
rotational_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, rotational_displacement_force_test_utils::gravityGradientResidual
|
||||||
rotational_displacement_force_test_utils::gravityGradientResidual),
|
),
|
||||||
rotational_displacement_force_test_utils::copy_residual_block(
|
rotational_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, rotational_displacement_force_test_utils::gravityPotentialResidual
|
||||||
rotational_displacement_force_test_utils::gravityPotentialResidual),
|
),
|
||||||
rotational_displacement_force_test_utils::copy_residual_block(
|
rotational_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, rotational_displacement_force_test_utils::densityResidual
|
||||||
rotational_displacement_force_test_utils::densityResidual),
|
),
|
||||||
rotational_displacement_force_test_utils::copy_residual_block(
|
rotational_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, rotational_displacement_force_test_utils::enthalpyResidual
|
||||||
rotational_displacement_force_test_utils::enthalpyResidual),
|
),
|
||||||
rotational_displacement_force_test_utils::copy_residual_block(
|
rotational_displacement_force_test_utils::copy_residual_block(
|
||||||
action, layout,
|
action, layout, rotational_displacement_force_test_utils::massResidual
|
||||||
rotational_displacement_force_test_utils::massResidual)};
|
)
|
||||||
|
};
|
||||||
|
|
||||||
for (const mfem::Vector &row : zeroRows) {
|
for (const mfem::Vector &row : zeroRows) {
|
||||||
CHECK(rotational_displacement_force_test_utils::global_norm(
|
CHECK(rotational_displacement_force_test_utils::global_norm(row, f.mesh->GetComm()) == 0.0);
|
||||||
row, f.mesh->GetComm()) == 0.0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,115 +8,202 @@
|
|||||||
import mean_field;
|
import mean_field;
|
||||||
import test_helpers;
|
import test_helpers;
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Satisfies Its Analytic Identities",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_unit) {
|
"Polytropic EOS Satisfies Its Analytic Identities",
|
||||||
|
tags::barotrope_eos_unit
|
||||||
|
) {
|
||||||
|
using namespace mean_field::eos;
|
||||||
|
|
||||||
constexpr double polytropic_index = 3.0;
|
constexpr double polytropic_index = 3.0;
|
||||||
constexpr double polytropic_constant = 1.5;
|
constexpr double polytropic_constant = 1.5;
|
||||||
|
|
||||||
const mean_field::eos::Polytrope barotrope(polytropic_index,
|
const Polytrope barotrope(polytropic_index, polytropic_constant);
|
||||||
polytropic_constant);
|
|
||||||
|
|
||||||
const std::array<double, 5> densities{1.0e-6, 1.0e-3, 0.1, 0.7, 2.0};
|
using densityV = DensityValue;
|
||||||
|
using pressureV = PressureValue;
|
||||||
|
using enthalpyV = SpecificEnthalpyValue;
|
||||||
|
|
||||||
for (const double density : densities) {
|
constexpr std::array<densityV, 5> densities{
|
||||||
const double pressure = barotrope.pressure_from_density(density);
|
densityV{1.0e-6}, densityV{1.0e-3}, densityV{0.1}, densityV{0.7}, densityV{2.0}
|
||||||
|
};
|
||||||
|
|
||||||
const double enthalpy = barotrope.enthalpy_from_density(density);
|
for (const densityV density : densities) {
|
||||||
|
const pressureV pressure = evaluate<quantity::Pressure>(barotrope, density);
|
||||||
|
|
||||||
const double reconstructed_density =
|
const enthalpyV enthalpy = evaluate<quantity::SpecificEnthalpy>(barotrope, density);
|
||||||
barotrope.density_from_enthalpy(enthalpy);
|
const densityV reconstructed_density = evaluate<quantity::Density>(barotrope, enthalpy);
|
||||||
|
|
||||||
const double reconstructed_pressure =
|
const pressureV reconstructed_pressure = evaluate<quantity::Pressure>(barotrope, enthalpy);
|
||||||
barotrope.pressure_from_enthalpy(enthalpy);
|
|
||||||
|
|
||||||
const double reconstructed_enthalpy =
|
const enthalpyV reconstructed_enthalpy = evaluate<quantity::SpecificEnthalpy>(barotrope, pressure);
|
||||||
barotrope.enthalpy_from_pressure(pressure);
|
|
||||||
|
|
||||||
CHECK_THAT(reconstructed_density,
|
CHECK_THAT(reconstructed_density.value(), Catch::Matchers::WithinRel(density.value(), 2.0e-14));
|
||||||
Catch::Matchers::WithinRel(density, 2.0e-14));
|
|
||||||
|
|
||||||
CHECK_THAT(reconstructed_pressure,
|
CHECK_THAT(reconstructed_pressure.value(), Catch::Matchers::WithinRel(pressure.value(), 2.0e-14));
|
||||||
Catch::Matchers::WithinRel(pressure, 2.0e-14));
|
|
||||||
|
|
||||||
CHECK_THAT(reconstructed_enthalpy,
|
CHECK_THAT(reconstructed_enthalpy.value(), Catch::Matchers::WithinRel(enthalpy.value(), 2.0e-14));
|
||||||
Catch::Matchers::WithinRel(enthalpy, 2.0e-14));
|
|
||||||
|
|
||||||
CHECK_THAT(pressure,
|
|
||||||
Catch::Matchers::WithinRel(
|
|
||||||
density * enthalpy / (polytropic_index + 1.0), 2.0e-14));
|
|
||||||
|
|
||||||
CHECK_THAT(barotrope.pressure_derivative_from_enthalpy(enthalpy),
|
|
||||||
Catch::Matchers::WithinRel(density, 2.0e-14));
|
|
||||||
|
|
||||||
CHECK_THAT(
|
CHECK_THAT(
|
||||||
barotrope.pressure_derivative_from_density(density),
|
pressure.value(),
|
||||||
Catch::Matchers::WithinRel(enthalpy / polytropic_index, 2.0e-14));
|
Catch::Matchers::WithinRel(density.value() * enthalpy.value() / (polytropic_index + 1.0), 2.0e-14)
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK_THAT(
|
||||||
|
(mean_field::eos::partialDerivative<
|
||||||
|
mean_field::eos::quantity::Pressure, mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{enthalpy}
|
||||||
|
)
|
||||||
|
.value()),
|
||||||
|
Catch::Matchers::WithinRel(density.value(), 2.0e-14)
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK_THAT(
|
||||||
|
(mean_field::eos::partialDerivative<
|
||||||
|
mean_field::eos::quantity::Pressure, mean_field::eos::quantity::Density>(
|
||||||
|
barotrope, mean_field::eos::DensityValue{density}
|
||||||
|
)
|
||||||
|
.value()),
|
||||||
|
Catch::Matchers::WithinRel(enthalpy.value() / polytropic_index, 2.0e-14)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Derivatives Match Centered Differences",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_jacobian) {
|
"Polytropic EOS Derivatives Match Centered Differences",
|
||||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
tags::barotrope_eos_jacobian
|
||||||
|
) {
|
||||||
|
using namespace mean_field::eos;
|
||||||
|
|
||||||
const std::array<double, 4> enthalpies{0.05, 0.2, 0.7, 1.4};
|
const Polytrope barotrope(3.0, 1.5);
|
||||||
|
|
||||||
for (const double enthalpy : enthalpies) {
|
using densityV = DensityValue;
|
||||||
const double step = 1.0e-6 * std::max(1.0, enthalpy);
|
using pressureV = PressureValue;
|
||||||
|
using enthalpyV = SpecificEnthalpyValue;
|
||||||
|
|
||||||
const double density_difference =
|
constexpr std::array<enthalpyV, 4> enthalpies{enthalpyV{0.05}, enthalpyV{0.2}, enthalpyV{0.7}, enthalpyV{1.4}};
|
||||||
(barotrope.density_from_enthalpy(enthalpy + step) -
|
|
||||||
barotrope.density_from_enthalpy(enthalpy - step)) /
|
|
||||||
(2.0 * step);
|
|
||||||
|
|
||||||
const double pressure_difference =
|
for (const enthalpyV enthalpy : enthalpies) {
|
||||||
(barotrope.pressure_from_enthalpy(enthalpy + step) -
|
const enthalpyV step = enthalpyV{1.0e-6} * std::max(1.0, enthalpy.value());
|
||||||
barotrope.pressure_from_enthalpy(enthalpy - step)) /
|
|
||||||
(2.0 * step);
|
const densityV density_difference = (evaluate<quantity::Density>(barotrope, enthalpy + step) -
|
||||||
|
evaluate<quantity::Density>(barotrope, enthalpy - step)) /
|
||||||
|
(2.0 * step.value());
|
||||||
|
|
||||||
|
const pressureV pressure_difference = (evaluate<quantity::Pressure>(barotrope, enthalpy + step) -
|
||||||
|
evaluate<quantity::Pressure>(barotrope, enthalpy - step)) /
|
||||||
|
(2.0 * step.value());
|
||||||
|
|
||||||
CHECK_THAT(
|
CHECK_THAT(
|
||||||
density_difference,
|
density_difference.value(),
|
||||||
Catch::Matchers::WithinRel(
|
Catch::Matchers::WithinRel(
|
||||||
barotrope.density_derivative_from_enthalpy(enthalpy), 5.0e-10));
|
mean_field::eos::partialDerivative<
|
||||||
|
mean_field::eos::quantity::Density, mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, enthalpy
|
||||||
|
)
|
||||||
|
.value(),
|
||||||
|
5.0e-10
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_THAT(
|
CHECK_THAT(
|
||||||
pressure_difference,
|
pressure_difference.value(),
|
||||||
Catch::Matchers::WithinRel(
|
Catch::Matchers::WithinRel(
|
||||||
barotrope.pressure_derivative_from_enthalpy(enthalpy), 5.0e-10));
|
mean_field::eos::partialDerivative<
|
||||||
|
mean_field::eos::quantity::Pressure, mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, enthalpy
|
||||||
|
)
|
||||||
|
.value(),
|
||||||
|
5.0e-10
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Has An Exact Zero Density Surface",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_unit) {
|
"Polytropic EOS Has An Exact Zero Density Surface",
|
||||||
|
tags::barotrope_eos_unit
|
||||||
|
) {
|
||||||
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
|
||||||
|
|
||||||
CHECK(barotrope.density_from_enthalpy(-1.0) == 0.0);
|
CHECK(
|
||||||
CHECK(barotrope.density_from_enthalpy(0.0) == 0.0);
|
mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{-1.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{0.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(barotrope.pressure_from_enthalpy(-1.0) == 0.0);
|
CHECK(
|
||||||
CHECK(barotrope.pressure_from_enthalpy(0.0) == 0.0);
|
mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{-1.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{0.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(barotrope.density_derivative_from_enthalpy(-1.0) == 0.0);
|
CHECK(
|
||||||
|
(mean_field::eos::partialDerivative<
|
||||||
|
mean_field::eos::quantity::Density, mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{-1.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(barotrope.density_derivative_from_enthalpy(0.0) == 0.0);
|
CHECK(
|
||||||
|
(mean_field::eos::partialDerivative<
|
||||||
|
mean_field::eos::quantity::Density, mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{0.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(barotrope.pressure_derivative_from_enthalpy(0.0) == 0.0);
|
CHECK(
|
||||||
|
(mean_field::eos::partialDerivative<
|
||||||
|
mean_field::eos::quantity::Pressure, mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, mean_field::eos::SpecificEnthalpyValue{0.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Rejects Invalid Material Parameters",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_unit) {
|
"Polytropic EOS Rejects Invalid Material Parameters",
|
||||||
|
tags::barotrope_eos_unit
|
||||||
|
) {
|
||||||
CHECK_THROWS_AS(mean_field::eos::Polytrope(0.5, 1.0), std::invalid_argument);
|
CHECK_THROWS_AS(mean_field::eos::Polytrope(0.5, 1.0), std::invalid_argument);
|
||||||
|
|
||||||
CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, 0.0), std::invalid_argument);
|
CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, 0.0), std::invalid_argument);
|
||||||
|
|
||||||
CHECK_THROWS_AS(
|
CHECK_THROWS_AS(mean_field::eos::Polytrope(std::numeric_limits<double>::infinity(), 1.0), std::invalid_argument);
|
||||||
mean_field::eos::Polytrope(std::numeric_limits<double>::infinity(), 1.0),
|
|
||||||
std::invalid_argument);
|
|
||||||
|
|
||||||
const mean_field::eos::Polytrope barotrope(3.0, 1.0);
|
const mean_field::eos::Polytrope barotrope(3.0, 1.0);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.pressure_from_density(-1.0), std::domain_error);
|
CHECK_THROWS_AS(
|
||||||
|
mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(barotrope, mean_field::eos::DensityValue{-1.0}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.enthalpy_from_density(-1.0), std::domain_error);
|
CHECK_THROWS_AS(
|
||||||
|
mean_field::eos::evaluate<mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, mean_field::eos::DensityValue{-1.0}
|
||||||
|
),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.enthalpy_from_pressure(-1.0), std::domain_error);
|
CHECK_THROWS_AS(
|
||||||
|
mean_field::eos::evaluate<mean_field::eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, mean_field::eos::PressureValue{-1.0}
|
||||||
|
),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,32 +13,39 @@
|
|||||||
import mean_field;
|
import mean_field;
|
||||||
import test_helpers;
|
import test_helpers;
|
||||||
|
|
||||||
namespace polytropic_eos_test_utils {
|
namespace eos = mean_field::eos;
|
||||||
template <typename Function>
|
|
||||||
double centered_derivative(Function &&function, const double position,
|
|
||||||
const double step) {
|
|
||||||
return (function(position + step) - function(position - step)) / (2.0 * step);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Integrand>
|
namespace polytropic_eos_test_utils {
|
||||||
double integrate_cube(const mfem::IntegrationRule &integrationRule,
|
template <typename Function>
|
||||||
Integrand &&integrand) {
|
double centered_derivative(
|
||||||
|
Function &&function,
|
||||||
|
const double position,
|
||||||
|
const double step
|
||||||
|
) {
|
||||||
|
return (function(position + step) - function(position - step)) / (2.0 * step);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Integrand>
|
||||||
|
double integrate_cube(
|
||||||
|
const mfem::IntegrationRule &integrationRule,
|
||||||
|
Integrand &&integrand
|
||||||
|
) {
|
||||||
double integral = 0.0;
|
double integral = 0.0;
|
||||||
|
|
||||||
for (int pointIndex = 0; pointIndex < integrationRule.GetNPoints();
|
for (int pointIndex = 0; pointIndex < integrationRule.GetNPoints(); ++pointIndex) {
|
||||||
++pointIndex) {
|
const mfem::IntegrationPoint &integrationPoint = integrationRule.IntPoint(pointIndex);
|
||||||
const mfem::IntegrationPoint &integrationPoint =
|
|
||||||
integrationRule.IntPoint(pointIndex);
|
|
||||||
|
|
||||||
integral += integrationPoint.weight * integrand(integrationPoint);
|
integral += integrationPoint.weight * integrand(integrationPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
return integral;
|
return integral;
|
||||||
}
|
}
|
||||||
} // namespace polytropic_eos_test_utils
|
} // namespace polytropic_eos_test_utils
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Satisfies Its Thermodynamic Identities",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_unit) {
|
"Polytropic EOS Satisfies Its Thermodynamic Identities",
|
||||||
|
tags::barotrope_eos_unit
|
||||||
|
) {
|
||||||
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
||||||
|
|
||||||
constexpr std::array<double, 4> densities{1.0e-4, 0.02, 0.37, 2.4};
|
constexpr std::array<double, 4> densities{1.0e-4, 0.02, 0.37, 2.4};
|
||||||
@@ -47,11 +54,9 @@ TEST_CASE("Polytropic EOS Satisfies Its Thermodynamic Identities",
|
|||||||
|
|
||||||
for (const double polytropicIndex : polytropicIndices) {
|
for (const double polytropicIndex : polytropicIndices) {
|
||||||
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
||||||
const mean_field::eos::Polytrope barotrope(polytropicIndex,
|
const mean_field::eos::Polytrope barotrope(polytropicIndex, polytropicConstant);
|
||||||
polytropicConstant);
|
|
||||||
|
|
||||||
const double expectedEnthalpyScale =
|
const double expectedEnthalpyScale = (polytropicIndex + 1.0) * polytropicConstant;
|
||||||
(polytropicIndex + 1.0) * polytropicConstant;
|
|
||||||
|
|
||||||
CHECK(barotrope.polytropic_index() == polytropicIndex);
|
CHECK(barotrope.polytropic_index() == polytropicIndex);
|
||||||
|
|
||||||
@@ -62,45 +67,41 @@ TEST_CASE("Polytropic EOS Satisfies Its Thermodynamic Identities",
|
|||||||
for (const double density : densities) {
|
for (const double density : densities) {
|
||||||
CAPTURE(polytropicIndex, polytropicConstant, density);
|
CAPTURE(polytropicIndex, polytropicConstant, density);
|
||||||
|
|
||||||
const double expectedPressure =
|
const double expectedPressure = polytropicConstant * std::pow(density, 1.0 + 1.0 / polytropicIndex);
|
||||||
polytropicConstant * std::pow(density, 1.0 + 1.0 / polytropicIndex);
|
|
||||||
|
|
||||||
const double expectedEnthalpy =
|
const double expectedEnthalpy = expectedEnthalpyScale * std::pow(density, 1.0 / polytropicIndex);
|
||||||
expectedEnthalpyScale * std::pow(density, 1.0 / polytropicIndex);
|
|
||||||
|
|
||||||
const double pressureFromDensity =
|
const double pressureFromDensity =
|
||||||
barotrope.pressure_from_density(density);
|
eos::evaluate<eos::quantity::Pressure>(barotrope, eos::DensityValue{density}).value();
|
||||||
|
|
||||||
const double enthalpyFromDensity =
|
const double enthalpyFromDensity =
|
||||||
barotrope.enthalpy_from_density(density);
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(barotrope, eos::DensityValue{density}).value();
|
||||||
|
|
||||||
const double recoveredDensity =
|
const double recoveredDensity =
|
||||||
barotrope.density_from_enthalpy(enthalpyFromDensity);
|
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{enthalpyFromDensity})
|
||||||
|
.value();
|
||||||
|
|
||||||
const double pressureFromEnthalpy =
|
const double pressureFromEnthalpy =
|
||||||
barotrope.pressure_from_enthalpy(enthalpyFromDensity);
|
eos::evaluate<eos::quantity::Pressure>(barotrope, eos::SpecificEnthalpyValue{enthalpyFromDensity})
|
||||||
|
.value();
|
||||||
|
|
||||||
CHECK_THAT(pressureFromDensity,
|
CHECK_THAT(pressureFromDensity, Catch::Matchers::WithinRel(expectedPressure, 2.0e-13));
|
||||||
Catch::Matchers::WithinRel(expectedPressure, 2.0e-13));
|
|
||||||
|
|
||||||
CHECK_THAT(enthalpyFromDensity,
|
CHECK_THAT(enthalpyFromDensity, Catch::Matchers::WithinRel(expectedEnthalpy, 2.0e-13));
|
||||||
Catch::Matchers::WithinRel(expectedEnthalpy, 2.0e-13));
|
|
||||||
|
|
||||||
CHECK_THAT(recoveredDensity,
|
CHECK_THAT(recoveredDensity, Catch::Matchers::WithinRel(density, 5.0e-13));
|
||||||
Catch::Matchers::WithinRel(density, 5.0e-13));
|
|
||||||
|
|
||||||
CHECK_THAT(pressureFromEnthalpy,
|
CHECK_THAT(pressureFromEnthalpy, Catch::Matchers::WithinRel(expectedPressure, 5.0e-13));
|
||||||
Catch::Matchers::WithinRel(expectedPressure, 5.0e-13));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Polytropic identity:
|
* Polytropic identity:
|
||||||
*
|
*
|
||||||
* P = rho h / (n + 1).
|
* P = rho h / (n + 1).
|
||||||
*/
|
*/
|
||||||
CHECK_THAT(pressureFromEnthalpy,
|
CHECK_THAT(
|
||||||
Catch::Matchers::WithinRel(density * enthalpyFromDensity /
|
pressureFromEnthalpy,
|
||||||
(polytropicIndex + 1.0),
|
Catch::Matchers::WithinRel(density * enthalpyFromDensity / (polytropicIndex + 1.0), 5.0e-13)
|
||||||
5.0e-13));
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Polytropic identity:
|
* Polytropic identity:
|
||||||
@@ -108,11 +109,14 @@ TEST_CASE("Polytropic EOS Satisfies Its Thermodynamic Identities",
|
|||||||
* dP / dh = rho.
|
* dP / dh = rho.
|
||||||
*
|
*
|
||||||
* The implementation should return the same
|
* The implementation should return the same
|
||||||
* value as density_from_enthalpy().
|
* value as the density from specific enthalpy relation.
|
||||||
*/
|
*/
|
||||||
CHECK(
|
CHECK(
|
||||||
barotrope.pressure_derivative_from_enthalpy(enthalpyFromDensity) ==
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
barotrope.density_from_enthalpy(enthalpyFromDensity));
|
barotrope, eos::SpecificEnthalpyValue{enthalpyFromDensity}
|
||||||
|
)
|
||||||
|
.value() == recoveredDensity)
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since
|
* Since
|
||||||
@@ -123,16 +127,22 @@ TEST_CASE("Polytropic EOS Satisfies Its Thermodynamic Identities",
|
|||||||
*
|
*
|
||||||
* dP / d rho = h / n.
|
* dP / d rho = h / n.
|
||||||
*/
|
*/
|
||||||
CHECK_THAT(barotrope.pressure_derivative_from_density(density),
|
CHECK_THAT(
|
||||||
Catch::Matchers::WithinRel(
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(
|
||||||
enthalpyFromDensity / polytropicIndex, 5.0e-13));
|
barotrope, eos::DensityValue{density}
|
||||||
|
)
|
||||||
|
.value()),
|
||||||
|
Catch::Matchers::WithinRel(enthalpyFromDensity / polytropicIndex, 5.0e-13)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Pressure Derivatives Match Centered Differences",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_jacobian) {
|
"Polytropic EOS Pressure Derivatives Match Centered Differences",
|
||||||
|
tags::barotrope_eos_jacobian
|
||||||
|
) {
|
||||||
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
||||||
|
|
||||||
constexpr std::array<double, 3> positiveValues{0.2, 0.73, 1.8};
|
constexpr std::array<double, 3> positiveValues{0.2, 0.73, 1.8};
|
||||||
@@ -140,55 +150,62 @@ TEST_CASE("Polytropic EOS Pressure Derivatives Match Centered Differences",
|
|||||||
constexpr double polytropicConstant = 0.61;
|
constexpr double polytropicConstant = 0.61;
|
||||||
|
|
||||||
for (const double polytropicIndex : polytropicIndices) {
|
for (const double polytropicIndex : polytropicIndices) {
|
||||||
const mean_field::eos::Polytrope barotrope(polytropicIndex,
|
const mean_field::eos::Polytrope barotrope(polytropicIndex, polytropicConstant);
|
||||||
polytropicConstant);
|
|
||||||
|
|
||||||
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
||||||
for (const double enthalpy : positiveValues) {
|
for (const double enthalpy : positiveValues) {
|
||||||
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
|
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
|
||||||
|
|
||||||
const double numericalDerivative =
|
const double numericalDerivative = polytropic_eos_test_utils::centered_derivative(
|
||||||
polytropic_eos_test_utils::centered_derivative(
|
|
||||||
[&barotrope](const double perturbedEnthalpy) {
|
[&barotrope](const double perturbedEnthalpy) {
|
||||||
return barotrope.pressure_from_enthalpy(perturbedEnthalpy);
|
return eos::evaluate<eos::quantity::Pressure>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{perturbedEnthalpy}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
},
|
},
|
||||||
enthalpy, step);
|
enthalpy, step
|
||||||
|
);
|
||||||
|
|
||||||
const double analyticDerivative =
|
const double analyticDerivative =
|
||||||
barotrope.pressure_derivative_from_enthalpy(enthalpy);
|
eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{enthalpy}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative,
|
CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative, analyticDerivative);
|
||||||
analyticDerivative);
|
|
||||||
|
|
||||||
CHECK_THAT(numericalDerivative,
|
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
||||||
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const double density : positiveValues) {
|
for (const double density : positiveValues) {
|
||||||
const double step = 2.0e-6 * std::max(1.0, std::abs(density));
|
const double step = 2.0e-6 * std::max(1.0, std::abs(density));
|
||||||
|
|
||||||
const double numericalDerivative =
|
const double numericalDerivative = polytropic_eos_test_utils::centered_derivative(
|
||||||
polytropic_eos_test_utils::centered_derivative(
|
|
||||||
[&barotrope](const double perturbedDensity) {
|
[&barotrope](const double perturbedDensity) {
|
||||||
return barotrope.pressure_from_density(perturbedDensity);
|
return eos::evaluate<eos::quantity::Pressure>(barotrope, eos::DensityValue{perturbedDensity})
|
||||||
|
.value();
|
||||||
},
|
},
|
||||||
density, step);
|
density, step
|
||||||
|
);
|
||||||
|
|
||||||
const double analyticDerivative =
|
const double analyticDerivative =
|
||||||
barotrope.pressure_derivative_from_density(density);
|
eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(
|
||||||
|
barotrope, eos::DensityValue{density}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
CAPTURE(polytropicIndex, density, step, numericalDerivative,
|
CAPTURE(polytropicIndex, density, step, numericalDerivative, analyticDerivative);
|
||||||
analyticDerivative);
|
|
||||||
|
|
||||||
CHECK_THAT(numericalDerivative,
|
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
||||||
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Density Derivative Matches Centered Differences",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_jacobian) {
|
"Polytropic EOS Density Derivative Matches Centered Differences",
|
||||||
|
tags::barotrope_eos_jacobian
|
||||||
|
) {
|
||||||
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
||||||
|
|
||||||
constexpr std::array<double, 3> enthalpies{0.2, 0.73, 1.8};
|
constexpr std::array<double, 3> enthalpies{0.2, 0.73, 1.8};
|
||||||
@@ -196,72 +213,100 @@ TEST_CASE("Polytropic EOS Density Derivative Matches Centered Differences",
|
|||||||
constexpr double polytropicConstant = 0.61;
|
constexpr double polytropicConstant = 0.61;
|
||||||
|
|
||||||
for (const double polytropicIndex : polytropicIndices) {
|
for (const double polytropicIndex : polytropicIndices) {
|
||||||
const mean_field::eos::Polytrope barotrope(polytropicIndex,
|
const mean_field::eos::Polytrope barotrope(polytropicIndex, polytropicConstant);
|
||||||
polytropicConstant);
|
|
||||||
|
|
||||||
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
||||||
for (const double enthalpy : enthalpies) {
|
for (const double enthalpy : enthalpies) {
|
||||||
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
|
const double step = 2.0e-6 * std::max(1.0, std::abs(enthalpy));
|
||||||
|
|
||||||
const double numericalDerivative =
|
const double numericalDerivative = polytropic_eos_test_utils::centered_derivative(
|
||||||
polytropic_eos_test_utils::centered_derivative(
|
|
||||||
[&barotrope](const double perturbedEnthalpy) {
|
[&barotrope](const double perturbedEnthalpy) {
|
||||||
return barotrope.density_from_enthalpy(perturbedEnthalpy);
|
return eos::evaluate<eos::quantity::Density>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{perturbedEnthalpy}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
},
|
},
|
||||||
enthalpy, step);
|
enthalpy, step
|
||||||
|
);
|
||||||
|
|
||||||
const double analyticDerivative =
|
const double analyticDerivative =
|
||||||
barotrope.density_derivative_from_enthalpy(enthalpy);
|
eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{enthalpy}
|
||||||
|
)
|
||||||
|
.value();
|
||||||
|
|
||||||
CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative,
|
CAPTURE(polytropicIndex, enthalpy, step, numericalDerivative, analyticDerivative);
|
||||||
analyticDerivative);
|
|
||||||
|
|
||||||
CHECK_THAT(numericalDerivative,
|
CHECK_THAT(numericalDerivative, Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
||||||
Catch::Matchers::WithinRel(analyticDerivative, 5.0e-8));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Defines Consistent Surface And Exterior Behavior",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_unit) {
|
"Polytropic EOS Defines Consistent Surface And Exterior Behavior",
|
||||||
|
tags::barotrope_eos_unit
|
||||||
|
) {
|
||||||
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
||||||
|
|
||||||
constexpr double polytropicConstant = 0.47;
|
constexpr double polytropicConstant = 0.47;
|
||||||
constexpr double exteriorEnthalpy = -0.3;
|
constexpr double exteriorEnthalpy = -0.3;
|
||||||
|
|
||||||
for (const double polytropicIndex : polytropicIndices) {
|
for (const double polytropicIndex : polytropicIndices) {
|
||||||
const mean_field::eos::Polytrope barotrope(polytropicIndex,
|
const mean_field::eos::Polytrope barotrope(polytropicIndex, polytropicConstant);
|
||||||
polytropicConstant);
|
|
||||||
|
|
||||||
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
DYNAMIC_SECTION("polytropic index n = " << polytropicIndex) {
|
||||||
/*
|
/*
|
||||||
* Exact surface values.
|
* Exact surface values.
|
||||||
*/
|
*/
|
||||||
CHECK(barotrope.density_from_enthalpy(0.0) == 0.0);
|
CHECK(eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{0.0}).value() == 0.0);
|
||||||
|
|
||||||
CHECK(barotrope.pressure_from_enthalpy(0.0) == 0.0);
|
CHECK(eos::evaluate<eos::quantity::Pressure>(barotrope, eos::SpecificEnthalpyValue{0.0}).value() == 0.0);
|
||||||
|
|
||||||
CHECK(barotrope.pressure_derivative_from_enthalpy(0.0) == 0.0);
|
CHECK(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{0.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(barotrope.pressure_from_density(0.0) == 0.0);
|
CHECK(eos::evaluate<eos::quantity::Pressure>(barotrope, eos::DensityValue{0.0}).value() == 0.0);
|
||||||
|
|
||||||
CHECK(barotrope.enthalpy_from_density(0.0) == 0.0);
|
CHECK(eos::evaluate<eos::quantity::SpecificEnthalpy>(barotrope, eos::DensityValue{0.0}).value() == 0.0);
|
||||||
|
|
||||||
CHECK(barotrope.pressure_derivative_from_density(0.0) == 0.0);
|
CHECK(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(
|
||||||
|
barotrope, eos::DensityValue{0.0}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Positive-part extension into h < 0.
|
* Positive-part extension into h < 0.
|
||||||
*/
|
*/
|
||||||
CHECK(barotrope.density_from_enthalpy(exteriorEnthalpy) == 0.0);
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{exteriorEnthalpy})
|
||||||
|
.value() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(barotrope.pressure_from_enthalpy(exteriorEnthalpy) == 0.0);
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(barotrope, eos::SpecificEnthalpyValue{exteriorEnthalpy})
|
||||||
|
.value() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(barotrope.density_derivative_from_enthalpy(exteriorEnthalpy) ==
|
CHECK(
|
||||||
0.0);
|
(eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{exteriorEnthalpy}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
CHECK(barotrope.pressure_derivative_from_enthalpy(exteriorEnthalpy) ==
|
CHECK(
|
||||||
0.0);
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{exteriorEnthalpy}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At h = 0, rho(h) has a nonzero right
|
* At h = 0, rho(h) has a nonzero right
|
||||||
@@ -270,20 +315,23 @@ TEST_CASE("Polytropic EOS Defines Consistent Surface And Exterior Behavior",
|
|||||||
const double expectedSurfaceDensityDerivative =
|
const double expectedSurfaceDensityDerivative =
|
||||||
polytropicIndex == 1.0 ? 1.0 / barotrope.enthalpy_scale() : 0.0;
|
polytropicIndex == 1.0 ? 1.0 / barotrope.enthalpy_scale() : 0.0;
|
||||||
|
|
||||||
CHECK(barotrope.density_derivative_from_enthalpy(0.0) ==
|
CHECK(
|
||||||
expectedSurfaceDensityDerivative);
|
(eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{0.0}
|
||||||
|
)
|
||||||
|
.value() == expectedSurfaceDensityDerivative)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Polytropic EOS Rejects Invalid Physical Inputs",
|
TEST_CASE(
|
||||||
tags::barotrope_eos_unit) {
|
"Polytropic EOS Rejects Invalid Physical Inputs",
|
||||||
CHECK_THROWS_AS(mean_field::eos::Polytrope(0.999, 1.0),
|
tags::barotrope_eos_unit
|
||||||
std::invalid_argument);
|
) {
|
||||||
|
CHECK_THROWS_AS(mean_field::eos::Polytrope(0.999, 1.0), std::invalid_argument);
|
||||||
|
|
||||||
CHECK_THROWS_AS(
|
CHECK_THROWS_AS(mean_field::eos::Polytrope(std::numeric_limits<double>::infinity(), 1.0), std::invalid_argument);
|
||||||
mean_field::eos::Polytrope(std::numeric_limits<double>::infinity(), 1.0),
|
|
||||||
std::invalid_argument);
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, 0.0), std::invalid_argument);
|
CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, 0.0), std::invalid_argument);
|
||||||
|
|
||||||
@@ -291,37 +339,55 @@ TEST_CASE("Polytropic EOS Rejects Invalid Physical Inputs",
|
|||||||
|
|
||||||
const mean_field::eos::Polytrope barotrope(3.0, 0.75);
|
const mean_field::eos::Polytrope barotrope(3.0, 0.75);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.pressure_from_density(-0.1), std::domain_error);
|
CHECK_THROWS_AS(eos::evaluate<eos::quantity::Pressure>(barotrope, eos::DensityValue{-0.1}), std::domain_error);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.enthalpy_from_density(-0.1), std::domain_error);
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(barotrope, eos::DensityValue{-0.1}), std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.pressure_derivative_from_density(-0.1),
|
CHECK_THROWS_AS(
|
||||||
std::domain_error);
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(barotrope, eos::DensityValue{-0.1})),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
constexpr std::array<double, 3> nonfiniteValues{
|
constexpr std::array<double, 3> nonfiniteValues{
|
||||||
std::numeric_limits<double>::infinity(),
|
std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity(),
|
||||||
-std::numeric_limits<double>::infinity(),
|
std::numeric_limits<double>::quiet_NaN()
|
||||||
std::numeric_limits<double>::quiet_NaN()};
|
};
|
||||||
|
|
||||||
for (const double nonfiniteValue : nonfiniteValues) {
|
for (const double nonfiniteValue : nonfiniteValues) {
|
||||||
CAPTURE(nonfiniteValue);
|
CAPTURE(nonfiniteValue);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.density_from_enthalpy(nonfiniteValue),
|
CHECK_THROWS_AS(
|
||||||
std::domain_error);
|
eos::evaluate<eos::quantity::Density>(barotrope, eos::SpecificEnthalpyValue{nonfiniteValue}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.pressure_from_enthalpy(nonfiniteValue),
|
CHECK_THROWS_AS(
|
||||||
std::domain_error);
|
eos::evaluate<eos::quantity::Pressure>(barotrope, eos::SpecificEnthalpyValue{nonfiniteValue}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.density_derivative_from_enthalpy(nonfiniteValue),
|
CHECK_THROWS_AS(
|
||||||
std::domain_error);
|
(eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{nonfiniteValue}
|
||||||
|
)),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
CHECK_THROWS_AS(barotrope.pressure_derivative_from_enthalpy(nonfiniteValue),
|
CHECK_THROWS_AS(
|
||||||
std::domain_error);
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
barotrope, eos::SpecificEnthalpyValue{nonfiniteValue}
|
||||||
|
)),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Pressure Force And Pressure Integral Have Distinct Registered Forms",
|
TEST_CASE(
|
||||||
tags::barotrope_pressure_quadrature_unit) {
|
"Pressure Force And Pressure Integral Have Distinct Registered Forms",
|
||||||
|
tags::barotrope_pressure_quadrature_unit
|
||||||
|
) {
|
||||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -336,38 +402,34 @@ TEST_CASE("Pressure Force And Pressure Integral Have Distinct Registered Forms",
|
|||||||
*
|
*
|
||||||
* beyond the registered enthalpy operand.
|
* beyond the registered enthalpy operand.
|
||||||
*/
|
*/
|
||||||
constexpr int enthalpyOrder =
|
constexpr int enthalpyOrder = mean_field::field::Enthalpy::Scalar::familyOrder;
|
||||||
mean_field::field::Enthalpy::Scalar::familyOrder;
|
|
||||||
|
|
||||||
constexpr int pressureExtraOrder = 3 * enthalpyOrder;
|
constexpr int pressureExtraOrder = 3 * enthalpyOrder;
|
||||||
|
|
||||||
constexpr int geometryWeightOrder = 2;
|
constexpr int geometryWeightOrder = 2;
|
||||||
|
|
||||||
constexpr mean_field::quadrature::Query pressureIntegralQuery =
|
constexpr mean_field::quadrature::Query pressureIntegralQuery =
|
||||||
EnthalpyField::make_query<
|
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
|
||||||
mean_field::field::Enthalpy::Form::PressureIntegral>(
|
mean_field::quadrature::QuadratureRole::diagnostic, geometryWeightOrder,
|
||||||
mean_field::quadrature::QuadratureRole::diagnostic,
|
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
|
||||||
geometryWeightOrder, std::array<int, 1>{pressureExtraOrder},
|
mean_field::quadrature::MappingKind::general
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
);
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
|
||||||
constexpr mean_field::quadrature::Query pressureForceQuery =
|
constexpr mean_field::quadrature::Query pressureForceQuery =
|
||||||
EnthalpyField::make_query<
|
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
|
||||||
mean_field::field::Enthalpy::Form::PressureForce>(
|
mean_field::quadrature::QuadratureRole::discretization, geometryWeightOrder,
|
||||||
mean_field::quadrature::QuadratureRole::discretization,
|
std::array<int, 1>{pressureExtraOrder}, mean_field::utils::DOMAINS::STELLAR,
|
||||||
geometryWeightOrder, std::array<int, 1>{pressureExtraOrder},
|
mean_field::quadrature::MappingKind::general
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
);
|
||||||
mean_field::quadrature::MappingKind::general);
|
|
||||||
|
STATIC_CHECK(mean_field::field::Enthalpy::Form::PressureIntegral::dynamicOrderCount == 1);
|
||||||
|
|
||||||
|
STATIC_CHECK(mean_field::field::Enthalpy::Form::PressureForce::dynamicOrderCount == 1);
|
||||||
|
|
||||||
STATIC_CHECK(
|
STATIC_CHECK(
|
||||||
mean_field::field::Enthalpy::Form::PressureIntegral::dynamicOrderCount ==
|
mean_field::field::Enthalpy::Form::PressureIntegral::policyKey !=
|
||||||
1);
|
mean_field::field::Enthalpy::Form::PressureForce::policyKey
|
||||||
|
);
|
||||||
STATIC_CHECK(
|
|
||||||
mean_field::field::Enthalpy::Form::PressureForce::dynamicOrderCount == 1);
|
|
||||||
|
|
||||||
STATIC_CHECK(mean_field::field::Enthalpy::Form::PressureIntegral::policyKey !=
|
|
||||||
mean_field::field::Enthalpy::Form::PressureForce::policyKey);
|
|
||||||
|
|
||||||
REQUIRE(pressureIntegralQuery.base_order.has_value());
|
REQUIRE(pressureIntegralQuery.base_order.has_value());
|
||||||
|
|
||||||
@@ -394,17 +456,13 @@ TEST_CASE("Pressure Force And Pressure Integral Have Distinct Registered Forms",
|
|||||||
*/
|
*/
|
||||||
CHECK(*pressureForceQuery.base_order == 16);
|
CHECK(*pressureForceQuery.base_order == 16);
|
||||||
|
|
||||||
CHECK(pressureIntegralQuery.term ==
|
CHECK(pressureIntegralQuery.term == mean_field::quadrature::Term::pressure_integral);
|
||||||
mean_field::quadrature::Term::pressure_integral);
|
|
||||||
|
|
||||||
CHECK(pressureForceQuery.term ==
|
CHECK(pressureForceQuery.term == mean_field::quadrature::Term::pressure_force);
|
||||||
mean_field::quadrature::Term::pressure_force);
|
|
||||||
|
|
||||||
CHECK(pressureIntegralQuery.role ==
|
CHECK(pressureIntegralQuery.role == mean_field::quadrature::QuadratureRole::diagnostic);
|
||||||
mean_field::quadrature::QuadratureRole::diagnostic);
|
|
||||||
|
|
||||||
CHECK(pressureForceQuery.role ==
|
CHECK(pressureForceQuery.role == mean_field::quadrature::QuadratureRole::discretization);
|
||||||
mean_field::quadrature::QuadratureRole::discretization);
|
|
||||||
|
|
||||||
CHECK(pressureIntegralQuery.domain == mean_field::utils::DOMAINS::STELLAR);
|
CHECK(pressureIntegralQuery.domain == mean_field::utils::DOMAINS::STELLAR);
|
||||||
|
|
||||||
@@ -415,19 +473,16 @@ TEST_CASE("Pressure Force And Pressure Integral Have Distinct Registered Forms",
|
|||||||
* controls.
|
* controls.
|
||||||
*/
|
*/
|
||||||
mean_field::quadrature::RuleSet ruleSet =
|
mean_field::quadrature::RuleSet ruleSet =
|
||||||
mean_field::quadrature::make_rule_set(
|
mean_field::quadrature::make_rule_set(mean_field::quadrature::Mode::production);
|
||||||
mean_field::quadrature::Mode::production);
|
|
||||||
|
|
||||||
ruleSet.pressure_integral.boost = 3;
|
ruleSet.pressure_integral.boost = 3;
|
||||||
ruleSet.pressure_force.boost = 5;
|
ruleSet.pressure_force.boost = 5;
|
||||||
|
|
||||||
const mean_field::quadrature::Policy policy(std::move(ruleSet));
|
const mean_field::quadrature::Policy policy(std::move(ruleSet));
|
||||||
|
|
||||||
const mean_field::quadrature::Resolution pressureIntegralResolution =
|
const mean_field::quadrature::Resolution pressureIntegralResolution = policy.resolve(pressureIntegralQuery);
|
||||||
policy.resolve(pressureIntegralQuery);
|
|
||||||
|
|
||||||
const mean_field::quadrature::Resolution pressureForceResolution =
|
const mean_field::quadrature::Resolution pressureForceResolution = policy.resolve(pressureForceQuery);
|
||||||
policy.resolve(pressureForceQuery);
|
|
||||||
|
|
||||||
CHECK(pressureIntegralResolution.base_order == 14);
|
CHECK(pressureIntegralResolution.base_order == 14);
|
||||||
|
|
||||||
@@ -442,12 +497,13 @@ TEST_CASE("Pressure Force And Pressure Integral Have Distinct Registered Forms",
|
|||||||
CHECK(pressureForceResolution.order == 21);
|
CHECK(pressureForceResolution.order == 21);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Pressure Quadrature Exactly Integrates An N Three Polynomial",
|
TEST_CASE(
|
||||||
tags::barotrope_pressure_quadrature_accuracy) {
|
"Pressure Quadrature Exactly Integrates An N Three Polynomial",
|
||||||
|
tags::barotrope_pressure_quadrature_accuracy
|
||||||
|
) {
|
||||||
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
||||||
|
|
||||||
constexpr int enthalpyOrder =
|
constexpr int enthalpyOrder = mean_field::field::Enthalpy::Scalar::familyOrder;
|
||||||
mean_field::field::Enthalpy::Scalar::familyOrder;
|
|
||||||
|
|
||||||
constexpr int pressureExtraOrder = 3 * enthalpyOrder;
|
constexpr int pressureExtraOrder = 3 * enthalpyOrder;
|
||||||
|
|
||||||
@@ -461,24 +517,20 @@ TEST_CASE("Pressure Quadrature Exactly Integrates An N Three Polynomial",
|
|||||||
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
const mean_field::eos::Polytrope barotrope(3.0, 0.25);
|
||||||
|
|
||||||
constexpr mean_field::quadrature::Query pressureIntegralQuery =
|
constexpr mean_field::quadrature::Query pressureIntegralQuery =
|
||||||
EnthalpyField::make_query<
|
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureIntegral>(
|
||||||
mean_field::field::Enthalpy::Form::PressureIntegral>(
|
mean_field::quadrature::QuadratureRole::diagnostic, 0, std::array<int, 1>{pressureExtraOrder},
|
||||||
mean_field::quadrature::QuadratureRole::diagnostic, 0,
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::affine
|
||||||
std::array<int, 1>{pressureExtraOrder},
|
);
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
|
||||||
mean_field::quadrature::MappingKind::affine);
|
|
||||||
|
|
||||||
constexpr mean_field::quadrature::Query pressureForceQuery =
|
constexpr mean_field::quadrature::Query pressureForceQuery =
|
||||||
EnthalpyField::make_query<
|
EnthalpyField::make_query<mean_field::field::Enthalpy::Form::PressureForce>(
|
||||||
mean_field::field::Enthalpy::Form::PressureForce>(
|
mean_field::quadrature::QuadratureRole::discretization, 0, std::array<int, 1>{pressureExtraOrder},
|
||||||
mean_field::quadrature::QuadratureRole::discretization, 0,
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::affine
|
||||||
std::array<int, 1>{pressureExtraOrder},
|
);
|
||||||
mean_field::utils::DOMAINS::STELLAR,
|
|
||||||
mean_field::quadrature::MappingKind::affine);
|
|
||||||
|
|
||||||
const mean_field::quadrature::RuleFactory ruleFactory{
|
const mean_field::quadrature::RuleFactory ruleFactory{
|
||||||
mean_field::quadrature::Policy(mean_field::quadrature::make_rule_set(
|
mean_field::quadrature::Policy(mean_field::quadrature::make_rule_set(mean_field::quadrature::Mode::production))
|
||||||
mean_field::quadrature::Mode::production))};
|
};
|
||||||
|
|
||||||
const mean_field::quadrature::MfemRule pressureIntegralRule =
|
const mean_field::quadrature::MfemRule pressureIntegralRule =
|
||||||
ruleFactory.get(pressureIntegralQuery, mfem::Geometry::CUBE);
|
ruleFactory.get(pressureIntegralQuery, mfem::Geometry::CUBE);
|
||||||
@@ -496,17 +548,15 @@ TEST_CASE("Pressure Quadrature Exactly Integrates An N Three Polynomial",
|
|||||||
*
|
*
|
||||||
* P = x^12 y^12 z^12 / 4.
|
* P = x^12 y^12 z^12 / 4.
|
||||||
*/
|
*/
|
||||||
const double numericalPressureIntegral =
|
const double numericalPressureIntegral = polytropic_eos_test_utils::integrate_cube(
|
||||||
polytropic_eos_test_utils::integrate_cube(
|
*pressureIntegralRule.integration_rule, [&barotrope](const mfem::IntegrationPoint &integrationPoint) {
|
||||||
*pressureIntegralRule.integration_rule,
|
const double coordinateProduct = integrationPoint.x * integrationPoint.y * integrationPoint.z;
|
||||||
[&barotrope](const mfem::IntegrationPoint &integrationPoint) {
|
|
||||||
const double coordinateProduct =
|
|
||||||
integrationPoint.x * integrationPoint.y * integrationPoint.z;
|
|
||||||
|
|
||||||
const double enthalpy = std::pow(coordinateProduct, 3.0);
|
const double enthalpy = std::pow(coordinateProduct, 3.0);
|
||||||
|
|
||||||
return barotrope.pressure_from_enthalpy(enthalpy);
|
return eos::evaluate<eos::quantity::Pressure>(barotrope, eos::SpecificEnthalpyValue{enthalpy}).value();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const double analyticPressureIntegral = 0.25 / std::pow(13.0, 3.0);
|
const double analyticPressureIntegral = 0.25 / std::pow(13.0, 3.0);
|
||||||
|
|
||||||
@@ -521,38 +571,33 @@ TEST_CASE("Pressure Quadrature Exactly Integrates An N Three Polynomial",
|
|||||||
* -P div(w)
|
* -P div(w)
|
||||||
* = -x^14 y^14 z^14 / 4.
|
* = -x^14 y^14 z^14 / 4.
|
||||||
*/
|
*/
|
||||||
const double numericalPressureForceIntegral =
|
const double numericalPressureForceIntegral = polytropic_eos_test_utils::integrate_cube(
|
||||||
polytropic_eos_test_utils::integrate_cube(
|
*pressureForceRule.integration_rule, [&barotrope](const mfem::IntegrationPoint &integrationPoint) {
|
||||||
*pressureForceRule.integration_rule,
|
const double coordinateProduct = integrationPoint.x * integrationPoint.y * integrationPoint.z;
|
||||||
[&barotrope](const mfem::IntegrationPoint &integrationPoint) {
|
|
||||||
const double coordinateProduct =
|
|
||||||
integrationPoint.x * integrationPoint.y * integrationPoint.z;
|
|
||||||
|
|
||||||
const double enthalpy = std::pow(coordinateProduct, 3.0);
|
const double enthalpy = std::pow(coordinateProduct, 3.0);
|
||||||
|
|
||||||
const double pressure = barotrope.pressure_from_enthalpy(enthalpy);
|
const double pressure =
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(barotrope, eos::SpecificEnthalpyValue{enthalpy}).value();
|
||||||
|
|
||||||
const double testDivergence =
|
const double testDivergence = integrationPoint.x * integrationPoint.x * integrationPoint.y *
|
||||||
integrationPoint.x * integrationPoint.x * integrationPoint.y *
|
|
||||||
integrationPoint.y * integrationPoint.z * integrationPoint.z;
|
integrationPoint.y * integrationPoint.z * integrationPoint.z;
|
||||||
|
|
||||||
return -pressure * testDivergence;
|
return -pressure * testDivergence;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const double analyticPressureForceIntegral = -0.25 / std::pow(15.0, 3.0);
|
const double analyticPressureForceIntegral = -0.25 / std::pow(15.0, 3.0);
|
||||||
|
|
||||||
INFO("Pressure-integral quadrature order = "
|
INFO("Pressure-integral quadrature order = " << pressureIntegralRule.resolution.order);
|
||||||
<< pressureIntegralRule.resolution.order);
|
|
||||||
|
|
||||||
INFO("Pressure-force quadrature order = "
|
INFO("Pressure-force quadrature order = " << pressureForceRule.resolution.order);
|
||||||
<< pressureForceRule.resolution.order);
|
|
||||||
|
|
||||||
INFO("Numerical pressure integral = " << numericalPressureIntegral);
|
INFO("Numerical pressure integral = " << numericalPressureIntegral);
|
||||||
|
|
||||||
INFO("Analytic pressure integral = " << analyticPressureIntegral);
|
INFO("Analytic pressure integral = " << analyticPressureIntegral);
|
||||||
|
|
||||||
INFO(
|
INFO("Numerical pressure-force integral = " << numericalPressureForceIntegral);
|
||||||
"Numerical pressure-force integral = " << numericalPressureForceIntegral);
|
|
||||||
|
|
||||||
INFO("Analytic pressure-force integral = " << analyticPressureForceIntegral);
|
INFO("Analytic pressure-force integral = " << analyticPressureForceIntegral);
|
||||||
|
|
||||||
@@ -564,10 +609,7 @@ TEST_CASE("Pressure Quadrature Exactly Integrates An N Three Polynomial",
|
|||||||
|
|
||||||
CHECK(pressureForceRule.resolution.order == 14);
|
CHECK(pressureForceRule.resolution.order == 14);
|
||||||
|
|
||||||
CHECK_THAT(numericalPressureIntegral,
|
CHECK_THAT(numericalPressureIntegral, Catch::Matchers::WithinAbs(analyticPressureIntegral, 5.0e-14));
|
||||||
Catch::Matchers::WithinAbs(analyticPressureIntegral, 5.0e-14));
|
|
||||||
|
|
||||||
CHECK_THAT(
|
CHECK_THAT(numericalPressureForceIntegral, Catch::Matchers::WithinAbs(analyticPressureForceIntegral, 5.0e-14));
|
||||||
numericalPressureForceIntegral,
|
|
||||||
Catch::Matchers::WithinAbs(analyticPressureForceIntegral, 5.0e-14));
|
|
||||||
}
|
}
|
||||||
|
|||||||
125
tests/physics/equation_of_state_consumer_contracts.cpp
Normal file
125
tests/physics/equation_of_state_consumer_contracts.cpp
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
namespace eos = mean_field::eos;
|
||||||
|
|
||||||
|
class DensityClosureEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::DensityFromSpecificEnthalpy>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::DensityValue evaluate(
|
||||||
|
eos::DensityFromSpecificEnthalpy,
|
||||||
|
const eos::SpecificEnthalpyValue specificEnthalpy
|
||||||
|
) const noexcept {
|
||||||
|
return eos::DensityValue{specificEnthalpy.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PartialDerivative<
|
||||||
|
eos::quantity::Density,
|
||||||
|
eos::quantity::SpecificEnthalpy>
|
||||||
|
partialDerivative(
|
||||||
|
eos::DensityFromSpecificEnthalpy,
|
||||||
|
eos::WithRespectTo<eos::quantity::SpecificEnthalpy>,
|
||||||
|
eos::SpecificEnthalpyValue
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PartialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>{1.0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DensityClosureWithoutDerivative final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::DensityFromSpecificEnthalpy>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::DensityValue evaluate(
|
||||||
|
eos::DensityFromSpecificEnthalpy,
|
||||||
|
const eos::SpecificEnthalpyValue specificEnthalpy
|
||||||
|
) const noexcept {
|
||||||
|
return eos::DensityValue{specificEnthalpy.value()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EnthalpyPressureEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::PressureFromSpecificEnthalpy>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PressureValue evaluate(
|
||||||
|
eos::PressureFromSpecificEnthalpy,
|
||||||
|
const eos::SpecificEnthalpyValue specificEnthalpy
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PressureValue{2.0 * specificEnthalpy.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PartialDerivative<
|
||||||
|
eos::quantity::Pressure,
|
||||||
|
eos::quantity::SpecificEnthalpy>
|
||||||
|
partialDerivative(
|
||||||
|
eos::PressureFromSpecificEnthalpy,
|
||||||
|
eos::WithRespectTo<eos::quantity::SpecificEnthalpy>,
|
||||||
|
eos::SpecificEnthalpyValue
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PartialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>{2.0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DensitySeedEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::SpecificEnthalpyFromDensity>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
||||||
|
eos::SpecificEnthalpyFromDensity,
|
||||||
|
const eos::DensityValue density
|
||||||
|
) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{3.0 * density.value()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GeneralEquationOfStateWithoutCurrentConsumerRelations final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::SpecificEnthalpyFromPressure>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
||||||
|
eos::SpecificEnthalpyFromPressure,
|
||||||
|
const eos::PressureValue pressure
|
||||||
|
) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{pressure.value()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Barotropic Closure EOS Requires Density And Its Enthalpy Derivative",
|
||||||
|
tags::barotropic_closure_equation_of_state_contract
|
||||||
|
) {
|
||||||
|
STATIC_CHECK(eos::BarotropicClosureEquationOfState<eos::Polytrope>);
|
||||||
|
STATIC_CHECK(eos::BarotropicClosureEquationOfState<DensityClosureEquationOfState>);
|
||||||
|
STATIC_CHECK(eos::EquationOfStateModel<DensityClosureWithoutDerivative>);
|
||||||
|
STATIC_CHECK_FALSE(eos::BarotropicClosureEquationOfState<DensityClosureWithoutDerivative>);
|
||||||
|
STATIC_CHECK_FALSE(eos::BarotropicClosureEquationOfState<EnthalpyPressureEquationOfState>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Pressure Force EOS Requires Pressure And Its Enthalpy Derivative",
|
||||||
|
tags::pressure_force_equation_of_state_contract
|
||||||
|
) {
|
||||||
|
STATIC_CHECK(eos::PressureForceEquationOfState<eos::Polytrope>);
|
||||||
|
STATIC_CHECK(eos::PressureForceEquationOfState<EnthalpyPressureEquationOfState>);
|
||||||
|
STATIC_CHECK_FALSE(eos::PressureForceEquationOfState<DensityClosureEquationOfState>);
|
||||||
|
STATIC_CHECK_FALSE(eos::PressureForceEquationOfState<DensityClosureWithoutDerivative>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Structure Seed EOS Requires Enthalpy From Density",
|
||||||
|
tags::structure_seed_equation_of_state_contract
|
||||||
|
) {
|
||||||
|
STATIC_CHECK(eos::StructureSeedEquationOfState<eos::Polytrope>);
|
||||||
|
STATIC_CHECK(eos::StructureSeedEquationOfState<DensitySeedEquationOfState>);
|
||||||
|
STATIC_CHECK_FALSE(eos::StructureSeedEquationOfState<DensityClosureEquationOfState>);
|
||||||
|
|
||||||
|
STATIC_CHECK(eos::EquationOfStateModel<GeneralEquationOfStateWithoutCurrentConsumerRelations>);
|
||||||
|
STATIC_CHECK_FALSE(eos::StructureSeedEquationOfState<GeneralEquationOfStateWithoutCurrentConsumerRelations>);
|
||||||
|
STATIC_CHECK_FALSE(eos::BarotropicClosureEquationOfState<GeneralEquationOfStateWithoutCurrentConsumerRelations>);
|
||||||
|
STATIC_CHECK_FALSE(eos::PressureForceEquationOfState<GeneralEquationOfStateWithoutCurrentConsumerRelations>);
|
||||||
|
}
|
||||||
322
tests/physics/equation_of_state_runtime_view.cpp
Normal file
322
tests/physics/equation_of_state_runtime_view.cpp
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
#include <array>
|
||||||
|
#include <concepts>
|
||||||
|
#include <expected>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
namespace eos = mean_field::eos;
|
||||||
|
|
||||||
|
class LinearPressureEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::PressureFromDensity>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PressureValue evaluate(
|
||||||
|
eos::PressureFromDensity,
|
||||||
|
const eos::DensityValue density
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PressureValue{2.0 * density.value() + 0.5};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DensityAlias final : eos::ThermodynamicQuantity {
|
||||||
|
static constexpr std::string_view identifier = "density";
|
||||||
|
};
|
||||||
|
|
||||||
|
class AmbiguouslyIdentifiedEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::Relation<DensityAlias, eos::quantity::Density>>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::QuantityValue<DensityAlias> evaluate(
|
||||||
|
eos::Relation<
|
||||||
|
DensityAlias,
|
||||||
|
eos::quantity::Density>,
|
||||||
|
const eos::DensityValue density
|
||||||
|
) const noexcept {
|
||||||
|
return eos::QuantityValue<DensityAlias>{density.value()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<
|
||||||
|
eos::PressureValue,
|
||||||
|
eos::EvaluationError>
|
||||||
|
pressureAtDensity(
|
||||||
|
const eos::EquationOfStateView equationOfState,
|
||||||
|
const eos::DensityValue density
|
||||||
|
) {
|
||||||
|
return equationOfState.tryEvaluate<eos::quantity::Pressure>(density);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const eos::RuntimeRelationDescriptor *findRelation(
|
||||||
|
const eos::EquationOfStateView equationOfState,
|
||||||
|
const eos::ThermodynamicQuantityId output,
|
||||||
|
const eos::ThermodynamicQuantityId input
|
||||||
|
) {
|
||||||
|
for (const eos::RuntimeRelationDescriptor &relation : equationOfState.relations()) {
|
||||||
|
if (relation.outputQuantity == output && relation.inputQuantities.size() == 1 &&
|
||||||
|
relation.inputQuantities[0] == input) {
|
||||||
|
return std::addressof(relation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Runtime EOS View Generates The Polytropic Relation Catalog",
|
||||||
|
tags::equation_of_state_runtime_contract
|
||||||
|
) {
|
||||||
|
STATIC_CHECK(eos::RuntimeEquationOfStateModel<eos::Polytrope>);
|
||||||
|
STATIC_CHECK(eos::RuntimeEquationOfStateModel<LinearPressureEquationOfState>);
|
||||||
|
STATIC_CHECK(eos::EquationOfStateModel<AmbiguouslyIdentifiedEquationOfState>);
|
||||||
|
STATIC_CHECK_FALSE(eos::RuntimeEquationOfStateModel<AmbiguouslyIdentifiedEquationOfState>);
|
||||||
|
STATIC_CHECK(std::is_trivially_copyable_v<eos::EquationOfStateView>);
|
||||||
|
STATIC_CHECK_FALSE(std::constructible_from<eos::EquationOfStateView, eos::Polytrope &&>);
|
||||||
|
|
||||||
|
const eos::Polytrope equationOfState(3.0, 0.25);
|
||||||
|
const eos::Polytrope secondEquationOfState(1.5, 0.73);
|
||||||
|
|
||||||
|
const eos::EquationOfStateView view{equationOfState};
|
||||||
|
const eos::EquationOfStateView secondView{secondEquationOfState};
|
||||||
|
|
||||||
|
REQUIRE(view.relations().size() == eos::Polytrope::Relations::size);
|
||||||
|
CHECK(view.relations().data() == secondView.relations().data());
|
||||||
|
|
||||||
|
CHECK(eos::thermodynamicQuantityId<eos::quantity::Density>.name() == "density");
|
||||||
|
CHECK(eos::thermodynamicQuantityId<eos::quantity::Pressure>.name() == "pressure");
|
||||||
|
CHECK(eos::thermodynamicQuantityId<eos::quantity::SpecificEnthalpy>.name() == "specific_enthalpy");
|
||||||
|
|
||||||
|
const eos::RuntimeRelationDescriptor *pressureFromDensity = findRelation(
|
||||||
|
view, eos::thermodynamicQuantityId<eos::quantity::Pressure>,
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::Density>
|
||||||
|
);
|
||||||
|
|
||||||
|
REQUIRE(pressureFromDensity != nullptr);
|
||||||
|
CHECK(pressureFromDensity->hasPartialDerivative(0));
|
||||||
|
|
||||||
|
const eos::RuntimeRelationDescriptor *specificEnthalpyFromPressure = findRelation(
|
||||||
|
view, eos::thermodynamicQuantityId<eos::quantity::SpecificEnthalpy>,
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::Pressure>
|
||||||
|
);
|
||||||
|
|
||||||
|
REQUIRE(specificEnthalpyFromPressure != nullptr);
|
||||||
|
CHECK_FALSE(specificEnthalpyFromPressure->hasPartialDerivative(0));
|
||||||
|
|
||||||
|
const eos::RuntimeRelationDescriptor *pressureFromSpecificEnthalpy = findRelation(
|
||||||
|
view, eos::thermodynamicQuantityId<eos::quantity::Pressure>,
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::SpecificEnthalpy>
|
||||||
|
);
|
||||||
|
|
||||||
|
REQUIRE(pressureFromSpecificEnthalpy != nullptr);
|
||||||
|
CHECK(pressureFromSpecificEnthalpy->hasPartialDerivative(0));
|
||||||
|
|
||||||
|
const eos::RuntimeRelationDescriptor *specificEnthalpyFromDensity = findRelation(
|
||||||
|
view, eos::thermodynamicQuantityId<eos::quantity::SpecificEnthalpy>,
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::Density>
|
||||||
|
);
|
||||||
|
|
||||||
|
REQUIRE(specificEnthalpyFromDensity != nullptr);
|
||||||
|
CHECK_FALSE(specificEnthalpyFromDensity->hasPartialDerivative(0));
|
||||||
|
|
||||||
|
const eos::RuntimeRelationDescriptor *densityFromSpecificEnthalpy = findRelation(
|
||||||
|
view, eos::thermodynamicQuantityId<eos::quantity::Density>,
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::SpecificEnthalpy>
|
||||||
|
);
|
||||||
|
|
||||||
|
REQUIRE(densityFromSpecificEnthalpy != nullptr);
|
||||||
|
CHECK(densityFromSpecificEnthalpy->hasPartialDerivative(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Runtime EOS View Matches Typed Polytropic Evaluation",
|
||||||
|
tags::equation_of_state_runtime_compatibility
|
||||||
|
) {
|
||||||
|
const eos::Polytrope equationOfState(3.0, 0.25);
|
||||||
|
const eos::EquationOfStateView view{equationOfState};
|
||||||
|
|
||||||
|
const eos::DensityValue density{0.7};
|
||||||
|
const eos::SpecificEnthalpyValue specificEnthalpy{0.9};
|
||||||
|
const eos::PressureValue pressure{0.04};
|
||||||
|
|
||||||
|
const auto runtimePressureFromDensity = view.tryEvaluate<eos::quantity::Pressure>(density);
|
||||||
|
const auto runtimePressureFromSpecificEnthalpy = view.tryEvaluate<eos::quantity::Pressure>(specificEnthalpy);
|
||||||
|
const auto runtimeSpecificEnthalpyFromDensity = view.tryEvaluate<eos::quantity::SpecificEnthalpy>(density);
|
||||||
|
const auto runtimeSpecificEnthalpyFromPressure = view.tryEvaluate<eos::quantity::SpecificEnthalpy>(pressure);
|
||||||
|
const auto runtimeDensityFromSpecificEnthalpy = view.tryEvaluate<eos::quantity::Density>(specificEnthalpy);
|
||||||
|
|
||||||
|
REQUIRE(runtimePressureFromDensity.has_value());
|
||||||
|
REQUIRE(runtimePressureFromSpecificEnthalpy.has_value());
|
||||||
|
REQUIRE(runtimeSpecificEnthalpyFromDensity.has_value());
|
||||||
|
REQUIRE(runtimeSpecificEnthalpyFromPressure.has_value());
|
||||||
|
REQUIRE(runtimeDensityFromSpecificEnthalpy.has_value());
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
runtimePressureFromDensity->value() == eos::evaluate<eos::quantity::Pressure>(equationOfState, density).value()
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
runtimePressureFromSpecificEnthalpy->value() ==
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, specificEnthalpy).value()
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
runtimeSpecificEnthalpyFromDensity->value() ==
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, density).value()
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
runtimeSpecificEnthalpyFromPressure->value() ==
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, pressure).value()
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
runtimeDensityFromSpecificEnthalpy->value() ==
|
||||||
|
eos::evaluate<eos::quantity::Density>(equationOfState, specificEnthalpy).value()
|
||||||
|
);
|
||||||
|
|
||||||
|
const std::array runtimeDensityInput{
|
||||||
|
eos::RuntimeQuantityValue{eos::thermodynamicQuantityId<eos::quantity::Density>, density.value()}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto erasedPressureFromDensity = view.tryEvaluate(
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::Pressure>,
|
||||||
|
std::span<const eos::RuntimeQuantityValue>{runtimeDensityInput}
|
||||||
|
);
|
||||||
|
|
||||||
|
REQUIRE(erasedPressureFromDensity.has_value());
|
||||||
|
CHECK(erasedPressureFromDensity->quantity == eos::thermodynamicQuantityId<eos::quantity::Pressure>);
|
||||||
|
CHECK(erasedPressureFromDensity->value == runtimePressureFromDensity->value());
|
||||||
|
|
||||||
|
const auto runtimePressureDerivative =
|
||||||
|
view.tryPartialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(specificEnthalpy);
|
||||||
|
|
||||||
|
const auto runtimeDensityDerivative =
|
||||||
|
view.tryPartialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(specificEnthalpy);
|
||||||
|
|
||||||
|
const auto runtimePressureDensityDerivative =
|
||||||
|
view.tryPartialDerivative<eos::quantity::Pressure, eos::quantity::Density>(density);
|
||||||
|
|
||||||
|
REQUIRE(runtimePressureDerivative.has_value());
|
||||||
|
REQUIRE(runtimeDensityDerivative.has_value());
|
||||||
|
REQUIRE(runtimePressureDensityDerivative.has_value());
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
runtimePressureDerivative->value() ==
|
||||||
|
eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, specificEnthalpy
|
||||||
|
)
|
||||||
|
.value()
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
runtimeDensityDerivative->value() ==
|
||||||
|
eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, specificEnthalpy
|
||||||
|
)
|
||||||
|
.value()
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
runtimePressureDensityDerivative->value() ==
|
||||||
|
eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(equationOfState, density).value()
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto erasedPressureDensityDerivative = view.tryPartialDerivative(
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::Pressure>, eos::thermodynamicQuantityId<eos::quantity::Density>,
|
||||||
|
std::span<const eos::RuntimeQuantityValue>{runtimeDensityInput}
|
||||||
|
);
|
||||||
|
|
||||||
|
REQUIRE(erasedPressureDensityDerivative.has_value());
|
||||||
|
CHECK(*erasedPressureDensityDerivative == runtimePressureDensityDerivative->value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Runtime EOS View Reports Unsupported And Invalid Requests",
|
||||||
|
tags::equation_of_state_runtime_contract
|
||||||
|
) {
|
||||||
|
const eos::Polytrope equationOfState(3.0, 0.25);
|
||||||
|
const eos::EquationOfStateView view{equationOfState};
|
||||||
|
|
||||||
|
constexpr eos::ThermodynamicQuantityId temperature{"temperature"};
|
||||||
|
|
||||||
|
const std::array densityInput{eos::RuntimeQuantityValue{eos::thermodynamicQuantityId<eos::quantity::Density>, 0.7}};
|
||||||
|
|
||||||
|
const std::array pressureInput{
|
||||||
|
eos::RuntimeQuantityValue{eos::thermodynamicQuantityId<eos::quantity::Pressure>, 0.04}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<eos::RuntimeQuantityValue, 0> noInputs{};
|
||||||
|
|
||||||
|
const auto unsupportedOutput =
|
||||||
|
view.tryEvaluate(temperature, std::span<const eos::RuntimeQuantityValue>{densityInput});
|
||||||
|
REQUIRE_FALSE(unsupportedOutput.has_value());
|
||||||
|
CHECK(unsupportedOutput.error().code() == eos::EvaluationErrorCode::unsupported_relation);
|
||||||
|
|
||||||
|
const auto wrongInputCount = view.tryEvaluate(
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::Pressure>, std::span<const eos::RuntimeQuantityValue>{noInputs}
|
||||||
|
);
|
||||||
|
REQUIRE_FALSE(wrongInputCount.has_value());
|
||||||
|
CHECK(wrongInputCount.error().code() == eos::EvaluationErrorCode::wrong_input_count);
|
||||||
|
|
||||||
|
const auto wrongInputQuantity = view.tryEvaluate(
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::Density>, std::span<const eos::RuntimeQuantityValue>{pressureInput}
|
||||||
|
);
|
||||||
|
REQUIRE_FALSE(wrongInputQuantity.has_value());
|
||||||
|
CHECK(wrongInputQuantity.error().code() == eos::EvaluationErrorCode::wrong_input_quantity);
|
||||||
|
|
||||||
|
const auto unsupportedDerivative = view.tryPartialDerivative(
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::SpecificEnthalpy>,
|
||||||
|
eos::thermodynamicQuantityId<eos::quantity::Pressure>, std::span<const eos::RuntimeQuantityValue>{pressureInput}
|
||||||
|
);
|
||||||
|
REQUIRE_FALSE(unsupportedDerivative.has_value());
|
||||||
|
CHECK(unsupportedDerivative.error().code() == eos::EvaluationErrorCode::unsupported_derivative);
|
||||||
|
|
||||||
|
const auto invalidDensity = view.tryEvaluate<eos::quantity::Pressure>(eos::DensityValue{-0.1});
|
||||||
|
REQUIRE_FALSE(invalidDensity.has_value());
|
||||||
|
CHECK(invalidDensity.error().code() == eos::EvaluationErrorCode::outside_domain);
|
||||||
|
|
||||||
|
const auto nonfiniteDensity =
|
||||||
|
view.tryEvaluate<eos::quantity::Pressure>(eos::DensityValue{std::numeric_limits<double>::quiet_NaN()});
|
||||||
|
REQUIRE_FALSE(nonfiniteDensity.has_value());
|
||||||
|
CHECK(nonfiniteDensity.error().code() == eos::EvaluationErrorCode::nonfinite_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"One Runtime EOS Function Accepts Heterogeneous Concrete Models",
|
||||||
|
tags::equation_of_state_runtime_compatibility
|
||||||
|
) {
|
||||||
|
const eos::Polytrope polytrope(3.0, 0.25);
|
||||||
|
const LinearPressureEquationOfState linearEquationOfState;
|
||||||
|
|
||||||
|
const std::array views{eos::EquationOfStateView{polytrope}, eos::EquationOfStateView{linearEquationOfState}};
|
||||||
|
|
||||||
|
const eos::DensityValue density{0.7};
|
||||||
|
|
||||||
|
const auto polytropicPressure = pressureAtDensity(views[0], density);
|
||||||
|
const auto linearPressure = pressureAtDensity(views[1], density);
|
||||||
|
|
||||||
|
REQUIRE(polytropicPressure.has_value());
|
||||||
|
REQUIRE(linearPressure.has_value());
|
||||||
|
|
||||||
|
CHECK(polytropicPressure->value() == eos::evaluate<eos::quantity::Pressure>(polytrope, density).value());
|
||||||
|
CHECK(linearPressure->value() == 1.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Runtime EOS View Remains Valid When Stable Ownership Moves",
|
||||||
|
tags::equation_of_state_runtime_contract
|
||||||
|
) {
|
||||||
|
auto owner = std::make_unique<const eos::Polytrope>(3.0, 0.25);
|
||||||
|
const eos::EquationOfStateView view{*owner};
|
||||||
|
|
||||||
|
auto movedOwner = std::move(owner);
|
||||||
|
|
||||||
|
const auto pressure = view.tryEvaluate<eos::quantity::Pressure>(eos::DensityValue{0.7});
|
||||||
|
|
||||||
|
REQUIRE(movedOwner != nullptr);
|
||||||
|
REQUIRE(pressure.has_value());
|
||||||
|
CHECK(pressure->value() == eos::evaluate<eos::quantity::Pressure>(*movedOwner, eos::DensityValue{0.7}).value());
|
||||||
|
}
|
||||||
228
tests/physics/equation_of_state_type_system.cpp
Normal file
228
tests/physics/equation_of_state_type_system.cpp
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
#include <concepts>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
namespace eos = mean_field::eos;
|
||||||
|
|
||||||
|
struct Entropy final : eos::ThermodynamicQuantity { };
|
||||||
|
struct ElectronFraction final : eos::ThermodynamicQuantity { };
|
||||||
|
|
||||||
|
using SpecificEnthalpyFromPressureAndEntropy =
|
||||||
|
eos::Relation<eos::quantity::SpecificEnthalpy, eos::quantity::Pressure, Entropy>;
|
||||||
|
|
||||||
|
class CompleteEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<
|
||||||
|
eos::PressureFromDensity,
|
||||||
|
eos::SpecificEnthalpyFromPressure,
|
||||||
|
SpecificEnthalpyFromPressureAndEntropy>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PressureValue evaluate(
|
||||||
|
eos::PressureFromDensity,
|
||||||
|
const eos::DensityValue density
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PressureValue{2.0 * density.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
||||||
|
eos::SpecificEnthalpyFromPressure,
|
||||||
|
const eos::PressureValue pressure
|
||||||
|
) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{3.0 * pressure.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
||||||
|
SpecificEnthalpyFromPressureAndEntropy,
|
||||||
|
const eos::PressureValue pressure,
|
||||||
|
const eos::QuantityValue<Entropy> entropy
|
||||||
|
) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{3.0 * pressure.value() + 5.0 * entropy.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PartialDerivative<
|
||||||
|
eos::quantity::SpecificEnthalpy,
|
||||||
|
Entropy>
|
||||||
|
partialDerivative(
|
||||||
|
SpecificEnthalpyFromPressureAndEntropy,
|
||||||
|
eos::WithRespectTo<Entropy>,
|
||||||
|
eos::PressureValue,
|
||||||
|
eos::QuantityValue<Entropy>
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PartialDerivative<eos::quantity::SpecificEnthalpy, Entropy>{5.0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MissingRelationImplementation final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::PressureFromDensity, eos::SpecificEnthalpyFromPressure>;
|
||||||
|
|
||||||
|
[[nodiscard]] eos::PressureValue evaluate(
|
||||||
|
eos::PressureFromDensity,
|
||||||
|
eos::DensityValue density
|
||||||
|
) const {
|
||||||
|
return eos::PressureValue{density.value()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IncorrectRelationOutput final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::PressureFromDensity>;
|
||||||
|
|
||||||
|
[[nodiscard]] eos::DensityValue evaluate(
|
||||||
|
eos::PressureFromDensity,
|
||||||
|
eos::DensityValue density
|
||||||
|
) const {
|
||||||
|
return density;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class InvalidRelationCatalog final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::Relation<double, eos::quantity::Density>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename EquationOfState>
|
||||||
|
concept CanEvaluateDensityFromSpecificEnthalpy = requires(const EquationOfState &equationOfState) {
|
||||||
|
eos::evaluate<eos::quantity::Density>(equationOfState, eos::SpecificEnthalpyValue{1.0});
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Thermodynamic Values Preserve Physical Quantity Types",
|
||||||
|
tags::equation_of_state_quantity_types
|
||||||
|
) {
|
||||||
|
STATIC_CHECK(eos::ThermodynamicQuantityType<eos::quantity::Density>);
|
||||||
|
STATIC_CHECK(eos::ThermodynamicQuantityType<eos::quantity::Pressure>);
|
||||||
|
STATIC_CHECK(eos::ThermodynamicQuantityType<eos::quantity::SpecificEnthalpy>);
|
||||||
|
STATIC_CHECK_FALSE(eos::ThermodynamicQuantityType<const eos::quantity::Pressure>);
|
||||||
|
|
||||||
|
STATIC_CHECK_FALSE(std::same_as<eos::DensityValue, eos::PressureValue>);
|
||||||
|
STATIC_CHECK_FALSE(std::same_as<eos::PressureValue, eos::SpecificEnthalpyValue>);
|
||||||
|
STATIC_CHECK_FALSE(std::is_convertible_v<double, eos::PressureValue>);
|
||||||
|
STATIC_CHECK_FALSE(std::is_constructible_v<eos::PressureValue, eos::DensityValue>);
|
||||||
|
|
||||||
|
STATIC_CHECK(std::is_trivially_copyable_v<eos::DensityValue>);
|
||||||
|
STATIC_CHECK(std::is_standard_layout_v<eos::DensityValue>);
|
||||||
|
STATIC_CHECK(sizeof(eos::DensityValue) == sizeof(double));
|
||||||
|
STATIC_CHECK(sizeof(eos::PressureValue) == sizeof(double));
|
||||||
|
STATIC_CHECK(sizeof(eos::SpecificEnthalpyValue) == sizeof(double));
|
||||||
|
STATIC_CHECK(std::is_empty_v<eos::PressureFromDensity>);
|
||||||
|
|
||||||
|
constexpr eos::DensityValue density{-0.25};
|
||||||
|
STATIC_CHECK(density.value() == -0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Thermodynamic Derivatives Preserve Numerator And Denominator Types",
|
||||||
|
tags::equation_of_state_quantity_types
|
||||||
|
) {
|
||||||
|
using PressureByDensity = eos::PartialDerivative<eos::quantity::Pressure, eos::quantity::Density>;
|
||||||
|
|
||||||
|
using PressureBySpecificEnthalpy = eos::PartialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>;
|
||||||
|
|
||||||
|
STATIC_CHECK_FALSE(std::same_as<PressureByDensity, PressureBySpecificEnthalpy>);
|
||||||
|
STATIC_CHECK_FALSE(std::is_convertible_v<PressureByDensity, PressureBySpecificEnthalpy>);
|
||||||
|
STATIC_CHECK(std::is_trivially_copyable_v<PressureByDensity>);
|
||||||
|
STATIC_CHECK(std::is_standard_layout_v<PressureByDensity>);
|
||||||
|
STATIC_CHECK(sizeof(PressureByDensity) == sizeof(double));
|
||||||
|
|
||||||
|
constexpr PressureByDensity derivative{1.75};
|
||||||
|
STATIC_CHECK(derivative.value() == 1.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"EOS Relation Catalog Rejects Invalid And Duplicate Relations",
|
||||||
|
tags::equation_of_state_relation_contract
|
||||||
|
) {
|
||||||
|
using ValidCatalog = eos::RelationCatalog<eos::PressureFromDensity, eos::SpecificEnthalpyFromPressure>;
|
||||||
|
|
||||||
|
using DuplicateCatalog = eos::RelationCatalog<eos::PressureFromDensity, eos::PressureFromDensity>;
|
||||||
|
|
||||||
|
using InvalidRelation = eos::Relation<double, eos::quantity::Density>;
|
||||||
|
using InvalidCatalog = eos::RelationCatalog<InvalidRelation>;
|
||||||
|
using RepeatedInputRelation =
|
||||||
|
eos::Relation<eos::quantity::Pressure, eos::quantity::Density, eos::quantity::Density>;
|
||||||
|
using RepeatedInputCatalog = eos::RelationCatalog<RepeatedInputRelation>;
|
||||||
|
|
||||||
|
STATIC_CHECK(eos::ValidRelationCatalog<ValidCatalog>);
|
||||||
|
STATIC_CHECK_FALSE(eos::ValidRelationCatalog<DuplicateCatalog>);
|
||||||
|
STATIC_CHECK_FALSE(eos::ValidRelationCatalog<InvalidCatalog>);
|
||||||
|
STATIC_CHECK_FALSE(eos::ValidRelationCatalog<RepeatedInputCatalog>);
|
||||||
|
STATIC_CHECK_FALSE(eos::ValidRelationCatalog<eos::RelationCatalog<>>);
|
||||||
|
|
||||||
|
STATIC_CHECK(eos::relationCatalogContains<ValidCatalog, eos::PressureFromDensity>);
|
||||||
|
STATIC_CHECK_FALSE(eos::relationCatalogContains<ValidCatalog, eos::DensityFromSpecificEnthalpy>);
|
||||||
|
STATIC_CHECK(eos::relationContainsInput<eos::PressureFromDensity, eos::quantity::Density>);
|
||||||
|
STATIC_CHECK_FALSE(eos::relationContainsInput<eos::PressureFromDensity, eos::quantity::Pressure>);
|
||||||
|
|
||||||
|
STATIC_CHECK(std::same_as<eos::RelationOutputT<eos::PressureFromDensity>, eos::quantity::Pressure>);
|
||||||
|
STATIC_CHECK(std::same_as<eos::RelationInputT<0, eos::PressureFromDensity>, eos::quantity::Density>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"EOS Model Contract Requires Every Declared Relation",
|
||||||
|
tags::equation_of_state_relation_contract
|
||||||
|
) {
|
||||||
|
STATIC_CHECK(eos::EquationOfStateModel<CompleteEquationOfState>);
|
||||||
|
STATIC_CHECK_FALSE(eos::EquationOfStateModel<MissingRelationImplementation>);
|
||||||
|
STATIC_CHECK_FALSE(eos::EquationOfStateModel<IncorrectRelationOutput>);
|
||||||
|
STATIC_CHECK_FALSE(eos::EquationOfStateModel<InvalidRelationCatalog>);
|
||||||
|
|
||||||
|
STATIC_CHECK(eos::SupportsRelation<CompleteEquationOfState, eos::PressureFromDensity>);
|
||||||
|
STATIC_CHECK_FALSE(eos::SupportsRelation<CompleteEquationOfState, eos::DensityFromSpecificEnthalpy>);
|
||||||
|
STATIC_CHECK_FALSE(CanEvaluateDensityFromSpecificEnthalpy<CompleteEquationOfState>);
|
||||||
|
STATIC_CHECK(
|
||||||
|
eos::SupportsPartialDerivative<CompleteEquationOfState, SpecificEnthalpyFromPressureAndEntropy, Entropy>
|
||||||
|
);
|
||||||
|
STATIC_CHECK_FALSE(
|
||||||
|
eos::SupportsPartialDerivative<
|
||||||
|
CompleteEquationOfState, SpecificEnthalpyFromPressureAndEntropy, ElectronFraction>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"EOS Evaluation Selects Relations From Typed Inputs",
|
||||||
|
tags::equation_of_state_relation_contract
|
||||||
|
) {
|
||||||
|
constexpr CompleteEquationOfState equationOfState;
|
||||||
|
|
||||||
|
constexpr eos::PressureValue pressure =
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::DensityValue{1.25});
|
||||||
|
|
||||||
|
constexpr eos::SpecificEnthalpyValue specificEnthalpy = eos::evaluate<eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::PressureValue{0.5}, eos::QuantityValue<Entropy>{0.2}
|
||||||
|
);
|
||||||
|
|
||||||
|
constexpr auto entropyDerivative = eos::partialDerivative<eos::quantity::SpecificEnthalpy, Entropy>(
|
||||||
|
equationOfState, eos::PressureValue{0.5}, eos::QuantityValue<Entropy>{0.2}
|
||||||
|
);
|
||||||
|
|
||||||
|
STATIC_CHECK(noexcept(eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::DensityValue{1.25})));
|
||||||
|
STATIC_CHECK(
|
||||||
|
noexcept(eos::partialDerivative<eos::quantity::SpecificEnthalpy, Entropy>(
|
||||||
|
equationOfState, eos::PressureValue{0.5}, eos::QuantityValue<Entropy>{0.2}
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
STATIC_CHECK(pressure.value() == 2.5);
|
||||||
|
STATIC_CHECK(specificEnthalpy.value() == 2.5);
|
||||||
|
STATIC_CHECK(entropyDerivative.value() == 5.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"EOS Evaluation Errors Retain A Structured Cause",
|
||||||
|
tags::equation_of_state_relation_contract
|
||||||
|
) {
|
||||||
|
const eos::EvaluationError error(
|
||||||
|
eos::EvaluationErrorCode::outside_domain, "Density is outside the relation domain."
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(error.code() == eos::EvaluationErrorCode::outside_domain);
|
||||||
|
CHECK(std::string_view{error.what()} == "Density is outside the relation domain.");
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
156
tests/physics/polytropic_eos_characterization.cpp
Normal file
156
tests/physics/polytropic_eos_characterization.cpp
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
namespace eos = mean_field::eos;
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Polytropic EOS Pressure To Specific Enthalpy Relation Is Characterized",
|
||||||
|
tags::polytropic_eos_characterization
|
||||||
|
) {
|
||||||
|
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
||||||
|
constexpr std::array<double, 3> polytropicConstants{0.25, 0.73, 2.0};
|
||||||
|
constexpr std::array<double, 5> pressures{0.0, 1.0e-12, 1.0e-4, 0.3, 5.0};
|
||||||
|
|
||||||
|
for (const double polytropicIndex : polytropicIndices) {
|
||||||
|
for (const double polytropicConstant : polytropicConstants) {
|
||||||
|
const mean_field::eos::Polytrope equationOfState(polytropicIndex, polytropicConstant);
|
||||||
|
|
||||||
|
for (const double pressure : pressures) {
|
||||||
|
CAPTURE(polytropicIndex, polytropicConstant, pressure);
|
||||||
|
|
||||||
|
const double indexPlusOne = polytropicIndex + 1.0;
|
||||||
|
const double expectedEnthalpy = indexPlusOne *
|
||||||
|
std::pow(polytropicConstant, polytropicIndex / indexPlusOne) *
|
||||||
|
std::pow(pressure, 1.0 / indexPlusOne);
|
||||||
|
|
||||||
|
const double enthalpy =
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::PressureValue{pressure})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
if (pressure == 0.0) {
|
||||||
|
CHECK(enthalpy == 0.0);
|
||||||
|
} else {
|
||||||
|
CHECK_THAT(enthalpy, Catch::Matchers::WithinRel(expectedEnthalpy, 5.0e-14));
|
||||||
|
|
||||||
|
const double recoveredPressure =
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::SpecificEnthalpyValue{enthalpy})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
CHECK_THAT(recoveredPressure, Catch::Matchers::WithinRel(pressure, 5.0e-13));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Polytropic EOS Domain Contract Covers Every Relation",
|
||||||
|
tags::polytropic_eos_characterization
|
||||||
|
) {
|
||||||
|
constexpr double infinity = std::numeric_limits<double>::infinity();
|
||||||
|
constexpr double quietNaN = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
|
||||||
|
for (const double invalidIndex : std::array<double, 4>{0.999, infinity, -infinity, quietNaN}) {
|
||||||
|
CAPTURE(invalidIndex);
|
||||||
|
CHECK_THROWS_AS(mean_field::eos::Polytrope(invalidIndex, 1.0), std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const double invalidConstant : std::array<double, 5>{0.0, -0.1, infinity, -infinity, quietNaN}) {
|
||||||
|
CAPTURE(invalidConstant);
|
||||||
|
CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, invalidConstant), std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mean_field::eos::Polytrope equationOfState(3.0, 0.75);
|
||||||
|
|
||||||
|
constexpr double negativeDensity = -0.1;
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::DensityValue{negativeDensity}), std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::DensityValue{negativeDensity}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(
|
||||||
|
equationOfState, eos::DensityValue{negativeDensity}
|
||||||
|
)),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::PressureValue{-0.1}), std::domain_error
|
||||||
|
);
|
||||||
|
|
||||||
|
constexpr double exteriorEnthalpy = -0.1;
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Density>(equationOfState, eos::SpecificEnthalpyValue{exteriorEnthalpy}).value() ==
|
||||||
|
0.0
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::SpecificEnthalpyValue{exteriorEnthalpy}).value() ==
|
||||||
|
0.0
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
(eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::SpecificEnthalpyValue{exteriorEnthalpy}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
CHECK(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::SpecificEnthalpyValue{exteriorEnthalpy}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const double nonfiniteValue : std::array<double, 3>{infinity, -infinity, quietNaN}) {
|
||||||
|
CAPTURE(nonfiniteValue);
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::DensityValue{nonfiniteValue}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::DensityValue{nonfiniteValue}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(
|
||||||
|
equationOfState, eos::DensityValue{nonfiniteValue}
|
||||||
|
)),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::PressureValue{nonfiniteValue}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::Density>(equationOfState, eos::SpecificEnthalpyValue{nonfiniteValue}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::SpecificEnthalpyValue{nonfiniteValue}),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
(eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::SpecificEnthalpyValue{nonfiniteValue}
|
||||||
|
)),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::SpecificEnthalpyValue{nonfiniteValue}
|
||||||
|
)),
|
||||||
|
std::domain_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
200
tests/physics/polytropic_eos_relations.cpp
Normal file
200
tests/physics/polytropic_eos_relations.cpp
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <concepts>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
namespace eos = mean_field::eos;
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept HasAnyUnaryEquationOfStateConversion =
|
||||||
|
requires(const Candidate &candidate, const double value) { candidate.pressure_from_density(value); } ||
|
||||||
|
requires(const Candidate &candidate, const double value) { candidate.pressure_from_enthalpy(value); } ||
|
||||||
|
requires(const Candidate &candidate, const double value) { candidate.enthalpy_from_density(value); } ||
|
||||||
|
requires(const Candidate &candidate, const double value) { candidate.enthalpy_from_pressure(value); } ||
|
||||||
|
requires(const Candidate &candidate, const double value) { candidate.density_from_enthalpy(value); } ||
|
||||||
|
requires(const Candidate &candidate, const double value) {
|
||||||
|
candidate.density_derivative_from_enthalpy(value);
|
||||||
|
} ||
|
||||||
|
requires(const Candidate &candidate, const double value) {
|
||||||
|
candidate.pressure_derivative_from_enthalpy(value);
|
||||||
|
} ||
|
||||||
|
requires(const Candidate &candidate, const double value) { candidate.pressure_derivative_from_density(value); };
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Polytropic EOS Declares Its Thermodynamic Relation Contract",
|
||||||
|
tags::polytropic_eos_relation_contract
|
||||||
|
) {
|
||||||
|
using Polytrope = eos::Polytrope;
|
||||||
|
|
||||||
|
STATIC_CHECK(eos::EquationOfStateModel<Polytrope>);
|
||||||
|
STATIC_CHECK_FALSE(std::is_polymorphic_v<Polytrope>);
|
||||||
|
STATIC_CHECK_FALSE(HasAnyUnaryEquationOfStateConversion<Polytrope>);
|
||||||
|
STATIC_CHECK(Polytrope::Relations::size == 5);
|
||||||
|
|
||||||
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::PressureFromDensity>);
|
||||||
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::PressureFromSpecificEnthalpy>);
|
||||||
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::SpecificEnthalpyFromDensity>);
|
||||||
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::SpecificEnthalpyFromPressure>);
|
||||||
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::DensityFromSpecificEnthalpy>);
|
||||||
|
|
||||||
|
STATIC_CHECK(eos::SupportsPartialDerivative<Polytrope, eos::PressureFromDensity, eos::quantity::Density>);
|
||||||
|
STATIC_CHECK(
|
||||||
|
eos::SupportsPartialDerivative<Polytrope, eos::PressureFromSpecificEnthalpy, eos::quantity::SpecificEnthalpy>
|
||||||
|
);
|
||||||
|
STATIC_CHECK(
|
||||||
|
eos::SupportsPartialDerivative<Polytrope, eos::DensityFromSpecificEnthalpy, eos::quantity::SpecificEnthalpy>
|
||||||
|
);
|
||||||
|
|
||||||
|
STATIC_CHECK_FALSE(
|
||||||
|
eos::SupportsPartialDerivative<Polytrope, eos::SpecificEnthalpyFromPressure, eos::quantity::Pressure>
|
||||||
|
);
|
||||||
|
STATIC_CHECK_FALSE(
|
||||||
|
eos::SupportsPartialDerivative<Polytrope, eos::SpecificEnthalpyFromDensity, eos::quantity::Density>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Polytropic EOS Typed Relations Preserve Analytic Values",
|
||||||
|
tags::polytropic_eos_characterization
|
||||||
|
) {
|
||||||
|
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
||||||
|
constexpr std::array<double, 2> polytropicConstants{0.25, 0.73};
|
||||||
|
constexpr std::array<double, 4> densities{0.0, 1.0e-6, 0.2, 2.0};
|
||||||
|
constexpr std::array<double, 4> specificEnthalpies{-0.3, 0.0, 0.2, 1.7};
|
||||||
|
constexpr std::array<double, 4> pressures{0.0, 1.0e-8, 0.3, 4.0};
|
||||||
|
|
||||||
|
for (const double polytropicIndex : polytropicIndices) {
|
||||||
|
for (const double polytropicConstant : polytropicConstants) {
|
||||||
|
const eos::Polytrope equationOfState(polytropicIndex, polytropicConstant);
|
||||||
|
|
||||||
|
for (const double density : densities) {
|
||||||
|
CAPTURE(polytropicIndex, polytropicConstant, density);
|
||||||
|
|
||||||
|
const double expectedPressure = polytropicConstant * std::pow(density, 1.0 + 1.0 / polytropicIndex);
|
||||||
|
const double expectedSpecificEnthalpy =
|
||||||
|
(polytropicIndex + 1.0) * polytropicConstant * std::pow(density, 1.0 / polytropicIndex);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::DensityValue{density}).value() ==
|
||||||
|
expectedPressure
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::DensityValue{density})
|
||||||
|
.value() == expectedSpecificEnthalpy
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK_THAT(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(
|
||||||
|
equationOfState, eos::DensityValue{density}
|
||||||
|
)
|
||||||
|
.value()),
|
||||||
|
Catch::Matchers::WithinRel(
|
||||||
|
density == 0.0 ? 0.0 : expectedSpecificEnthalpy / polytropicIndex, 2.0e-15
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const double specificEnthalpy : specificEnthalpies) {
|
||||||
|
CAPTURE(polytropicIndex, polytropicConstant, specificEnthalpy);
|
||||||
|
|
||||||
|
const double expectedDensity =
|
||||||
|
specificEnthalpy <= 0.0
|
||||||
|
? 0.0
|
||||||
|
: std::pow(specificEnthalpy / ((polytropicIndex + 1.0) * polytropicConstant), polytropicIndex);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Density>(equationOfState, eos::SpecificEnthalpyValue{specificEnthalpy})
|
||||||
|
.value() == expectedDensity
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(
|
||||||
|
equationOfState, eos::SpecificEnthalpyValue{specificEnthalpy}
|
||||||
|
)
|
||||||
|
.value() ==
|
||||||
|
(specificEnthalpy <= 0.0 ? 0.0 : expectedDensity * specificEnthalpy / (polytropicIndex + 1.0))
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::SpecificEnthalpyValue{specificEnthalpy}
|
||||||
|
)
|
||||||
|
.value() == expectedDensity)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const double pressure : pressures) {
|
||||||
|
CAPTURE(polytropicIndex, polytropicConstant, pressure);
|
||||||
|
|
||||||
|
const double indexPlusOne = polytropicIndex + 1.0;
|
||||||
|
const double expectedSpecificEnthalpy = indexPlusOne *
|
||||||
|
std::pow(polytropicConstant, polytropicIndex / indexPlusOne) *
|
||||||
|
std::pow(pressure, 1.0 / indexPlusOne);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::PressureValue{pressure})
|
||||||
|
.value() == expectedSpecificEnthalpy
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Typed Polytropic EOS Preserves Domain And Exterior Semantics",
|
||||||
|
tags::polytropic_eos_relation_contract
|
||||||
|
) {
|
||||||
|
const eos::Polytrope equationOfState(3.0, 0.75);
|
||||||
|
|
||||||
|
try {
|
||||||
|
static_cast<void>(eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::DensityValue{-0.1}));
|
||||||
|
FAIL("A negative density must be rejected.");
|
||||||
|
} catch (const eos::EvaluationError &error) {
|
||||||
|
CHECK(error.code() == eos::EvaluationErrorCode::outside_domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
static_cast<void>(eos::evaluate<eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::PressureValue{std::numeric_limits<double>::quiet_NaN()}
|
||||||
|
));
|
||||||
|
FAIL("A nonfinite pressure must be rejected.");
|
||||||
|
} catch (const eos::EvaluationError &error) {
|
||||||
|
CHECK(error.code() == eos::EvaluationErrorCode::nonfinite_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr double exteriorSpecificEnthalpy = -0.3;
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Density>(equationOfState, eos::SpecificEnthalpyValue{exteriorSpecificEnthalpy})
|
||||||
|
.value() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::SpecificEnthalpyValue{exteriorSpecificEnthalpy})
|
||||||
|
.value() == 0.0
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
(eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::SpecificEnthalpyValue{exteriorSpecificEnthalpy}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
||||||
|
equationOfState, eos::SpecificEnthalpyValue{exteriorSpecificEnthalpy}
|
||||||
|
)
|
||||||
|
.value() == 0.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
329
tests/surface/constant_surface_compilation.cpp
Normal file
329
tests/surface/constant_surface_compilation.cpp
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <concepts>
|
||||||
|
#include <limits>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
|
||||||
|
import mean_field;
|
||||||
|
import test_helpers;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
namespace eos = mean_field::eos;
|
||||||
|
namespace field = mean_field::field;
|
||||||
|
namespace surface = mean_field::surface;
|
||||||
|
|
||||||
|
struct Entropy final : eos::ThermodynamicQuantity {
|
||||||
|
static constexpr std::string_view identifier = "entropy";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ElectronFraction final : eos::ThermodynamicQuantity {
|
||||||
|
static constexpr std::string_view identifier = "electron_fraction";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EntropyField final {
|
||||||
|
static constexpr std::string_view name = "entropy";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ElectronFractionField final {
|
||||||
|
static constexpr std::string_view name = "electron_fraction";
|
||||||
|
};
|
||||||
|
|
||||||
|
using SpecificEnthalpyFromPressureEntropyAndElectronFraction =
|
||||||
|
eos::Relation<eos::quantity::SpecificEnthalpy, eos::quantity::Pressure, Entropy, ElectronFraction>;
|
||||||
|
|
||||||
|
class GeneralStellarMatterEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<SpecificEnthalpyFromPressureEntropyAndElectronFraction>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
||||||
|
SpecificEnthalpyFromPressureEntropyAndElectronFraction,
|
||||||
|
const eos::PressureValue pressure,
|
||||||
|
const eos::QuantityValue<Entropy> entropy,
|
||||||
|
const eos::QuantityValue<ElectronFraction> electronFraction
|
||||||
|
) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{
|
||||||
|
2.0 * pressure.value() + 3.0 * entropy.value() + 5.0 * electronFraction.value()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PartialDerivative<
|
||||||
|
eos::quantity::SpecificEnthalpy,
|
||||||
|
Entropy>
|
||||||
|
partialDerivative(
|
||||||
|
SpecificEnthalpyFromPressureEntropyAndElectronFraction,
|
||||||
|
eos::WithRespectTo<Entropy>,
|
||||||
|
eos::PressureValue,
|
||||||
|
eos::QuantityValue<Entropy>,
|
||||||
|
eos::QuantityValue<ElectronFraction>
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PartialDerivative<eos::quantity::SpecificEnthalpy, Entropy>{3.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PartialDerivative<
|
||||||
|
eos::quantity::SpecificEnthalpy,
|
||||||
|
ElectronFraction>
|
||||||
|
partialDerivative(
|
||||||
|
SpecificEnthalpyFromPressureEntropyAndElectronFraction,
|
||||||
|
eos::WithRespectTo<ElectronFraction>,
|
||||||
|
eos::PressureValue,
|
||||||
|
eos::QuantityValue<Entropy>,
|
||||||
|
eos::QuantityValue<ElectronFraction>
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PartialDerivative<eos::quantity::SpecificEnthalpy, ElectronFraction>{5.0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GeneralEquationOfStateWithoutElectronFractionPartial final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<SpecificEnthalpyFromPressureEntropyAndElectronFraction>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
||||||
|
SpecificEnthalpyFromPressureEntropyAndElectronFraction,
|
||||||
|
const eos::PressureValue pressure,
|
||||||
|
const eos::QuantityValue<Entropy> entropy,
|
||||||
|
const eos::QuantityValue<ElectronFraction> electronFraction
|
||||||
|
) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{pressure.value() + entropy.value() + electronFraction.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PartialDerivative<
|
||||||
|
eos::quantity::SpecificEnthalpy,
|
||||||
|
Entropy>
|
||||||
|
partialDerivative(
|
||||||
|
SpecificEnthalpyFromPressureEntropyAndElectronFraction,
|
||||||
|
eos::WithRespectTo<Entropy>,
|
||||||
|
eos::PressureValue,
|
||||||
|
eos::QuantityValue<Entropy>,
|
||||||
|
eos::QuantityValue<ElectronFraction>
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PartialDerivative<eos::quantity::SpecificEnthalpy, Entropy>{1.0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using SpecificEnthalpyFromPressureAndEntropy =
|
||||||
|
eos::Relation<eos::quantity::SpecificEnthalpy, eos::quantity::Pressure, Entropy>;
|
||||||
|
|
||||||
|
class AmbiguousSurfaceEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations =
|
||||||
|
eos::RelationCatalog<eos::SpecificEnthalpyFromPressure, SpecificEnthalpyFromPressureAndEntropy>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
||||||
|
eos::SpecificEnthalpyFromPressure,
|
||||||
|
const eos::PressureValue pressure
|
||||||
|
) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{pressure.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
||||||
|
SpecificEnthalpyFromPressureAndEntropy,
|
||||||
|
const eos::PressureValue pressure,
|
||||||
|
const eos::QuantityValue<Entropy> entropy
|
||||||
|
) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{pressure.value() + entropy.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PartialDerivative<
|
||||||
|
eos::quantity::SpecificEnthalpy,
|
||||||
|
Entropy>
|
||||||
|
partialDerivative(
|
||||||
|
SpecificEnthalpyFromPressureAndEntropy,
|
||||||
|
eos::WithRespectTo<Entropy>,
|
||||||
|
eos::PressureValue,
|
||||||
|
eos::QuantityValue<Entropy>
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PartialDerivative<eos::quantity::SpecificEnthalpy, Entropy>{1.0};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DensityOnlyEquationOfState final {
|
||||||
|
public:
|
||||||
|
using Relations = eos::RelationCatalog<eos::PressureFromDensity>;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr eos::PressureValue evaluate(
|
||||||
|
eos::PressureFromDensity,
|
||||||
|
const eos::DensityValue density
|
||||||
|
) const noexcept {
|
||||||
|
return eos::PressureValue{density.value()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using GeneralSurfaceFormulation = surface::SurfaceConstraintFormulation<
|
||||||
|
eos::quantity::SpecificEnthalpy,
|
||||||
|
field::Enthalpy,
|
||||||
|
surface::SurfaceStateBindings<
|
||||||
|
surface::SurfaceStateBinding<eos::quantity::SpecificEnthalpy, field::Enthalpy>,
|
||||||
|
surface::SurfaceStateBinding<Entropy, EntropyField>,
|
||||||
|
surface::SurfaceStateBinding<ElectronFraction, ElectronFractionField>>>;
|
||||||
|
|
||||||
|
struct PolytropicSurfaceState final {
|
||||||
|
double specificEnthalpy;
|
||||||
|
|
||||||
|
[[nodiscard]] eos::SpecificEnthalpyValue value(eos::quantity::SpecificEnthalpy) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{specificEnthalpy};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GeneralSurfaceState final {
|
||||||
|
double specificEnthalpy;
|
||||||
|
double entropy;
|
||||||
|
double electronFraction;
|
||||||
|
|
||||||
|
[[nodiscard]] eos::SpecificEnthalpyValue value(eos::quantity::SpecificEnthalpy) const noexcept {
|
||||||
|
return eos::SpecificEnthalpyValue{specificEnthalpy};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] eos::QuantityValue<Entropy> value(Entropy) const noexcept {
|
||||||
|
return eos::QuantityValue<Entropy>{entropy};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] eos::QuantityValue<ElectronFraction> value(ElectronFraction) const noexcept {
|
||||||
|
return eos::QuantityValue<ElectronFraction>{electronFraction};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Candidate>
|
||||||
|
concept HasTargetEnthalpy = requires(const Candidate &candidate) { candidate.targetEnthalpy; };
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Constant Pressure Surface Prescribes Only A Pressure Quantity",
|
||||||
|
tags::surface_prescription_type_contract
|
||||||
|
) {
|
||||||
|
STATIC_CHECK(std::same_as<surface::ConstantPressureSurface::PhysicalQuantity, eos::quantity::Pressure>);
|
||||||
|
STATIC_CHECK(std::constructible_from<surface::ConstantPressureSurface, eos::PressureValue>);
|
||||||
|
STATIC_CHECK_FALSE(std::constructible_from<surface::ConstantPressureSurface, eos::SpecificEnthalpyValue>);
|
||||||
|
STATIC_CHECK_FALSE(std::constructible_from<surface::ConstantPressureSurface, double>);
|
||||||
|
STATIC_CHECK(std::same_as<surface::Isobaric, surface::ConstantPressureSurface>);
|
||||||
|
STATIC_CHECK(std::is_trivially_copyable_v<surface::ConstantPressureSurface>);
|
||||||
|
STATIC_CHECK(std::is_trivially_copyable_v<surface::PressureSurfaceDescriptor>);
|
||||||
|
STATIC_CHECK(std::is_trivially_copyable_v<surface::RuntimeSurfaceConstraintDependencies>);
|
||||||
|
|
||||||
|
const surface::ConstantPressureSurface pressureSurface{eos::PressureValue{0.03125}};
|
||||||
|
|
||||||
|
CHECK(pressureSurface.targetPressure() == eos::PressureValue{0.03125});
|
||||||
|
CHECK(pressureSurface.descriptor().targetPressure == 0.03125);
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(surface::ConstantPressureSurface{eos::PressureValue{-0.1}}, std::invalid_argument);
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
surface::ConstantPressureSurface{eos::PressureValue{std::numeric_limits<double>::infinity()}},
|
||||||
|
std::invalid_argument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Polytropic EOS Resolves Constant Surface Pressure Through Its Enthalpy Relation",
|
||||||
|
tags::surface_constraint_compilation
|
||||||
|
) {
|
||||||
|
using Formulation = surface::BarotropicSurfaceFormulation;
|
||||||
|
|
||||||
|
STATIC_CHECK(surface::PressureSurfaceCompilable<Formulation, eos::Polytrope>);
|
||||||
|
STATIC_CHECK_FALSE(surface::PressureSurfaceCompilable<Formulation, DensityOnlyEquationOfState>);
|
||||||
|
|
||||||
|
const eos::Polytrope equationOfState(3.0, 0.25);
|
||||||
|
const surface::ConstantPressureSurface pressureSurface{eos::PressureValue{0.03125}};
|
||||||
|
const auto constraint = surface::compilePressureSurfaceConstraint<Formulation>(pressureSurface, equationOfState);
|
||||||
|
|
||||||
|
using Constraint = std::remove_cvref_t<decltype(constraint)>;
|
||||||
|
using Dependencies = Constraint::SurfaceDependencies;
|
||||||
|
|
||||||
|
STATIC_CHECK(std::is_trivially_copyable_v<Constraint>);
|
||||||
|
STATIC_CHECK(std::same_as<Constraint::Relation, eos::SpecificEnthalpyFromPressure>);
|
||||||
|
STATIC_CHECK(std::same_as<Dependencies::RowField, field::Enthalpy>);
|
||||||
|
STATIC_CHECK(std::same_as<Dependencies::StateFieldTypes, field::TypeList<field::Enthalpy>>);
|
||||||
|
STATIC_CHECK_FALSE(HasTargetEnthalpy<Constraint>);
|
||||||
|
|
||||||
|
const double requiredSpecificEnthalpy =
|
||||||
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, pressureSurface.targetPressure()).value();
|
||||||
|
const PolytropicSurfaceState state{requiredSpecificEnthalpy};
|
||||||
|
const PolytropicSurfaceState variation{-0.19};
|
||||||
|
|
||||||
|
CHECK(constraint.targetPressure() == eos::PressureValue{0.03125});
|
||||||
|
CHECK(constraint.residual(state) == 0.0);
|
||||||
|
CHECK(constraint.jacobianAction(state, variation) == -0.19);
|
||||||
|
|
||||||
|
const auto runtimeDependencies = constraint.runtimeDependencies();
|
||||||
|
REQUIRE(runtimeDependencies.stateFields.size() == 1);
|
||||||
|
CHECK(runtimeDependencies.residualRowField == surface::surfaceFieldId<field::Enthalpy>);
|
||||||
|
CHECK(runtimeDependencies.stateFields[0] == surface::surfaceFieldId<field::Enthalpy>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"General EOS Resolves Constant Surface Pressure With Local Composition",
|
||||||
|
tags::surface_constraint_compilation
|
||||||
|
) {
|
||||||
|
STATIC_CHECK(surface::PressureSurfaceCompilable<GeneralSurfaceFormulation, GeneralStellarMatterEquationOfState>);
|
||||||
|
STATIC_CHECK_FALSE(
|
||||||
|
surface::PressureSurfaceCompilable<surface::BarotropicSurfaceFormulation, GeneralStellarMatterEquationOfState>
|
||||||
|
);
|
||||||
|
STATIC_CHECK_FALSE(
|
||||||
|
surface::PressureSurfaceCompilable<
|
||||||
|
GeneralSurfaceFormulation, GeneralEquationOfStateWithoutElectronFractionPartial>
|
||||||
|
);
|
||||||
|
STATIC_CHECK_FALSE(surface::PressureSurfaceCompilable<GeneralSurfaceFormulation, AmbiguousSurfaceEquationOfState>);
|
||||||
|
|
||||||
|
const GeneralStellarMatterEquationOfState equationOfState;
|
||||||
|
const surface::ConstantPressureSurface pressureSurface{eos::PressureValue{0.4}};
|
||||||
|
const auto constraint =
|
||||||
|
surface::compilePressureSurfaceConstraint<GeneralSurfaceFormulation>(pressureSurface, equationOfState);
|
||||||
|
|
||||||
|
using Constraint = std::remove_cvref_t<decltype(constraint)>;
|
||||||
|
using Dependencies = Constraint::SurfaceDependencies;
|
||||||
|
|
||||||
|
STATIC_CHECK(std::same_as<Constraint::Relation, SpecificEnthalpyFromPressureEntropyAndElectronFraction>);
|
||||||
|
STATIC_CHECK(
|
||||||
|
std::same_as<
|
||||||
|
Dependencies::StateFieldTypes, field::TypeList<field::Enthalpy, EntropyField, ElectronFractionField>>
|
||||||
|
);
|
||||||
|
|
||||||
|
constexpr GeneralSurfaceState firstSurface{
|
||||||
|
.specificEnthalpy = 2.0 * 0.4 + 3.0 * 0.2 + 5.0 * 0.1, .entropy = 0.2, .electronFraction = 0.1
|
||||||
|
};
|
||||||
|
constexpr GeneralSurfaceState secondSurface{
|
||||||
|
.specificEnthalpy = 2.0 * 0.4 + 3.0 * 0.3 + 5.0 * 0.1, .entropy = 0.3, .electronFraction = 0.1
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK(firstSurface.specificEnthalpy != secondSurface.specificEnthalpy);
|
||||||
|
CHECK(constraint.residual(firstSurface) == 0.0);
|
||||||
|
CHECK(constraint.residual(secondSurface) == 0.0);
|
||||||
|
|
||||||
|
const auto runtimeDependencies = constraint.runtimeDependencies();
|
||||||
|
REQUIRE(runtimeDependencies.stateFields.size() == 3);
|
||||||
|
CHECK(runtimeDependencies.stateFields[0] == surface::surfaceFieldId<field::Enthalpy>);
|
||||||
|
CHECK(runtimeDependencies.stateFields[1] == surface::surfaceFieldId<EntropyField>);
|
||||||
|
CHECK(runtimeDependencies.stateFields[2] == surface::surfaceFieldId<ElectronFractionField>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"General EOS Pressure Surface Jacobian Includes Every Local State Dependency",
|
||||||
|
tags::surface_constraint_jacobian
|
||||||
|
) {
|
||||||
|
const GeneralStellarMatterEquationOfState equationOfState;
|
||||||
|
const surface::ConstantPressureSurface pressureSurface{eos::PressureValue{0.4}};
|
||||||
|
const auto constraint =
|
||||||
|
surface::compilePressureSurfaceConstraint<GeneralSurfaceFormulation>(pressureSurface, equationOfState);
|
||||||
|
|
||||||
|
constexpr GeneralSurfaceState state{.specificEnthalpy = 1.7, .entropy = 0.2, .electronFraction = 0.1};
|
||||||
|
constexpr GeneralSurfaceState variation{.specificEnthalpy = 0.7, .entropy = -0.2, .electronFraction = 0.05};
|
||||||
|
constexpr double step = 1.0e-7;
|
||||||
|
|
||||||
|
const GeneralSurfaceState forward{
|
||||||
|
.specificEnthalpy = state.specificEnthalpy + step * variation.specificEnthalpy,
|
||||||
|
.entropy = state.entropy + step * variation.entropy,
|
||||||
|
.electronFraction = state.electronFraction + step * variation.electronFraction
|
||||||
|
};
|
||||||
|
const GeneralSurfaceState backward{
|
||||||
|
.specificEnthalpy = state.specificEnthalpy - step * variation.specificEnthalpy,
|
||||||
|
.entropy = state.entropy - step * variation.entropy,
|
||||||
|
.electronFraction = state.electronFraction - step * variation.electronFraction
|
||||||
|
};
|
||||||
|
|
||||||
|
const double finiteDifference = (constraint.residual(forward) - constraint.residual(backward)) / (2.0 * step);
|
||||||
|
const double jacobianAction = constraint.jacobianAction(state, variation);
|
||||||
|
|
||||||
|
CHECK(jacobianAction == variation.specificEnthalpy - 3.0 * variation.entropy - 5.0 * variation.electronFraction);
|
||||||
|
CHECK_THAT(finiteDifference, Catch::Matchers::WithinAbs(jacobianAction, 2.0e-9));
|
||||||
|
}
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#include <cmath>
|
|
||||||
#include <limits>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
|
|
||||||
import mean_field;
|
|
||||||
import test_helpers;
|
|
||||||
|
|
||||||
TEST_CASE(
|
|
||||||
"Isobaric Surface Resolves Zero Pressure To Zero Enthalpy",
|
|
||||||
tags::barotrope &tags::unit &tags::surface
|
|
||||||
) {
|
|
||||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
|
||||||
|
|
||||||
const mean_field::surface::Isobaric surface;
|
|
||||||
|
|
||||||
const mean_field::surface::ResolvedSurfaceCondition resolved = surface.resolve(equationOfState);
|
|
||||||
|
|
||||||
CHECK(surface.targetPressure() == 0.0);
|
|
||||||
CHECK(resolved.targetEnthalpy == 0.0);
|
|
||||||
CHECK(resolved.residual(0.0) == 0.0);
|
|
||||||
CHECK(resolved.residual(0.37) == 0.37);
|
|
||||||
CHECK(resolved.jacobianAction(-0.19) == -0.19);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE(
|
|
||||||
"Isobaric Surface Resolves Positive Pressure Through The EOS",
|
|
||||||
tags::barotrope &tags::unit &tags::surface
|
|
||||||
) {
|
|
||||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
|
||||||
|
|
||||||
constexpr double targetPressure = 0.03125;
|
|
||||||
|
|
||||||
const mean_field::surface::Isobaric surface(targetPressure);
|
|
||||||
|
|
||||||
const mean_field::surface::ResolvedSurfaceCondition resolved = surface.resolve(equationOfState);
|
|
||||||
|
|
||||||
const double recoveredPressure = equationOfState.pressure_from_enthalpy(resolved.targetEnthalpy);
|
|
||||||
|
|
||||||
INFO("Resolved surface enthalpy = " << resolved.targetEnthalpy);
|
|
||||||
INFO("Recovered surface pressure = " << recoveredPressure);
|
|
||||||
|
|
||||||
CHECK(resolved.targetEnthalpy > 0.0);
|
|
||||||
CHECK(std::abs(recoveredPressure - targetPressure) < 64.0 * std::numeric_limits<double>::epsilon());
|
|
||||||
CHECK(resolved.residual(resolved.targetEnthalpy) == 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE(
|
|
||||||
"Isobaric Surface Rejects Invalid Pressure Targets",
|
|
||||||
tags::barotrope &tags::unit &tags::surface
|
|
||||||
) {
|
|
||||||
CHECK_THROWS_AS(mean_field::surface::Isobaric(-1.0), std::invalid_argument);
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(mean_field::surface::Isobaric(std::numeric_limits<double>::infinity()), std::invalid_argument);
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(mean_field::surface::Isobaric(std::numeric_limits<double>::quiet_NaN()), std::invalid_argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE(
|
|
||||||
"Surface Base Dispatch Preserves The Isobaric Prescription",
|
|
||||||
tags::barotrope &tags::unit &tags::surface
|
|
||||||
) {
|
|
||||||
const mean_field::eos::Polytrope equationOfState(3.0, 0.25);
|
|
||||||
|
|
||||||
const mean_field::surface::Isobaric isobaric(0.02);
|
|
||||||
|
|
||||||
const mean_field::surface::SurfaceBase &surface = isobaric;
|
|
||||||
|
|
||||||
surface.validate(equationOfState);
|
|
||||||
|
|
||||||
const mean_field::surface::ResolvedSurfaceCondition resolved = surface.resolve(equationOfState);
|
|
||||||
|
|
||||||
CHECK(resolved.targetEnthalpy > 0.0);
|
|
||||||
CHECK(resolved.residual(resolved.targetEnthalpy) == 0.0);
|
|
||||||
}
|
|
||||||
@@ -16,18 +16,25 @@ template <std::size_t N> struct Tag {
|
|||||||
std::array<char, N> chars{};
|
std::array<char, N> chars{};
|
||||||
|
|
||||||
// ReSharper disable once CppNonExplicitConvertingConstructor
|
// ReSharper disable once CppNonExplicitConvertingConstructor
|
||||||
consteval Tag(std::array<char, N> arr) : chars(arr) {}
|
consteval Tag(
|
||||||
|
std::array<
|
||||||
|
char,
|
||||||
|
N> arr
|
||||||
|
)
|
||||||
|
: chars(arr) {
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable once CppNonExplicitConversionOperator
|
// ReSharper disable once CppNonExplicitConversionOperator
|
||||||
constexpr operator const char *() const { return chars.data(); }
|
constexpr operator const char *() const {
|
||||||
|
return chars.data();
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable once CppNonExplicitConversionOperator
|
// ReSharper disable once CppNonExplicitConversionOperator
|
||||||
constexpr operator Catch::StringRef() const {
|
constexpr operator Catch::StringRef() const {
|
||||||
return Catch::StringRef(chars.data(), N - 1);
|
return Catch::StringRef(chars.data(), N - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t M>
|
template <std::size_t M> consteval Tag<N + M - 1> operator&(const Tag<M> &other) const {
|
||||||
consteval Tag<N + M - 1> operator&(const Tag<M> &other) const {
|
|
||||||
std::array<char, N + M - 1> res{};
|
std::array<char, N + M - 1> res{};
|
||||||
std::ranges::copy(chars.begin(), chars.end() - 1, res.begin());
|
std::ranges::copy(chars.begin(), chars.end() - 1, res.begin());
|
||||||
std::ranges::copy(other.chars, res.begin() + (N - 1));
|
std::ranges::copy(other.chars, res.begin() + (N - 1));
|
||||||
@@ -44,109 +51,112 @@ template <std::size_t N> consteval auto make_tag(const char (&str)[N]) {
|
|||||||
return Tag<N + 2>{res};
|
return Tag<N + 2>{res};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N, std::size_t M>
|
template <
|
||||||
consteval auto sub_tag(const Tag<N> &parent, const char (&str)[M]) {
|
std::size_t N,
|
||||||
|
std::size_t M>
|
||||||
|
consteval auto sub_tag(
|
||||||
|
const Tag<N> &parent,
|
||||||
|
const char (&str)[M]
|
||||||
|
) {
|
||||||
return parent & make_tag(str);
|
return parent & make_tag(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace test_utils::detail {
|
namespace test_utils::detail {
|
||||||
std::optional<mean_field::utils::Args> configured_args;
|
std::optional<mean_field::utils::Args> configured_args;
|
||||||
|
|
||||||
mean_field::utils::Args make_default_args() {
|
mean_field::utils::Args make_default_args() {
|
||||||
mean_field::utils::Args args;
|
mean_field::utils::Args args;
|
||||||
args.mesh_file = "sandbox.smesh";
|
args.mesh_file = "sandbox.smesh";
|
||||||
args.p.rtol = 1.0e-12;
|
args.p.rtol = 1.0e-12;
|
||||||
args.p.atol = 1.0e-12;
|
args.p.atol = 1.0e-12;
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
} // namespace test_utils::detail
|
} // namespace test_utils::detail
|
||||||
|
|
||||||
export namespace test_utils {
|
export namespace test_utils {
|
||||||
void set_args(mean_field::utils::Args args) {
|
void set_args(mean_field::utils::Args args) {
|
||||||
detail::configured_args = std::move(args);
|
detail::configured_args = std::move(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
mean_field::utils::Args setup_args() {
|
mean_field::utils::Args setup_args() {
|
||||||
if (detail::configured_args.has_value()) {
|
if (detail::configured_args.has_value()) {
|
||||||
return *detail::configured_args;
|
return *detail::configured_args;
|
||||||
}
|
}
|
||||||
|
|
||||||
return detail::make_default_args();
|
return detail::make_default_args();
|
||||||
}
|
}
|
||||||
} // namespace test_utils
|
} // namespace test_utils
|
||||||
|
|
||||||
export namespace gravity_prepared_test_utils {
|
export namespace gravity_prepared_test_utils {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
template <typename FieldT>
|
template <typename FieldT> inline mean_field::field::FieldDofMap make_field_map(const mean_field::fem::FEM &f) {
|
||||||
inline mean_field::field::FieldDofMap
|
|
||||||
make_field_map(const mean_field::fem::FEM &f) {
|
|
||||||
if constexpr (std::same_as<FieldT, mean_field::field::Density>) {
|
if constexpr (std::same_as<FieldT, mean_field::field::Density>) {
|
||||||
return mean_field::field::make_field_dof_map<FieldT, DomainSchema>(
|
return mean_field::field::make_field_dof_map<FieldT, DomainSchema>(*f.densityFes);
|
||||||
*f.densityFes);
|
|
||||||
} else if constexpr (std::same_as<FieldT, mean_field::field::Displacement>) {
|
} else if constexpr (std::same_as<FieldT, mean_field::field::Displacement>) {
|
||||||
return mean_field::field::make_field_dof_map<FieldT, DomainSchema>(
|
return mean_field::field::make_field_dof_map<FieldT, DomainSchema>(*f.displacementFes);
|
||||||
*f.displacementFes);
|
|
||||||
} else {
|
} else {
|
||||||
static_assert(std::same_as<FieldT, mean_field::field::Gravity>);
|
static_assert(std::same_as<FieldT, mean_field::field::Gravity>);
|
||||||
return mean_field::field::make_field_dof_map<FieldT, DomainSchema>(
|
return mean_field::field::make_field_dof_map<FieldT, DomainSchema>(*f.gravityFluxFes);
|
||||||
*f.gravityFluxFes);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FieldT>
|
template <typename FieldT>
|
||||||
inline mfem::Vector gather_field(const mean_field::fem::FEM &f,
|
inline mfem::Vector gather_field(
|
||||||
const mfem::Vector &true_vector) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const mfem::Vector &true_vector
|
||||||
|
) {
|
||||||
return make_field_map<FieldT>(f).gather(true_vector);
|
return make_field_map<FieldT>(f).gather(true_vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline mfem::Vector make_deterministic_vector(const int size,
|
inline mfem::Vector make_deterministic_vector(
|
||||||
const double phase = 0.0) {
|
const int size,
|
||||||
|
const double phase = 0.0
|
||||||
|
) {
|
||||||
mfem::Vector vector(size);
|
mfem::Vector vector(size);
|
||||||
|
|
||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i) {
|
||||||
const double index = static_cast<double>(i + 1);
|
const double index = static_cast<double>(i + 1);
|
||||||
vector(i) = std::sin(0.37 * index + phase) +
|
vector(i) = std::sin(0.37 * index + phase) + 0.31 * std::cos(0.19 * index - 0.5 * phase);
|
||||||
0.31 * std::cos(0.19 * index - 0.5 * phase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline mfem::Vector make_displacement(const mean_field::fem::FEM &f,
|
inline mfem::Vector make_displacement(
|
||||||
const double scale) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const double scale
|
||||||
|
) {
|
||||||
mfem::ParGridFunction displacement(f.displacementFes.get());
|
mfem::ParGridFunction displacement(f.displacementFes.get());
|
||||||
|
|
||||||
auto displacement_function = [scale](const mfem::Vector &position,
|
auto displacement_function = [scale](const mfem::Vector &position, mfem::Vector &value) {
|
||||||
mfem::Vector &value) {
|
|
||||||
value.SetSize(3);
|
value.SetSize(3);
|
||||||
value(0) = scale * (0.04 * position(0) + 0.01 * position(1) * position(2));
|
value(0) = scale * (0.04 * position(0) + 0.01 * position(1) * position(2));
|
||||||
value(1) =
|
value(1) = scale * (-0.03 * position(1) + 0.008 * position(0) * position(2));
|
||||||
scale * (-0.03 * position(1) + 0.008 * position(0) * position(2));
|
|
||||||
value(2) = scale * (0.02 * position(2) - 0.006 * position(0) * position(1));
|
value(2) = scale * (0.02 * position(2) - 0.006 * position(0) * position(1));
|
||||||
};
|
};
|
||||||
|
|
||||||
mfem::VectorFunctionCoefficient coefficient(f.mesh->Dimension(),
|
mfem::VectorFunctionCoefficient coefficient(f.mesh->Dimension(), displacement_function);
|
||||||
displacement_function);
|
|
||||||
displacement.ProjectCoefficient(coefficient);
|
displacement.ProjectCoefficient(coefficient);
|
||||||
|
|
||||||
mfem::Vector displacement_true;
|
mfem::Vector displacement_true;
|
||||||
displacement.GetTrueDofs(displacement_true);
|
displacement.GetTrueDofs(displacement_true);
|
||||||
return displacement_true;
|
return displacement_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline mfem::Vector make_domain_supported_density(const mean_field::fem::FEM &f,
|
inline mfem::Vector make_domain_supported_density(
|
||||||
const bool stellar) {
|
const mean_field::fem::FEM &f,
|
||||||
|
const bool stellar
|
||||||
|
) {
|
||||||
mfem::Vector attribute_values(f.mesh->attributes.Max());
|
mfem::Vector attribute_values(f.mesh->attributes.Max());
|
||||||
attribute_values = 0.0;
|
attribute_values = 0.0;
|
||||||
|
|
||||||
using DomainSchema =
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
||||||
|
|
||||||
for (int i = 0; i < f.mesh->attributes.Size(); ++i) {
|
for (int i = 0; i < f.mesh->attributes.Size(); ++i) {
|
||||||
const int attribute = f.mesh->attributes[i];
|
const int attribute = f.mesh->attributes[i];
|
||||||
const bool is_stellar = DomainSchema::template attribute_belongs_to<
|
const bool is_stellar =
|
||||||
mean_field::utils::domain::Stellar>(attribute);
|
DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Stellar>(attribute);
|
||||||
|
|
||||||
if (is_stellar == stellar) {
|
if (is_stellar == stellar) {
|
||||||
attribute_values(attribute - 1) = 1.0;
|
attribute_values(attribute - 1) = 1.0;
|
||||||
@@ -160,109 +170,114 @@ inline mfem::Vector make_domain_supported_density(const mean_field::fem::FEM &f,
|
|||||||
mfem::Vector density_true;
|
mfem::Vector density_true;
|
||||||
density.GetTrueDofs(density_true);
|
density.GetTrueDofs(density_true);
|
||||||
return density_true;
|
return density_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline mfem::Vector linear_combination(const mfem::Vector &first,
|
inline mfem::Vector linear_combination(
|
||||||
|
const mfem::Vector &first,
|
||||||
const double first_scale,
|
const double first_scale,
|
||||||
const mfem::Vector &second,
|
const mfem::Vector &second,
|
||||||
const double second_scale) {
|
const double second_scale
|
||||||
MFEM_VERIFY(first.Size() == second.Size(),
|
) {
|
||||||
"Cannot combine vectors with different sizes.");
|
MFEM_VERIFY(first.Size() == second.Size(), "Cannot combine vectors with different sizes.");
|
||||||
|
|
||||||
mfem::Vector combination(first);
|
mfem::Vector combination(first);
|
||||||
combination *= first_scale;
|
combination *= first_scale;
|
||||||
combination.Add(second_scale, second);
|
combination.Add(second_scale, second);
|
||||||
return combination;
|
return combination;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double global_norm(const mfem::Vector &vector, MPI_Comm communicator) {
|
inline double global_norm(
|
||||||
|
const mfem::Vector &vector,
|
||||||
|
MPI_Comm communicator
|
||||||
|
) {
|
||||||
const double local_norm_squared = vector * vector;
|
const double local_norm_squared = vector * vector;
|
||||||
double global_norm_squared = 0.0;
|
double global_norm_squared = 0.0;
|
||||||
MPI_Allreduce(&local_norm_squared, &global_norm_squared, 1, MPI_DOUBLE,
|
MPI_Allreduce(&local_norm_squared, &global_norm_squared, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||||
MPI_SUM, communicator);
|
|
||||||
return std::sqrt(global_norm_squared);
|
return std::sqrt(global_norm_squared);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double global_dot(const mfem::Vector &first, const mfem::Vector &second,
|
inline double global_dot(
|
||||||
MPI_Comm communicator) {
|
const mfem::Vector &first,
|
||||||
MFEM_VERIFY(first.Size() == second.Size(),
|
const mfem::Vector &second,
|
||||||
"Cannot take the dot product of vectors with different sizes.");
|
MPI_Comm communicator
|
||||||
|
) {
|
||||||
|
MFEM_VERIFY(first.Size() == second.Size(), "Cannot take the dot product of vectors with different sizes.");
|
||||||
|
|
||||||
const double local_dot = first * second;
|
const double local_dot = first * second;
|
||||||
double global_dot = 0.0;
|
double global_dot = 0.0;
|
||||||
MPI_Allreduce(&local_dot, &global_dot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
MPI_Allreduce(&local_dot, &global_dot, 1, MPI_DOUBLE, MPI_SUM, communicator);
|
||||||
return global_dot;
|
return global_dot;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double relative_error(const mfem::Vector &computed,
|
inline double relative_error(
|
||||||
|
const mfem::Vector &computed,
|
||||||
const mfem::Vector &reference,
|
const mfem::Vector &reference,
|
||||||
MPI_Comm communicator) {
|
MPI_Comm communicator
|
||||||
MFEM_VERIFY(computed.Size() == reference.Size(),
|
) {
|
||||||
"Cannot compare vectors with different sizes.");
|
MFEM_VERIFY(computed.Size() == reference.Size(), "Cannot compare vectors with different sizes.");
|
||||||
|
|
||||||
mfem::Vector difference(computed);
|
mfem::Vector difference(computed);
|
||||||
difference -= reference;
|
difference -= reference;
|
||||||
|
|
||||||
return global_norm(difference, communicator) /
|
return global_norm(difference, communicator) /
|
||||||
std::max(global_norm(reference, communicator),
|
std::max(global_norm(reference, communicator), std::numeric_limits<double>::epsilon());
|
||||||
std::numeric_limits<double>::epsilon());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
inline double relative_scalar_error(const double computed,
|
inline double relative_scalar_error(
|
||||||
const double reference) {
|
const double computed,
|
||||||
return std::abs(computed - reference) /
|
const double reference
|
||||||
std::max(std::abs(reference), std::numeric_limits<double>::epsilon());
|
) {
|
||||||
}
|
return std::abs(computed - reference) / std::max(std::abs(reference), std::numeric_limits<double>::epsilon());
|
||||||
|
}
|
||||||
} // namespace gravity_prepared_test_utils
|
} // namespace gravity_prepared_test_utils
|
||||||
|
|
||||||
export namespace field_dof_test_utils {
|
export namespace field_dof_test_utils {
|
||||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||||
|
|
||||||
inline mean_field::mapping::DomainMapper make_domain_mapper() {
|
inline mean_field::mapping::DomainMapper make_domain_mapper() {
|
||||||
const mean_field::utils::Args args = test_utils::setup_args();
|
const mean_field::utils::Args args = test_utils::setup_args();
|
||||||
return mean_field::mapping::DomainMapper(
|
return mean_field::mapping::DomainMapper(
|
||||||
args.domain_mapper_options,
|
args.domain_mapper_options,
|
||||||
std::make_unique<const mean_field::mapping::compactification::
|
std::make_unique<const mean_field::mapping::compactification::KelvinCompactification>(args.kelvin_options)
|
||||||
KelvinCompactification>(args.kelvin_options));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr int vacuum_material_attribute =
|
inline constexpr int vacuum_material_attribute =
|
||||||
DomainSchema::template material_attribute<
|
DomainSchema::template material_attribute<mean_field::utils::domain::Vacuum>();
|
||||||
mean_field::utils::domain::Vacuum>();
|
|
||||||
|
|
||||||
template <typename FieldT>
|
template <typename FieldT>
|
||||||
inline mean_field::field::FieldDofMap
|
inline mean_field::field::FieldDofMap make_map(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||||
make_map(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
return mean_field::field::make_field_dof_map<FieldT, DomainSchema>(finiteElementSpace);
|
||||||
return mean_field::field::make_field_dof_map<FieldT, DomainSchema>(
|
}
|
||||||
finiteElementSpace);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FieldT>
|
template <typename FieldT>
|
||||||
inline mfem::Vector make_deterministic_supported_vector(
|
inline mfem::Vector make_deterministic_supported_vector(
|
||||||
const mfem::ParFiniteElementSpace &finiteElementSpace, const double phase) {
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||||
const mean_field::field::FieldDofMap map =
|
const double phase
|
||||||
make_map<FieldT>(finiteElementSpace);
|
) {
|
||||||
const mfem::Vector full =
|
const mean_field::field::FieldDofMap map = make_map<FieldT>(finiteElementSpace);
|
||||||
gravity_prepared_test_utils::make_deterministic_vector(map.full_size(),
|
const mfem::Vector full = gravity_prepared_test_utils::make_deterministic_vector(map.full_size(), phase);
|
||||||
phase);
|
|
||||||
return map.gather(full);
|
return map.gather(full);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline mfem::Vector make_supported_displacement(const mean_field::fem::FEM &f,
|
inline mfem::Vector make_supported_displacement(
|
||||||
const double phase) {
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::field::FieldDofMap map =
|
const double phase
|
||||||
make_map<mean_field::field::Displacement>(*f.displacementFes);
|
) {
|
||||||
|
const mean_field::field::FieldDofMap map = make_map<mean_field::field::Displacement>(*f.displacementFes);
|
||||||
return map.gather(gravity_prepared_test_utils::make_displacement(f, phase));
|
return map.gather(gravity_prepared_test_utils::make_displacement(f, phase));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void apply_hydrostatic_reference(
|
inline void apply_hydrostatic_reference(
|
||||||
const mean_field::fem::FEM &f,
|
const mean_field::fem::FEM &f,
|
||||||
const mean_field::physics::RigidRotation &rotation,
|
const mean_field::physics::RigidRotation &rotation,
|
||||||
const mfem::Vector &enthalpy, const mfem::Vector &gravityPotential,
|
const mfem::Vector &enthalpy,
|
||||||
const mfem::Vector &displacement, const double bernoulliConstant,
|
const mfem::Vector &gravityPotential,
|
||||||
mfem::Vector &residual) {
|
const mfem::Vector &displacement,
|
||||||
const mean_field::field::FieldDofMap enthalpyMap =
|
const double bernoulliConstant,
|
||||||
make_map<mean_field::field::Enthalpy>(*f.enthalpyFes);
|
mfem::Vector &residual
|
||||||
|
) {
|
||||||
|
const mean_field::field::FieldDofMap enthalpyMap = make_map<mean_field::field::Enthalpy>(*f.enthalpyFes);
|
||||||
const mean_field::field::FieldDofMap gravityPotentialMap =
|
const mean_field::field::FieldDofMap gravityPotentialMap =
|
||||||
make_map<mean_field::field::Gravity>(*f.gravityPotentialFes);
|
make_map<mean_field::field::Gravity>(*f.gravityPotentialFes);
|
||||||
const mean_field::field::FieldDofMap displacementMap =
|
const mean_field::field::FieldDofMap displacementMap =
|
||||||
@@ -278,191 +293,189 @@ inline void apply_hydrostatic_reference(
|
|||||||
displacementMap.scatter(displacement, displacementTrue);
|
displacementMap.scatter(displacement, displacementTrue);
|
||||||
|
|
||||||
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
mean_field::operators::kernels::apply_hydrostatic_equilibrium(
|
||||||
f, *f.domainMapperStateless, rotation, enthalpyTrue, gravityPotentialTrue,
|
f, *f.domainMapperStateless, rotation, enthalpyTrue, gravityPotentialTrue, displacementTrue,
|
||||||
displacementTrue, bernoulliConstant, residualTrue);
|
bernoulliConstant, residualTrue
|
||||||
|
);
|
||||||
|
|
||||||
residual.SetSize(enthalpyMap.reduced_size());
|
residual.SetSize(enthalpyMap.reduced_size());
|
||||||
enthalpyMap.gather(residualTrue, residual);
|
enthalpyMap.gather(residualTrue, residual);
|
||||||
}
|
}
|
||||||
} // namespace field_dof_test_utils
|
} // namespace field_dof_test_utils
|
||||||
|
|
||||||
export namespace tags {
|
export namespace tags {
|
||||||
inline constexpr auto geometry = make_tag("geometry");
|
inline constexpr auto geometry = make_tag("geometry");
|
||||||
inline constexpr auto physics = make_tag("physics");
|
inline constexpr auto physics = make_tag("physics");
|
||||||
inline constexpr auto unit = make_tag("unit");
|
inline constexpr auto unit = make_tag("unit");
|
||||||
inline constexpr auto mesh = make_tag("mesh");
|
inline constexpr auto mesh = make_tag("mesh");
|
||||||
inline constexpr auto integration = make_tag("integration");
|
inline constexpr auto integration = make_tag("integration");
|
||||||
inline constexpr auto solver = make_tag("solver");
|
inline constexpr auto solver = make_tag("solver");
|
||||||
inline constexpr auto integrator = make_tag("integrator");
|
inline constexpr auto integrator = make_tag("integrator");
|
||||||
inline constexpr auto mapping = make_tag("mapping");
|
inline constexpr auto mapping = make_tag("mapping");
|
||||||
inline constexpr auto utils = make_tag("utils");
|
inline constexpr auto utils = make_tag("utils");
|
||||||
inline constexpr auto mfem_operators = make_tag("operators");
|
inline constexpr auto mfem_operators = make_tag("operators");
|
||||||
inline constexpr auto initialization = make_tag("initialization");
|
inline constexpr auto initialization = make_tag("initialization");
|
||||||
inline constexpr auto accuracy = make_tag("accuracy");
|
inline constexpr auto accuracy = make_tag("accuracy");
|
||||||
inline constexpr auto closure = make_tag("closure");
|
inline constexpr auto closure = make_tag("closure");
|
||||||
inline constexpr auto kernels = make_tag("kernels");
|
inline constexpr auto kernels = make_tag("kernels");
|
||||||
inline constexpr auto surface = make_tag("surface");
|
inline constexpr auto surface = make_tag("surface");
|
||||||
inline constexpr auto model = make_tag("model");
|
inline constexpr auto model = make_tag("model");
|
||||||
|
|
||||||
inline constexpr auto field = sub_tag(mesh & physics, "field");
|
inline constexpr auto field = sub_tag(mesh & physics, "field");
|
||||||
inline constexpr auto field_dof = field & make_tag("dof");
|
inline constexpr auto field_dof = field & make_tag("dof");
|
||||||
inline constexpr auto field_dof_unit = field_dof & unit;
|
inline constexpr auto field_dof_unit = field_dof & unit;
|
||||||
inline constexpr auto field_dof_integration = field_dof & integration;
|
inline constexpr auto field_dof_integration = field_dof & integration;
|
||||||
|
|
||||||
inline constexpr auto pressure = sub_tag(physics, "pressure");
|
inline constexpr auto pressure = sub_tag(physics, "pressure");
|
||||||
|
|
||||||
inline constexpr auto hydro = sub_tag(physics, "hydro");
|
inline constexpr auto hydro = sub_tag(physics, "hydro");
|
||||||
inline constexpr auto jacobian = sub_tag(integration & physics, "jacobian");
|
inline constexpr auto jacobian = sub_tag(integration & physics, "jacobian");
|
||||||
inline constexpr auto residuals = sub_tag(integration & physics, "residuals");
|
inline constexpr auto residuals = sub_tag(integration & physics, "residuals");
|
||||||
inline constexpr auto volume = sub_tag(mesh & geometry, "volume");
|
inline constexpr auto volume = sub_tag(mesh & geometry, "volume");
|
||||||
inline constexpr auto quadrature =
|
inline constexpr auto quadrature = sub_tag(mesh & geometry & solver, "quadrature");
|
||||||
sub_tag(mesh & geometry & solver, "quadrature");
|
inline constexpr auto convergence = sub_tag(solver, "convergence");
|
||||||
inline constexpr auto convergence = sub_tag(solver, "convergence");
|
inline constexpr auto transformations = sub_tag(mesh & geometry, "transformations");
|
||||||
inline constexpr auto transformations =
|
|
||||||
sub_tag(mesh & geometry, "transformations");
|
|
||||||
|
|
||||||
inline constexpr auto h_refinement =
|
inline constexpr auto h_refinement = sub_tag(mesh & convergence, "h_refinement");
|
||||||
sub_tag(mesh & convergence, "h_refinement");
|
inline constexpr auto p_refinement = sub_tag(mesh & convergence, "p_refinement");
|
||||||
inline constexpr auto p_refinement =
|
|
||||||
sub_tag(mesh & convergence, "p_refinement");
|
|
||||||
|
|
||||||
inline constexpr auto analytic_comparison =
|
inline constexpr auto analytic_comparison = sub_tag(solver & physics & residuals, "analytic_comparison");
|
||||||
sub_tag(solver & physics & residuals, "analytic_comparison");
|
inline constexpr auto self_consistency = sub_tag(solver & physics, "self_consistency");
|
||||||
inline constexpr auto self_consistency =
|
|
||||||
sub_tag(solver & physics, "self_consistency");
|
|
||||||
|
|
||||||
inline constexpr auto centrifugal = sub_tag(solver & physics, "centrifugal");
|
inline constexpr auto centrifugal = sub_tag(solver & physics, "centrifugal");
|
||||||
inline constexpr auto advection = sub_tag(solver & physics, "advection");
|
inline constexpr auto advection = sub_tag(solver & physics, "advection");
|
||||||
inline constexpr auto coriolis = sub_tag(solver & physics, "coriolis");
|
inline constexpr auto coriolis = sub_tag(solver & physics, "coriolis");
|
||||||
inline constexpr auto gravity = sub_tag(solver & physics, "gravity");
|
inline constexpr auto gravity = sub_tag(solver & physics, "gravity");
|
||||||
inline constexpr auto enthalpy = sub_tag(solver & physics, "enthalpy");
|
inline constexpr auto enthalpy = sub_tag(solver & physics, "enthalpy");
|
||||||
inline constexpr auto barotrope = sub_tag(physics, "barotrope");
|
inline constexpr auto barotrope = sub_tag(physics, "barotrope");
|
||||||
inline constexpr auto mass_continuity =
|
inline constexpr auto mass_continuity = sub_tag(solver & physics, "mass_continuity");
|
||||||
sub_tag(solver & physics, "mass_continuity");
|
inline constexpr auto pressure_gradient = sub_tag(solver & physics, "pressure_gradient");
|
||||||
inline constexpr auto pressure_gradient =
|
inline constexpr auto viscosity = sub_tag(solver & physics, "viscosity");
|
||||||
sub_tag(solver & physics, "pressure_gradient");
|
|
||||||
inline constexpr auto viscosity = sub_tag(solver & physics, "viscosity");
|
|
||||||
|
|
||||||
inline constexpr auto compactification =
|
inline constexpr auto compactification = sub_tag(mesh & mapping, "compactification");
|
||||||
sub_tag(mesh & mapping, "compactification");
|
inline constexpr auto kelvin = sub_tag(compactification, "kelvin");
|
||||||
inline constexpr auto kelvin = sub_tag(compactification, "kelvin");
|
inline constexpr auto mapping_evaluator = mapping & make_tag("grid_function_evaluator");
|
||||||
inline constexpr auto mapping_evaluator =
|
inline constexpr auto mapping_evaluator_unit = mapping_evaluator & unit;
|
||||||
mapping & make_tag("grid_function_evaluator");
|
|
||||||
inline constexpr auto mapping_evaluator_unit = mapping_evaluator & unit;
|
|
||||||
|
|
||||||
inline constexpr auto prepared = sub_tag(solver & physics, "prepared");
|
inline constexpr auto prepared = sub_tag(solver & physics, "prepared");
|
||||||
inline constexpr auto contexts = sub_tag(solver, "contexts");
|
inline constexpr auto contexts = sub_tag(solver, "contexts");
|
||||||
|
|
||||||
inline constexpr auto domain = sub_tag(mesh, "domain");
|
inline constexpr auto domain = sub_tag(mesh, "domain");
|
||||||
|
|
||||||
// Canonical gravity-suite tags. These intentionally compose leaf tags
|
// Canonical gravity-suite tags. These intentionally compose leaf tags
|
||||||
// exactly once so Catch2 output remains useful and free of repeated
|
// exactly once so Catch2 output remains useful and free of repeated
|
||||||
// [solver]/[physics] entries inherited from older composite tags.
|
// [solver]/[physics] entries inherited from older composite tags.
|
||||||
inline constexpr auto gravity_unit = gravity & unit;
|
inline constexpr auto gravity_unit = gravity & unit;
|
||||||
inline constexpr auto gravity_integration = gravity & integration;
|
inline constexpr auto gravity_integration = gravity & integration;
|
||||||
inline constexpr auto gravity_operator = gravity & mfem_operators;
|
inline constexpr auto gravity_operator = gravity & mfem_operators;
|
||||||
inline constexpr auto gravity_prepared = gravity & make_tag("prepared");
|
inline constexpr auto gravity_prepared = gravity & make_tag("prepared");
|
||||||
inline constexpr auto gravity_context = gravity & make_tag("context");
|
inline constexpr auto gravity_context = gravity & make_tag("context");
|
||||||
inline constexpr auto gravity_kernel = gravity & kernels;
|
inline constexpr auto gravity_kernel = gravity & kernels;
|
||||||
inline constexpr auto gravity_accuracy = gravity & accuracy;
|
inline constexpr auto gravity_accuracy = gravity & accuracy;
|
||||||
inline constexpr auto gravity_operator_unit = gravity_operator & unit;
|
inline constexpr auto gravity_operator_unit = gravity_operator & unit;
|
||||||
inline constexpr auto gravity_operator_integration =
|
inline constexpr auto gravity_operator_integration = gravity_operator & integration;
|
||||||
gravity_operator & integration;
|
inline constexpr auto gravity_operator_convergence = gravity_operator & integration & make_tag("convergence");
|
||||||
inline constexpr auto gravity_operator_convergence =
|
inline constexpr auto gravity_analytic = gravity & integration & make_tag("analytic_comparison");
|
||||||
gravity_operator & integration & make_tag("convergence");
|
inline constexpr auto gravity_consistency = gravity & integration & make_tag("self_consistency");
|
||||||
inline constexpr auto gravity_analytic =
|
inline constexpr auto gravity_prepared_jacobian = gravity_prepared & integration & make_tag("jacobian");
|
||||||
gravity & integration & make_tag("analytic_comparison");
|
inline constexpr auto gravity_prepared_unit = gravity_prepared & unit;
|
||||||
inline constexpr auto gravity_consistency =
|
inline constexpr auto gravity_prepared_jacobian_accuracy = gravity_prepared_jacobian & accuracy;
|
||||||
gravity & integration & make_tag("self_consistency");
|
inline constexpr auto gravity_kernel_accuracy = gravity_kernel & accuracy;
|
||||||
inline constexpr auto gravity_prepared_jacobian =
|
inline constexpr auto gravity_kernel_integration = gravity_kernel & integration;
|
||||||
gravity_prepared & integration & make_tag("jacobian");
|
inline constexpr auto gravity_kernel_convergence = gravity_kernel & integration & make_tag("convergence");
|
||||||
inline constexpr auto gravity_prepared_unit = gravity_prepared & unit;
|
inline constexpr auto gravity_analytic_accuracy = gravity_analytic & accuracy;
|
||||||
inline constexpr auto gravity_prepared_jacobian_accuracy =
|
inline constexpr auto gravity_consistency_accuracy = gravity_consistency & accuracy;
|
||||||
gravity_prepared_jacobian & accuracy;
|
inline constexpr auto gravity_integrator_unit = gravity & integrator & unit;
|
||||||
inline constexpr auto gravity_kernel_accuracy = gravity_kernel & accuracy;
|
|
||||||
inline constexpr auto gravity_kernel_integration = gravity_kernel & integration;
|
|
||||||
inline constexpr auto gravity_kernel_convergence =
|
|
||||||
gravity_kernel & integration & make_tag("convergence");
|
|
||||||
inline constexpr auto gravity_analytic_accuracy = gravity_analytic & accuracy;
|
|
||||||
inline constexpr auto gravity_consistency_accuracy =
|
|
||||||
gravity_consistency & accuracy;
|
|
||||||
inline constexpr auto gravity_integrator_unit = gravity & integrator & unit;
|
|
||||||
|
|
||||||
inline constexpr auto barotrope_prepared =
|
inline constexpr auto barotrope_prepared = barotrope & solver & make_tag("prepared");
|
||||||
barotrope & solver & make_tag("prepared");
|
inline constexpr auto barotrope_eos_unit = barotrope & unit & make_tag("eos");
|
||||||
inline constexpr auto barotrope_eos_unit = barotrope & unit & make_tag("eos");
|
inline constexpr auto barotrope_eos_jacobian = barotrope_eos_unit & integration & make_tag("jacobian");
|
||||||
inline constexpr auto barotrope_eos_jacobian =
|
inline constexpr auto polytropic_eos_characterization =
|
||||||
barotrope_eos_unit & integration & make_tag("jacobian");
|
barotrope & unit & make_tag("eos") & make_tag("characterization");
|
||||||
inline constexpr auto barotrope_pressure_quadrature =
|
inline constexpr auto polytropic_eos_relation_contract =
|
||||||
barotrope & mesh & geometry & solver & make_tag("pressure") &
|
barotrope & unit & make_tag("eos") & make_tag("relation_contract");
|
||||||
|
inline constexpr auto polytropic_eos_compatibility = barotrope & unit & make_tag("eos") & make_tag("compatibility");
|
||||||
|
inline constexpr auto equation_of_state = physics & make_tag("eos");
|
||||||
|
inline constexpr auto equation_of_state_type_system = equation_of_state & unit & make_tag("type_system");
|
||||||
|
inline constexpr auto equation_of_state_quantity_types = equation_of_state_type_system & make_tag("quantity_types");
|
||||||
|
inline constexpr auto equation_of_state_relation_contract =
|
||||||
|
equation_of_state_type_system & make_tag("relation_contract");
|
||||||
|
inline constexpr auto equation_of_state_runtime_view = equation_of_state & unit & make_tag("runtime_view");
|
||||||
|
inline constexpr auto equation_of_state_runtime_contract =
|
||||||
|
equation_of_state_runtime_view & make_tag("relation_contract");
|
||||||
|
inline constexpr auto equation_of_state_runtime_compatibility =
|
||||||
|
equation_of_state_runtime_view & make_tag("compatibility");
|
||||||
|
inline constexpr auto equation_of_state_consumer_contract =
|
||||||
|
equation_of_state & unit & make_tag("consumer_contract");
|
||||||
|
inline constexpr auto barotropic_closure_equation_of_state_contract =
|
||||||
|
equation_of_state_consumer_contract & make_tag("barotropic_closure");
|
||||||
|
inline constexpr auto pressure_force_equation_of_state_contract =
|
||||||
|
equation_of_state_consumer_contract & make_tag("pressure_force");
|
||||||
|
inline constexpr auto structure_seed_equation_of_state_contract =
|
||||||
|
equation_of_state_consumer_contract & make_tag("structure_seed");
|
||||||
|
inline constexpr auto stellar_model_type_contract = barotrope & model & unit & make_tag("type_contract");
|
||||||
|
inline constexpr auto stellar_model_runtime_view = barotrope & model & unit & make_tag("runtime_view");
|
||||||
|
inline constexpr auto surface_prescription_type_contract =
|
||||||
|
surface & physics & unit & make_tag("prescription") & make_tag("type_contract");
|
||||||
|
inline constexpr auto surface_constraint_compilation =
|
||||||
|
surface & physics & unit & make_tag("constraint_compilation");
|
||||||
|
inline constexpr auto surface_constraint_jacobian = surface_constraint_compilation & jacobian;
|
||||||
|
inline constexpr auto surface_constraint_lifetime = surface & model & unit & make_tag("constraint_lifetime");
|
||||||
|
inline constexpr auto surface_boundary_dof_topology =
|
||||||
|
surface & field_dof & integration & make_tag("boundary_topology");
|
||||||
|
inline constexpr auto surface_row_replacement =
|
||||||
|
surface & barotrope_prepared & integration & make_tag("row_replacement");
|
||||||
|
inline constexpr auto translational_centering = geometry & solver & make_tag("translational_centering");
|
||||||
|
inline constexpr auto translational_centering_topology =
|
||||||
|
translational_centering & field_dof & integration & make_tag("point_topology");
|
||||||
|
inline constexpr auto translational_centering_enforcement =
|
||||||
|
translational_centering & barotrope_prepared & integration & make_tag("row_replacement");
|
||||||
|
inline constexpr auto barotrope_pressure_quadrature = barotrope & mesh & geometry & solver & make_tag("pressure") &
|
||||||
make_tag("pressure_gradient") & make_tag("quadrature");
|
make_tag("pressure_gradient") & make_tag("quadrature");
|
||||||
inline constexpr auto barotrope_pressure_quadrature_unit =
|
inline constexpr auto barotrope_pressure_quadrature_unit = barotrope_pressure_quadrature & unit;
|
||||||
barotrope_pressure_quadrature & unit;
|
inline constexpr auto barotrope_pressure_quadrature_accuracy = barotrope_pressure_quadrature & accuracy;
|
||||||
inline constexpr auto barotrope_pressure_quadrature_accuracy =
|
inline constexpr auto barotrope_prepared_jacobian = barotrope_prepared & integration & make_tag("jacobian");
|
||||||
barotrope_pressure_quadrature & accuracy;
|
inline constexpr auto barotrope_context = barotrope & solver & make_tag("context");
|
||||||
inline constexpr auto barotrope_prepared_jacobian =
|
inline constexpr auto barotrope_context_integration = barotrope_context & integration;
|
||||||
barotrope_prepared & integration & make_tag("jacobian");
|
inline constexpr auto barotrope_prepared_analytic =
|
||||||
inline constexpr auto barotrope_context =
|
|
||||||
barotrope & solver & make_tag("context");
|
|
||||||
inline constexpr auto barotrope_context_integration =
|
|
||||||
barotrope_context & integration;
|
|
||||||
inline constexpr auto barotrope_prepared_analytic =
|
|
||||||
barotrope_prepared & integration & make_tag("analytic_comparison");
|
barotrope_prepared & integration & make_tag("analytic_comparison");
|
||||||
inline constexpr auto barotrope_prepared_jacobian_accuracy =
|
inline constexpr auto barotrope_prepared_jacobian_accuracy = barotrope_prepared_jacobian & accuracy;
|
||||||
barotrope_prepared_jacobian & accuracy;
|
inline constexpr auto barotrope_prepared_jacobian_geometry = barotrope_prepared_jacobian & geometry;
|
||||||
inline constexpr auto barotrope_prepared_jacobian_geometry =
|
inline constexpr auto barotrope_prepared_jacobian_unit = barotrope_prepared_jacobian & unit;
|
||||||
barotrope_prepared_jacobian & geometry;
|
|
||||||
inline constexpr auto barotrope_prepared_jacobian_unit =
|
|
||||||
barotrope_prepared_jacobian & unit;
|
|
||||||
|
|
||||||
// Canonical hydrostatic-suite tags. The leaf tags are composed directly
|
// Canonical hydrostatic-suite tags. The leaf tags are composed directly
|
||||||
// so inherited [physics]/[solver] tags appear only once.
|
// so inherited [physics]/[solver] tags appear only once.
|
||||||
inline constexpr auto barotrope_hydrostatic =
|
inline constexpr auto barotrope_hydrostatic = barotrope & solver & make_tag("hydro");
|
||||||
barotrope & solver & make_tag("hydro");
|
inline constexpr auto barotrope_hydrostatic_context = barotrope_hydrostatic & make_tag("context");
|
||||||
inline constexpr auto barotrope_hydrostatic_context =
|
inline constexpr auto barotrope_hydrostatic_prepared = barotrope_hydrostatic & make_tag("prepared");
|
||||||
barotrope_hydrostatic & make_tag("context");
|
inline constexpr auto barotrope_hydrostatic_prepared_residual =
|
||||||
inline constexpr auto barotrope_hydrostatic_prepared =
|
|
||||||
barotrope_hydrostatic & make_tag("prepared");
|
|
||||||
inline constexpr auto barotrope_hydrostatic_prepared_residual =
|
|
||||||
barotrope_hydrostatic_prepared & integration & make_tag("residual");
|
barotrope_hydrostatic_prepared & integration & make_tag("residual");
|
||||||
inline constexpr auto barotrope_hydrostatic_prepared_jacobian =
|
inline constexpr auto barotrope_hydrostatic_prepared_jacobian =
|
||||||
barotrope_hydrostatic_prepared & integration & make_tag("jacobian");
|
barotrope_hydrostatic_prepared & integration & make_tag("jacobian");
|
||||||
inline constexpr auto barotrope_hydrostatic_prepared_analytic =
|
inline constexpr auto barotrope_hydrostatic_prepared_analytic =
|
||||||
barotrope_hydrostatic_prepared & integration &
|
barotrope_hydrostatic_prepared & integration & make_tag("analytic_comparison");
|
||||||
make_tag("analytic_comparison");
|
|
||||||
|
|
||||||
inline constexpr auto barotrope_mass_normalization =
|
inline constexpr auto barotrope_mass_normalization = barotrope & solver & make_tag("mass_normalization");
|
||||||
barotrope & solver & make_tag("mass_normalization");
|
inline constexpr auto barotrope_mass_normalization_context = barotrope_mass_normalization & make_tag("context");
|
||||||
inline constexpr auto barotrope_mass_normalization_context =
|
inline constexpr auto barotrope_mass_normalization_prepared = barotrope_mass_normalization & make_tag("prepared");
|
||||||
barotrope_mass_normalization & make_tag("context");
|
inline constexpr auto barotrope_mass_normalization_jacobian =
|
||||||
inline constexpr auto barotrope_mass_normalization_prepared =
|
|
||||||
barotrope_mass_normalization & make_tag("prepared");
|
|
||||||
inline constexpr auto barotrope_mass_normalization_jacobian =
|
|
||||||
barotrope_mass_normalization_prepared & integration & make_tag("jacobian");
|
barotrope_mass_normalization_prepared & integration & make_tag("jacobian");
|
||||||
inline constexpr auto barotrope_mass_normalization_analytic =
|
inline constexpr auto barotrope_mass_normalization_analytic =
|
||||||
barotrope_mass_normalization_prepared & integration &
|
barotrope_mass_normalization_prepared & integration & make_tag("analytic_comparison");
|
||||||
make_tag("analytic_comparison");
|
|
||||||
|
|
||||||
inline constexpr auto rotation_prepared = centrifugal & make_tag("prepared");
|
inline constexpr auto rotation_prepared = centrifugal & make_tag("prepared");
|
||||||
inline constexpr auto rotation_context = centrifugal & make_tag("context");
|
inline constexpr auto rotation_context = centrifugal & make_tag("context");
|
||||||
inline constexpr auto rotation_analytic =
|
inline constexpr auto rotation_analytic = centrifugal & integration & make_tag("analytic_comparison");
|
||||||
centrifugal & integration & make_tag("analytic_comparison");
|
inline constexpr auto rotation_context_unit = rotation_context & unit;
|
||||||
inline constexpr auto rotation_context_unit = rotation_context & unit;
|
inline constexpr auto rotation_prepared_unit = rotation_prepared & unit;
|
||||||
inline constexpr auto rotation_prepared_unit = rotation_prepared & unit;
|
inline constexpr auto rotation_prepared_jacobian = rotation_prepared & integration & make_tag("jacobian");
|
||||||
inline constexpr auto rotation_prepared_jacobian =
|
inline constexpr auto rotation_prepared_jacobian_accuracy = rotation_prepared_jacobian & accuracy;
|
||||||
rotation_prepared & integration & make_tag("jacobian");
|
inline constexpr auto rotation_kernel_accuracy = centrifugal & kernels & accuracy;
|
||||||
inline constexpr auto rotation_prepared_jacobian_accuracy =
|
inline constexpr auto rotation_integrator_unit = centrifugal & integrator & unit;
|
||||||
rotation_prepared_jacobian & accuracy;
|
inline constexpr auto rotation_integrator_integration = centrifugal & integrator & integration;
|
||||||
inline constexpr auto rotation_kernel_accuracy =
|
inline constexpr auto rotation_integrator_convergence =
|
||||||
centrifugal & kernels & accuracy;
|
|
||||||
inline constexpr auto rotation_integrator_unit = centrifugal & integrator & unit;
|
|
||||||
inline constexpr auto rotation_integrator_integration =
|
|
||||||
centrifugal & integrator & integration;
|
|
||||||
inline constexpr auto rotation_integrator_convergence =
|
|
||||||
rotation_integrator_integration & convergence & h_refinement;
|
rotation_integrator_integration & convergence & h_refinement;
|
||||||
inline constexpr auto rotation_analytic_unit = rotation_analytic & unit;
|
inline constexpr auto rotation_analytic_unit = rotation_analytic & unit;
|
||||||
inline constexpr auto rotation_analytic_accuracy = rotation_analytic & accuracy;
|
inline constexpr auto rotation_analytic_accuracy = rotation_analytic & accuracy;
|
||||||
inline constexpr auto rotation_analytic_accuracy_geometry =
|
inline constexpr auto rotation_analytic_accuracy_geometry = rotation_analytic_accuracy & geometry;
|
||||||
rotation_analytic_accuracy & geometry;
|
|
||||||
|
|
||||||
} // namespace tags
|
} // namespace tags
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <catch2/catch_test_case_info.hpp>
|
#include <catch2/catch_test_case_info.hpp>
|
||||||
#include <catch2/reporters/catch_reporter_registrars.hpp>
|
#include <catch2/reporters/catch_reporter_registrars.hpp>
|
||||||
#include <catch2/reporters/catch_reporter_streaming_base.hpp>
|
#include <catch2/reporters/catch_reporter_streaming_base.hpp>
|
||||||
|
#include <chrono>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -223,6 +224,7 @@ class CheckReporter : public Catch::StreamingReporterBase {
|
|||||||
bool passed;
|
bool passed;
|
||||||
std::size_t assertionsPassed;
|
std::size_t assertionsPassed;
|
||||||
std::size_t assertionsFailed;
|
std::size_t assertionsFailed;
|
||||||
|
double durationSeconds;
|
||||||
std::vector<std::string> failureMessages;
|
std::vector<std::string> failureMessages;
|
||||||
std::vector<std::string> infoMessages;
|
std::vector<std::string> infoMessages;
|
||||||
};
|
};
|
||||||
@@ -231,6 +233,7 @@ class CheckReporter : public Catch::StreamingReporterBase {
|
|||||||
std::vector<std::string> m_currentInfos;
|
std::vector<std::string> m_currentInfos;
|
||||||
std::unordered_set<unsigned int> m_currentInfoSequences;
|
std::unordered_set<unsigned int> m_currentInfoSequences;
|
||||||
std::vector<TestCaseData> m_testRunData;
|
std::vector<TestCaseData> m_testRunData;
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> m_testStartTime;
|
||||||
|
|
||||||
void captureInfoMessages(Catch::AssertionStats const &assertionStats) {
|
void captureInfoMessages(Catch::AssertionStats const &assertionStats) {
|
||||||
for (auto const &message : assertionStats.infoMessages) {
|
for (auto const &message : assertionStats.infoMessages) {
|
||||||
@@ -253,9 +256,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string getDescription() {
|
static std::string getDescription() {
|
||||||
return "Console reporter with wrapping, tags, and collapsible HTML "
|
return "Console reporter with wrapping, tags, live test progress, and collapsible HTML "
|
||||||
"export "
|
"export with ANSI color rendering.";
|
||||||
"with ANSI color rendering.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testRunStarting(Catch::TestRunInfo const &_testRunInfo) override {
|
void testRunStarting(Catch::TestRunInfo const &_testRunInfo) override {
|
||||||
@@ -263,8 +265,20 @@ public:
|
|||||||
|
|
||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
std::cout << std::left << std::setw(85) << "Test Case Name"
|
std::cout << std::left << std::setw(85) << "Test Case Name"
|
||||||
<< "Status " << std::right << std::setw(8) << "Passed" << std::setw(8) << "Failed" << '\n';
|
<< "Status " << std::right << std::setw(8) << "Passed" << std::setw(8) << "Failed" << std::setw(12)
|
||||||
std::cout << std::string(121, '-') << '\n';
|
<< "Time (s)" << '\n';
|
||||||
|
std::cout << std::string(133, '-') << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCaseStarting(Catch::TestCaseInfo const &testInfo) override {
|
||||||
|
StreamingReporterBase::testCaseStarting(testInfo);
|
||||||
|
|
||||||
|
m_testStartTime = std::chrono::steady_clock::now();
|
||||||
|
std::string name = testInfo.name;
|
||||||
|
auto wrappedName = wrapText(name, 83);
|
||||||
|
|
||||||
|
// Print progress line, \r to overwrite later, \033[K to clear till end of line
|
||||||
|
std::cout << "\r\033[K" << std::left << std::setw(85) << (wrappedName[0] + " ...") << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
void assertionEnded(Catch::AssertionStats const &assertionStats) override {
|
void assertionEnded(Catch::AssertionStats const &assertionStats) override {
|
||||||
@@ -300,14 +314,20 @@ public:
|
|||||||
void testCaseEnded(Catch::TestCaseStats const &stats) override {
|
void testCaseEnded(Catch::TestCaseStats const &stats) override {
|
||||||
StreamingReporterBase::testCaseEnded(stats);
|
StreamingReporterBase::testCaseEnded(stats);
|
||||||
|
|
||||||
|
auto endTime = std::chrono::steady_clock::now();
|
||||||
|
std::chrono::duration<double> elapsed = endTime - m_testStartTime;
|
||||||
|
double duration_s = elapsed.count();
|
||||||
|
|
||||||
bool passed = stats.totals.assertions.allPassed();
|
bool passed = stats.totals.assertions.allPassed();
|
||||||
std::string mark = passed ? "\033[32m✓\033[0m" : "\033[31m✗\033[0m";
|
std::string mark = passed ? "\033[32m✓\033[0m" : "\033[31m✗\033[0m";
|
||||||
|
|
||||||
std::string name = stats.testInfo->name;
|
std::string name = stats.testInfo->name;
|
||||||
auto wrappedName = wrapText(name, 83);
|
auto wrappedName = wrapText(name, 83);
|
||||||
|
|
||||||
std::cout << std::left << std::setw(85) << wrappedName[0] << mark << " " << std::right << std::setw(8)
|
// Overwrite the loading line with the actual result
|
||||||
<< stats.totals.assertions.passed << std::setw(8) << stats.totals.assertions.failed << '\n';
|
std::cout << "\r\033[K" << std::left << std::setw(85) << wrappedName[0] << mark << " " << std::right
|
||||||
|
<< std::setw(8) << stats.totals.assertions.passed << std::setw(8) << stats.totals.assertions.failed
|
||||||
|
<< std::setw(11) << std::fixed << std::setprecision(3) << duration_s << "s\n";
|
||||||
|
|
||||||
for (size_t i = 1; i < wrappedName.size(); ++i) {
|
for (size_t i = 1; i < wrappedName.size(); ++i) {
|
||||||
std::cout << " \033[90m↳ \033[0m" // Dim indent arrow
|
std::cout << " \033[90m↳ \033[0m" // Dim indent arrow
|
||||||
@@ -327,12 +347,12 @@ public:
|
|||||||
for (auto const &failure : m_currentFailures) {
|
for (auto const &failure : m_currentFailures) {
|
||||||
std::cout << failure << '\n';
|
std::cout << failure << '\n';
|
||||||
}
|
}
|
||||||
std::cout << std::string(121, '-') << '\n';
|
std::cout << std::string(133, '-') << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
m_testRunData.push_back(
|
m_testRunData.push_back(
|
||||||
{name, tagsStr, passed, stats.totals.assertions.passed, stats.totals.assertions.failed, m_currentFailures,
|
{name, tagsStr, passed, stats.totals.assertions.passed, stats.totals.assertions.failed, duration_s,
|
||||||
m_currentInfos}
|
m_currentFailures, m_currentInfos}
|
||||||
);
|
);
|
||||||
|
|
||||||
m_currentFailures.clear();
|
m_currentFailures.clear();
|
||||||
@@ -343,7 +363,7 @@ public:
|
|||||||
void testRunEnded(Catch::TestRunStats const &_testRunStats) override {
|
void testRunEnded(Catch::TestRunStats const &_testRunStats) override {
|
||||||
StreamingReporterBase::testRunEnded(_testRunStats);
|
StreamingReporterBase::testRunEnded(_testRunStats);
|
||||||
|
|
||||||
std::cout << std::string(121, '=') << '\n';
|
std::cout << std::string(133, '=') << '\n';
|
||||||
|
|
||||||
auto const &tc = _testRunStats.totals.testCases;
|
auto const &tc = _testRunStats.totals.testCases;
|
||||||
auto const &as = _testRunStats.totals.assertions;
|
auto const &as = _testRunStats.totals.assertions;
|
||||||
@@ -444,7 +464,9 @@ private:
|
|||||||
html << " </div>\n";
|
html << " </div>\n";
|
||||||
html << " <div class='stats'>\n";
|
html << " <div class='stats'>\n";
|
||||||
html << " <span class='text-green'>✓ " << test.assertionsPassed << "</span> | ";
|
html << " <span class='text-green'>✓ " << test.assertionsPassed << "</span> | ";
|
||||||
html << " <span class='text-red'>✗ " << test.assertionsFailed << "</span>\n";
|
html << " <span class='text-red'>✗ " << test.assertionsFailed << "</span> | ";
|
||||||
|
html << " <span style='color: #34495e;'>⌛ " << std::fixed << std::setprecision(3)
|
||||||
|
<< test.durationSeconds << "s</span>\n";
|
||||||
html << " </div>\n";
|
html << " </div>\n";
|
||||||
html << " </div>\n";
|
html << " </div>\n";
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user