perf(jacobian-action): major updates to jacobian action application by removing redudant quadrature work. ~5x increase in speed
This commit is contained in:
120
libmeanfield/interface/seed/lane_emden.cppm
Normal file
120
libmeanfield/interface/seed/lane_emden.cppm
Normal file
@@ -0,0 +1,120 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:seed.lane_emden;
|
||||
|
||||
export import :dimensions.quantities;
|
||||
export import :eos.polytrope;
|
||||
export import :model.typed_stellar;
|
||||
|
||||
export namespace mean_field::seed {
|
||||
struct DimensionlessLaneEmdenSolution final {
|
||||
mfem::Vector coordinate;
|
||||
mfem::Vector theta;
|
||||
mfem::Vector thetaDerivative;
|
||||
std::optional<double> firstZeroCoordinate;
|
||||
};
|
||||
|
||||
/*
|
||||
* Integrate the dimensionless Lane-Emden equation from the regular center
|
||||
* to either the first zero of theta or coordinateLimit, whichever occurs
|
||||
* first. This numerical kernel also supports the n = 0 and n = 5 analytic
|
||||
* benchmark cases even though they do not both define admissible seeds for
|
||||
* the current Polytrope EOS and finite stellar domain.
|
||||
*/
|
||||
[[nodiscard]] DimensionlessLaneEmdenSolution integrateLaneEmden(
|
||||
double polytropicIndex,
|
||||
double coordinateLimit,
|
||||
double integrationStep = 1.0e-3
|
||||
);
|
||||
|
||||
struct RadialProfile final {
|
||||
mfem::Vector radius;
|
||||
mfem::Vector density;
|
||||
mfem::Vector specificEnthalpy;
|
||||
|
||||
dimensions::LengthValue stellarRadius;
|
||||
dimensions::DensityValue centralDensity;
|
||||
dimensions::SpecificEnthalpyValue centralSpecificEnthalpy;
|
||||
};
|
||||
|
||||
class LaneEmden final {
|
||||
public:
|
||||
struct Parameters final {
|
||||
std::optional<dimensions::DensityValue> centralDensity{std::nullopt};
|
||||
int radialSampleCount{512};
|
||||
};
|
||||
|
||||
LaneEmden()
|
||||
: m_centralDensity(std::nullopt),
|
||||
m_radialSampleCount(512) {
|
||||
}
|
||||
|
||||
explicit LaneEmden(const Parameters parameters)
|
||||
: m_centralDensity(parameters.centralDensity),
|
||||
m_radialSampleCount(parameters.radialSampleCount) {
|
||||
if (m_centralDensity.has_value() &&
|
||||
(!std::isfinite(m_centralDensity->value()) || m_centralDensity->value() <= 0.0)) {
|
||||
throw std::invalid_argument("A Lane-Emden seed central density must be finite and positive.");
|
||||
}
|
||||
if (m_radialSampleCount < 2) {
|
||||
throw std::invalid_argument("A Lane-Emden seed requires at least two radial samples.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<dimensions::DensityValue> ¢ralDensity() const noexcept {
|
||||
return m_centralDensity;
|
||||
}
|
||||
|
||||
[[nodiscard]] int radialSampleCount() const noexcept {
|
||||
return m_radialSampleCount;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<dimensions::DensityValue> m_centralDensity;
|
||||
int m_radialSampleCount;
|
||||
};
|
||||
|
||||
[[nodiscard]] RadialProfile generateLaneEmdenProfile(
|
||||
const eos::Polytrope &equationOfState,
|
||||
dimensions::DensityValue centralDensity,
|
||||
int radialSampleCount
|
||||
);
|
||||
|
||||
template <model::StellarModelType Model>
|
||||
requires std::remove_cvref_t<Model>::template
|
||||
containsSpecification<eos::Polytrope> [[nodiscard]] RadialProfile generateRadialProfile(
|
||||
const Model &stellarModel,
|
||||
const LaneEmden &strategy
|
||||
) {
|
||||
std::optional<dimensions::DensityValue> centralDensity = strategy.centralDensity();
|
||||
|
||||
if (!centralDensity.has_value()) {
|
||||
if constexpr (std::remove_cvref_t<Model>::template containsSpecification<models::FixedCentralDensity>) {
|
||||
centralDensity = stellarModel.template specification<models::FixedCentralDensity>().targetDensity();
|
||||
} else {
|
||||
throw std::invalid_argument(
|
||||
"Lane-Emden seed generation requires either FixedCentralDensity or an explicit seed-only central "
|
||||
"density."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return generateLaneEmdenProfile(
|
||||
stellarModel.template specification<eos::Polytrope>(), *centralDensity, strategy.radialSampleCount()
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Strategy, typename Model>
|
||||
concept RadialSeedStrategyFor = requires(const Model &stellarModel, const Strategy &strategy) {
|
||||
{ generateRadialProfile(stellarModel, strategy) } -> std::same_as<RadialProfile>;
|
||||
};
|
||||
} // namespace mean_field::seed
|
||||
136
libmeanfield/interface/seed/stellar_equilibrium_projection.cppm
Normal file
136
libmeanfield/interface/seed/stellar_equilibrium_projection.cppm
Normal file
@@ -0,0 +1,136 @@
|
||||
module;
|
||||
|
||||
#include <concepts>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:seed.stellar_equilibrium_projection;
|
||||
|
||||
export import :operators.stellar_equilibrium_problem;
|
||||
export import :physics.gravity;
|
||||
export import :seed.lane_emden;
|
||||
|
||||
export namespace mean_field::seed {
|
||||
struct StellarEquilibriumProjectionOptions final {
|
||||
physics::GravitySolveOptions gravity{};
|
||||
double surfaceRadiusRelativeTolerance{5.0e-4};
|
||||
};
|
||||
|
||||
template <equilibrium::StellarEquilibriumModel Model> struct ProjectedEquilibriumState final {
|
||||
using ModelType = std::remove_cvref_t<Model>;
|
||||
|
||||
mfem::Vector values;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct ProjectedRadialFields final {
|
||||
mfem::Vector density;
|
||||
mfem::Vector gravityGradient;
|
||||
mfem::Vector gravityPotential;
|
||||
mfem::Vector specificEnthalpy;
|
||||
double bernoulliConstant;
|
||||
};
|
||||
|
||||
[[nodiscard]] ProjectedRadialFields projectRadialFields(
|
||||
const equilibrium::StellarDiscretization &discretization,
|
||||
const RadialProfile &profile,
|
||||
dimensions::MassValue targetMass,
|
||||
dimensions::PressureValue targetSurfacePressure,
|
||||
const StellarEquilibriumProjectionOptions &options
|
||||
);
|
||||
|
||||
inline void assignProjectedBlock(
|
||||
mfem::Vector destination,
|
||||
const mfem::Vector &source,
|
||||
const char *name
|
||||
) {
|
||||
if (destination.Size() != source.Size()) {
|
||||
throw std::invalid_argument(name);
|
||||
}
|
||||
destination = source;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <equilibrium::StellarEquilibriumModel Model>
|
||||
[[nodiscard]] ProjectedEquilibriumState<Model> projectRadialProfile(
|
||||
const equilibrium::StellarEquilibriumProblem<Model> &problem,
|
||||
const RadialProfile &profile,
|
||||
const StellarEquilibriumProjectionOptions &options = {}
|
||||
) {
|
||||
const detail::ProjectedRadialFields fields = detail::projectRadialFields(
|
||||
problem.GetDiscretization(), profile,
|
||||
problem.GetStellarModel().template specification<models::FixedTotalMass>().targetMass(),
|
||||
problem.GetStellarModel().template specification<surface::Isobaric>().targetPressure(), options
|
||||
);
|
||||
|
||||
mfem::Vector values(problem.StateSize());
|
||||
values = 0.0;
|
||||
const auto stateView = problem.GetManifest().stateView(values);
|
||||
|
||||
detail::assignProjectedBlock(
|
||||
stateView.block(utils::blocks::density_field.mass_term), fields.density,
|
||||
"The projected density does not match the compiled equilibrium-state block."
|
||||
);
|
||||
stateView.block(utils::blocks::surface_deformation_field.parameters_term) = 0.0;
|
||||
detail::assignProjectedBlock(
|
||||
stateView.block(utils::blocks::gravity_field.gradient_term), fields.gravityGradient,
|
||||
"The projected gravity gradient does not match the compiled equilibrium-state block."
|
||||
);
|
||||
detail::assignProjectedBlock(
|
||||
stateView.block(utils::blocks::gravity_field.poisson_term), fields.gravityPotential,
|
||||
"The projected gravity potential does not match the compiled equilibrium-state block."
|
||||
);
|
||||
detail::assignProjectedBlock(
|
||||
stateView.block(utils::blocks::enthalpy_field.specific_term), fields.specificEnthalpy,
|
||||
"The projected specific enthalpy does not match the compiled equilibrium-state block."
|
||||
);
|
||||
|
||||
/*
|
||||
* Projection of a continuous spherical profile onto a faceted
|
||||
* reference mesh generally leaves a small trace error on the physical
|
||||
* surface. The pressure condition replaces these carrier rows in the
|
||||
* compiled equilibrium problem, so impose its required carrier value
|
||||
* exactly after bulk projection instead of treating that geometric
|
||||
* mismatch as part of the initial residual.
|
||||
*/
|
||||
mfem::Vector enthalpy = stateView.block(utils::blocks::enthalpy_field.specific_term);
|
||||
const dimensions::SpecificEnthalpyValue requiredSurfaceEnthalpy =
|
||||
eos::evaluate<dimensions::quantity::SpecificEnthalpy>(
|
||||
problem.GetStellarModel().template specification<eos::Polytrope>(),
|
||||
problem.GetStellarModel().template specification<surface::Isobaric>().targetPressure()
|
||||
);
|
||||
for (const int surfaceRow : problem.GetPressureSurfaceRows().reduced_dofs()) {
|
||||
enthalpy(surfaceRow) = requiredSurfaceEnthalpy.value();
|
||||
}
|
||||
|
||||
mfem::Vector fixedMassCoordinate =
|
||||
stateView.block(utils::blocks::fixed_total_mass_constraint.mass_normalization_term);
|
||||
if (fixedMassCoordinate.Size() != 1) {
|
||||
throw std::invalid_argument("FixedTotalMass must generate exactly one equilibrium-state coordinate.");
|
||||
}
|
||||
fixedMassCoordinate(0) = fields.bernoulliConstant;
|
||||
|
||||
if constexpr (std::remove_cvref_t<Model>::template containsSpecification<models::FixedCentralDensity>) {
|
||||
stateView.block(utils::blocks::fixed_central_density_phase.central_value_term) = 0.0;
|
||||
}
|
||||
|
||||
return {.values = std::move(values)};
|
||||
}
|
||||
|
||||
template <
|
||||
equilibrium::StellarEquilibriumModel Model,
|
||||
typename Strategy>
|
||||
requires RadialSeedStrategyFor<
|
||||
Strategy,
|
||||
typename equilibrium::StellarEquilibriumProblem<Model>::ModelType>
|
||||
[[nodiscard]] ProjectedEquilibriumState<Model> makeProjectedEquilibriumState(
|
||||
const equilibrium::StellarEquilibriumProblem<Model> &problem,
|
||||
const Strategy &strategy,
|
||||
const StellarEquilibriumProjectionOptions &options = {}
|
||||
) {
|
||||
return projectRadialProfile(problem, generateRadialProfile(problem.GetStellarModel(), strategy), options);
|
||||
}
|
||||
} // namespace mean_field::seed
|
||||
Reference in New Issue
Block a user