feat(mean_field): added dimensions, discritization, and start of eos

The full rewrite of mean_field into something maintainable is progressing. dimensions is mostly done, discritization (domain, blocks, and fields) is done, and eos is progressing quickly
This commit is contained in:
2026-09-15 10:42:00 -04:00
parent 7c99debf2f
commit d1f59d6d70
88 changed files with 324020 additions and 268 deletions

View File

@@ -0,0 +1,118 @@
#include "serif/discretization/domain/mesh/topology.hpp"
#include <mfem.hpp>
namespace serif::discretization::domain::mesh {
const std::vector<int> MeshTopology::s_noElements{};
namespace {
[[nodiscard]] std::size_t as_index(const int id) noexcept {
return static_cast<std::size_t>(id);
}
[[nodiscard]] bool in_range(const int id, const std::size_t size) noexcept {
return id >= 0 && as_index(id) < size;
}
}
MeshTopology::MeshTopology(const mfem::Mesh &mesh)
: m_elementDomainIDs(as_index(mesh.GetNE()), 0),
m_faceElements(as_index(mesh.GetNumFaces())),
m_elementNeighbors(as_index(mesh.GetNE())),
m_boundaryElementBoundaryIDs(as_index(mesh.GetNBE()), 0),
m_boundaryElementFaceIDs(as_index(mesh.GetNBE()), -1),
m_boundaryElementsByFace(as_index(mesh.GetNumFaces())) {
// Which domain each volume element claims to belong to.
for (int elementID = 0; elementID < mesh.GetNE(); ++elementID) {
m_elementDomainIDs[as_index(elementID)] = mesh.GetAttribute(elementID);
}
// Face connectivity.
for (int faceID = 0; faceID < mesh.GetNumFaces(); ++faceID) {
int firstElementID = -1;
int secondElementID = -1;
mesh.GetFaceElements(faceID, &firstElementID, &secondElementID);
m_faceElements[as_index(faceID)] = FaceElements{
.firstElementID = firstElementID,
.secondElementID = secondElementID
};
if (firstElementID < 0 || secondElementID < 0) {
continue;
}
m_elementNeighbors[as_index(firstElementID)].push_back(secondElementID);
m_elementNeighbors[as_index(secondElementID)].push_back(firstElementID);
}
// Boundary elements
for (int boundaryElementID = 0; boundaryElementID < mesh.GetNBE(); ++boundaryElementID) {
m_boundaryElementBoundaryIDs[as_index(boundaryElementID)] = mesh.GetBdrAttribute(boundaryElementID);
const int faceID = mesh.GetBdrElementFaceIndex(boundaryElementID);
m_boundaryElementFaceIDs[as_index(boundaryElementID)] = faceID;
if (in_range(faceID, m_boundaryElementsByFace.size())) {
m_boundaryElementsByFace[as_index(faceID)].push_back(boundaryElementID);
}
}
}
int MeshTopology::element_count() const noexcept {
return static_cast<int>(m_elementDomainIDs.size());
}
int MeshTopology::face_count() const noexcept {
return static_cast<int>(m_faceElements.size());
}
int MeshTopology::boundary_element_count() const noexcept {
return static_cast<int>(m_boundaryElementBoundaryIDs.size());
}
int MeshTopology::element_domain_id(const int elementID) const {
if (!in_range(elementID, m_elementDomainIDs.size())) {
return 0;
}
return m_elementDomainIDs[as_index(elementID)];
}
int MeshTopology::boundary_element_boundary_id(const int boundaryElementID) const {
if (!in_range(boundaryElementID, m_boundaryElementBoundaryIDs.size())) {
return 0;
}
return m_boundaryElementBoundaryIDs[as_index(boundaryElementID)];
}
FaceElements MeshTopology::face_elements(const int faceID) const {
if (!in_range(faceID, m_faceElements.size())) {
return FaceElements{};
}
return m_faceElements[as_index(faceID)];
}
const std::vector<int> &MeshTopology::element_neighbors(const int elementID) const {
if (!in_range(elementID, m_elementNeighbors.size())) {
return s_noElements;
}
return m_elementNeighbors[as_index(elementID)];
}
int MeshTopology::boundary_element_face(const int boundaryElementID) const {
if (!in_range(boundaryElementID, m_boundaryElementFaceIDs.size())) {
return -1;
}
return m_boundaryElementFaceIDs[as_index(boundaryElementID)];
}
const std::vector<int> &MeshTopology::boundary_elements_on_face(const int faceID) const {
if (!in_range(faceID, m_boundaryElementsByFace.size())) {
return s_noElements;
}
return m_boundaryElementsByFace[as_index(faceID)];
}
}

View File

@@ -0,0 +1,52 @@
#include "serif/discretization/domain/schema/validation/results.hpp"
namespace serif::discretization::domain::schema::validation {
bool SchemaRelationValidationResult::valid() const noexcept {
return result.valid();
}
SchemaRelationValidationResult::operator bool() const noexcept {
return valid();
}
bool SchemaValidationResult::valid() const noexcept {
for (const auto &relationResult : relationResults) {
if (!relationResult.valid()) {
return false;
}
}
return true;
}
SchemaValidationResult::operator bool() const noexcept {
return valid();
}
std::size_t SchemaValidationResult::relation_count() const noexcept {
return relationResults.size();
}
std::size_t SchemaValidationResult::failed_relation_count() const noexcept {
std::size_t failureCount = 0;
for (const auto &relationResult : relationResults) {
if (!relationResult.valid()) {
++failureCount;
}
}
return failureCount;
}
std::size_t SchemaValidationResult::passed_relation_count() const noexcept {
return relationResults.size() - failed_relation_count();
}
std::optional<std::size_t> SchemaValidationResult::first_failed_relation_index() const noexcept {
for (std::size_t relationIndex = 0; relationIndex < relationResults.size(); ++relationIndex) {
if (!relationResults[relationIndex].valid()) {
return relationIndex;
}
}
return std::nullopt;
}
}

View File

@@ -0,0 +1,146 @@
#include "serif/eos/models/polytropic.hpp"
#include "serif/utils/misc/finite.hpp"
#include "serif/eos/exceptions.hpp"
namespace serif::eos::models {
using utils::misc::validate_finite;
using utils::misc::validate_greater_than;
using utils::misc::validate_less_than;
using utils::misc::validate_nonnegativity;
using namespace dimensions;
using namespace relations;
Polytrope::Polytrope(const PolytropeParameters& parameters) : Polytrope(parameters.n, parameters.K) {}
Polytrope::Polytrope(const double n, const double K):
m_polytropic_index(n),
m_polytropic_constant(K),
m_enthalpy_scale((n + 1.0) * K) {
validate_finite<EOSEvaluationError>(m_polytropic_index, EOSEvaluationErrorCode::outside_domain);
validate_greater_than<EOSEvaluationError>(m_polytropic_index, 1.0, EOSEvaluationErrorCode::outside_domain);
validate_finite<EOSEvaluationError>(m_polytropic_constant, EOSEvaluationErrorCode::outside_domain);
validate_greater_than<EOSEvaluationError>(m_polytropic_constant, 0.0, EOSEvaluationErrorCode::outside_domain);
}
[[nodiscard]] double Polytrope::polytropic_index() const noexcept {
return m_polytropic_index;
}
[[nodiscard]] double Polytrope::polytropic_constant() const noexcept {
return m_polytropic_constant;
}
[[nodiscard]] double Polytrope::enthalpy_scale() const noexcept {
return m_enthalpy_scale;
}
[[nodiscard]] PressureValue Polytrope::evaluate(PressureFromDensity, const DensityValue density) const {
validate_nonnegativity<EOSEvaluationError>(density.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
if (density.value() == 0.0) {
// P = 0
return PressureValue{0.0};
}
// P = K * (ρ)^(1 + 1/n)
return PressureValue{
m_polytropic_constant * std::pow(density.value(), 1.0 + 1.0 / m_polytropic_index)
};
}
[[nodiscard]] SpecificEnthalpyValue Polytrope::evaluate(SpecificEnthalpyFromDensity, const DensityValue density) const {
validate_nonnegativity<EOSEvaluationError>(density.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
if (density.value() == 0.0) {
// h = 0
return SpecificEnthalpyValue{0.0};
}
// h = hs * (ρ)^(1/n)
return SpecificEnthalpyValue{
m_enthalpy_scale * std::pow(density.value(), 1.0 / m_polytropic_index)
};
}
[[nodiscard]] DensityValue Polytrope::evaluate(DensityFromSpecificEnthalpy, SpecificEnthalpyValue specific_enthalpy) const {
validate_finite<EOSEvaluationError>(specific_enthalpy.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
if (specific_enthalpy.value() <= 0.0) {
// ρ = 0
return DensityValue{0.0};
}
// ρ = (h/hs)^n
return DensityValue{std::pow(specific_enthalpy.value() / m_enthalpy_scale, m_polytropic_index)};
}
[[nodiscard]] PressureValue Polytrope::evaluate(PressureFromSpecificEnthalpy, SpecificEnthalpyValue specific_enthalpy) const {
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specific_enthalpy);
if (specific_enthalpy.value() <= 0.0) {
// P = 0
return PressureValue{0.0};
}
// P = ρ * h / (n + 1)
return PressureValue{density.value() * specific_enthalpy.value()/ (m_polytropic_index + 1.0)};
}
[[nodiscard]] SpecificEnthalpyValue Polytrope::evaluate(SpecificEnthalpyFromPressure, PressureValue pressure) const {
validate_nonnegativity<EOSEvaluationError>(pressure.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
if (pressure.value() == 0.0) {
// h = 0
return SpecificEnthalpyValue{0.0};
}
const double indexPlusOne = m_polytropic_index + 1.0;
// h = (n+1) * (K^(n/(n+1))) * (P^(1/(n+1)))
return SpecificEnthalpyValue{
indexPlusOne * std::pow(m_polytropic_constant, m_polytropic_index / indexPlusOne) *
std::pow(pressure.value(), 1.0 / indexPlusOne)
};
}
[[nodiscard]] PartialDerivative<Density, SpecificEnthalpy> Polytrope::partial_derivative(DensityFromSpecificEnthalpy, WithRespectTo<SpecificEnthalpy>, const SpecificEnthalpyValue specific_enthalpy) const {
validate_finite<EOSEvaluationError>(specific_enthalpy.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
if (specific_enthalpy.value() < 0.0) {
// dρ/dh = 0
return PartialDerivative<Density, SpecificEnthalpy>{0.0};
}
if (specific_enthalpy.value() == 0.0) {
// dρ/dh = n == 1 -> 1/hs
// dρ/dh = else -> 0
return PartialDerivative<Density, SpecificEnthalpy>{
m_polytropic_index == 1.0 ? 1.0 / m_enthalpy_scale : 0.0
};
}
// dρ/dh = (n/hs) * (h/hs)^(n-1)
return PartialDerivative<Density, SpecificEnthalpy>{
m_polytropic_index / m_enthalpy_scale * std::pow(specific_enthalpy.value() / m_enthalpy_scale, m_polytropic_index - 1.0)
};
}
[[nodiscard]] PartialDerivative<Pressure, SpecificEnthalpy> Polytrope::partial_derivative(PressureFromSpecificEnthalpy, WithRespectTo<SpecificEnthalpy>, const SpecificEnthalpyValue specific_enthalpy) const {
const DensityValue density = evaluate(DensityFromSpecificEnthalpy{}, specific_enthalpy);
// dP/dh = ρ
return PartialDerivative<Pressure, SpecificEnthalpy>{density.value()};
}
[[nodiscard]] PartialDerivative<Pressure, Density> Polytrope::partial_derivative(PressureFromDensity, WithRespectTo<Density>, const DensityValue density) const {
validate_nonnegativity<EOSEvaluationError>(density.value(), EOSEvaluationErrorCode::invalid_thermodynamic_input);
if (density.value() == 0.0) {
// dP/dρ = 0
return PartialDerivative<Pressure, Density>{0.0};
}
// dP/dρ = K * (1 + 1/n) * (ρ)^(1/n)
return PartialDerivative<Pressure, Density>{
m_polytropic_constant * (1.0 + 1.0 / m_polytropic_index) * std::pow(density.value(), 1.0 / m_polytropic_index)
};
}
}