Files
MeanField/libmeanfield/interface/solver/stellar_structure.cppm

487 lines
20 KiB
C++

module;
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <memory>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <mfem.hpp>
#include <mpi.h>
export module mean_field:solver.stellar_structure;
export import :operators.stellar_equilibrium_problem;
export import :solver.stellar_equilibrium_types;
export namespace mean_field::solver {
template <equilibrium::DiscretizedStellarEquilibriumProblem Problem> class StellarEquilibriumEvaluationReport;
}
namespace mean_field::solver::detail {
enum class StellarViewCertification : std::uint8_t { unavailable, checkpoint, structure };
template <typename Problem> struct StellarStructureStorage final {
using ProblemType = std::remove_cvref_t<Problem>;
StellarStructureStorage(
std::unique_ptr<ProblemType> ownedProblem,
std::unique_ptr<mfem::Vector> acceptedPhysicalState,
std::unique_ptr<physics::RigidRotation> prescribedRotation
)
: problem(std::move(ownedProblem)),
physicalState(std::move(acceptedPhysicalState)),
rotation(std::move(prescribedRotation)) {
}
std::unique_ptr<ProblemType> problem;
std::unique_ptr<mfem::Vector> physicalState;
std::unique_ptr<physics::RigidRotation> rotation;
std::uint64_t viewGeneration{0};
StellarViewCertification certification{StellarViewCertification::unavailable};
};
template <typename Problem>
[[nodiscard]] bool IsCurrentView(
const std::weak_ptr<const StellarStructureStorage<Problem>> &candidate,
const std::uint64_t generation,
const bool requireConverged
) noexcept {
const auto storage = candidate.lock();
if (storage == nullptr || storage->problem == nullptr || storage->physicalState == nullptr ||
storage->viewGeneration != generation) {
return false;
}
if (requireConverged) {
return storage->certification == StellarViewCertification::structure;
}
return storage->certification == StellarViewCertification::checkpoint ||
storage->certification == StellarViewCertification::structure;
}
template <typename Problem>
[[nodiscard]] std::shared_ptr<const StellarStructureStorage<Problem>> RequireCurrentView(
const std::weak_ptr<const StellarStructureStorage<Problem>> &candidate,
const std::uint64_t generation,
const bool requireConverged
) {
auto storage = candidate.lock();
if (storage == nullptr || storage->problem == nullptr || storage->physicalState == nullptr ||
storage->viewGeneration != generation ||
(requireConverged && storage->certification != StellarViewCertification::structure) ||
(!requireConverged && storage->certification != StellarViewCertification::checkpoint &&
storage->certification != StellarViewCertification::structure)) {
throw std::logic_error("The stellar structure view is stale or is not certified for this result.");
}
return storage;
}
template <typename Vector> [[nodiscard]] std::span<const mfem::real_t> ReadOnlySpan(const Vector &values) noexcept {
return {values.GetData(), static_cast<std::size_t>(values.Size())};
}
template <typename Problem> struct StellarEvaluationReportAccess;
} // namespace mean_field::solver::detail
export namespace mean_field::equilibrium {
/*
* These are the future owning, self-contained values. There is no public
* construction path until deep capture and its MPI-independent storage
* schema are implemented.
*/
template <DiscretizedStellarEquilibriumProblem Problem> class StellarStructure final {
public:
using ProblemType = std::remove_cvref_t<Problem>;
StellarStructure(const StellarStructure &) = delete;
StellarStructure &operator=(const StellarStructure &) = delete;
StellarStructure(StellarStructure &&) noexcept = default;
StellarStructure &operator=(StellarStructure &&) = delete;
~StellarStructure() = default;
private:
StellarStructure() = default;
};
template <DiscretizedStellarEquilibriumProblem Problem> class StellarCheckpoint final {
public:
using ProblemType = std::remove_cvref_t<Problem>;
StellarCheckpoint(const StellarCheckpoint &) = delete;
StellarCheckpoint &operator=(const StellarCheckpoint &) = delete;
StellarCheckpoint(StellarCheckpoint &&) noexcept = default;
StellarCheckpoint &operator=(StellarCheckpoint &&) = delete;
~StellarCheckpoint() = default;
private:
StellarCheckpoint() = default;
};
/*
* Result views weakly observe context-owned storage. valid() remains safe
* after that context is destroyed. References and spans extracted from a
* valid view remain borrowed: the context must outlive their use, and the
* next evaluate() call invalidates them along with their originating view.
*/
template <DiscretizedStellarEquilibriumProblem Problem> class StellarStructureView final {
public:
using ProblemType = std::remove_cvref_t<Problem>;
using ModelType = typename ProblemType::ModelType;
[[nodiscard]] bool valid() const noexcept {
return solver::detail::IsCurrentView(m_storage, m_generation, true);
}
[[nodiscard]] const ModelType &model() const & {
const auto storage = RequireStorage();
return storage->problem->GetStellarModel();
}
[[nodiscard]] const ModelType &model() const && = delete;
[[nodiscard]] MPI_Comm communicator() const & {
const auto storage = RequireStorage();
return storage->problem->GetCommunicator();
}
[[nodiscard]] MPI_Comm communicator() const && = delete;
[[nodiscard]] std::span<const mfem::real_t> state() const & {
const auto storage = RequireStorage();
return solver::detail::ReadOnlySpan(*storage->physicalState);
}
[[nodiscard]] std::span<const mfem::real_t> state() const && = delete;
[[nodiscard]] std::span<const operators::RootBlockDescriptor> stateDescriptors() const & {
const auto storage = RequireStorage();
return storage->problem->GetManifest().valueBlocks();
}
[[nodiscard]] std::span<const operators::RootBlockDescriptor> stateDescriptors() const && = delete;
template <typename Term>
requires requires(
const typename ProblemType::ManifestType &manifest,
const mfem::Vector &physicalState,
const Term &term
) { manifest.stateView(physicalState).block(term); }
[[nodiscard]] std::span<const mfem::real_t> stateBlock(const Term &term) const & {
const auto storage = RequireStorage();
const auto block = storage->problem->GetManifest().stateView(*storage->physicalState).block(term);
return solver::detail::ReadOnlySpan(block);
}
template <typename Term> [[nodiscard]] std::span<const mfem::real_t> stateBlock(const Term &) const && = delete;
[[nodiscard]] std::optional<physics::RigidRotation> prescribedRotation() const & {
const auto storage = RequireStorage();
if (storage->rotation == nullptr) {
return std::nullopt;
}
return *storage->rotation;
}
[[nodiscard]] std::optional<physics::RigidRotation> prescribedRotation() const && = delete;
[[nodiscard]] physics::RigidRotation rotation() const & {
const auto storage = RequireStorage();
return storage->problem->GetPreparedOperator().GetRotation();
}
[[nodiscard]] physics::RigidRotation rotation() const && = delete;
[[nodiscard]] StellarStructure<ProblemType> capture() const {
(void)RequireStorage();
throw std::logic_error("Capturing a self-contained StellarStructure is not implemented.");
}
private:
template <DiscretizedStellarEquilibriumProblem> friend class solver::StellarEquilibriumEvaluationReport;
using Storage = solver::detail::StellarStructureStorage<ProblemType>;
StellarStructureView(
std::weak_ptr<const Storage> storage,
const std::uint64_t generation
) noexcept
: m_storage(std::move(storage)),
m_generation(generation) {
}
[[nodiscard]] std::shared_ptr<const Storage> RequireStorage() const {
return solver::detail::RequireCurrentView(m_storage, m_generation, true);
}
std::weak_ptr<const Storage> m_storage;
std::uint64_t m_generation;
};
template <DiscretizedStellarEquilibriumProblem Problem> class StellarCheckpointView final {
public:
using ProblemType = std::remove_cvref_t<Problem>;
using ModelType = typename ProblemType::ModelType;
[[nodiscard]] bool valid() const noexcept {
return solver::detail::IsCurrentView(m_storage, m_generation, false);
}
[[nodiscard]] const ModelType &model() const & {
const auto storage = RequireStorage();
return storage->problem->GetStellarModel();
}
[[nodiscard]] const ModelType &model() const && = delete;
[[nodiscard]] MPI_Comm communicator() const & {
const auto storage = RequireStorage();
return storage->problem->GetCommunicator();
}
[[nodiscard]] MPI_Comm communicator() const && = delete;
[[nodiscard]] std::span<const mfem::real_t> state() const & {
const auto storage = RequireStorage();
return solver::detail::ReadOnlySpan(*storage->physicalState);
}
[[nodiscard]] std::span<const mfem::real_t> state() const && = delete;
[[nodiscard]] std::span<const operators::RootBlockDescriptor> stateDescriptors() const & {
const auto storage = RequireStorage();
return storage->problem->GetManifest().valueBlocks();
}
[[nodiscard]] std::span<const operators::RootBlockDescriptor> stateDescriptors() const && = delete;
template <typename Term>
requires requires(
const typename ProblemType::ManifestType &manifest,
const mfem::Vector &physicalState,
const Term &term
) { manifest.stateView(physicalState).block(term); }
[[nodiscard]] std::span<const mfem::real_t> stateBlock(const Term &term) const & {
const auto storage = RequireStorage();
const auto block = storage->problem->GetManifest().stateView(*storage->physicalState).block(term);
return solver::detail::ReadOnlySpan(block);
}
template <typename Term> [[nodiscard]] std::span<const mfem::real_t> stateBlock(const Term &) const && = delete;
[[nodiscard]] std::optional<physics::RigidRotation> prescribedRotation() const & {
const auto storage = RequireStorage();
if (storage->rotation == nullptr) {
return std::nullopt;
}
return *storage->rotation;
}
[[nodiscard]] std::optional<physics::RigidRotation> prescribedRotation() const && = delete;
[[nodiscard]] physics::RigidRotation rotation() const & {
const auto storage = RequireStorage();
return storage->problem->GetPreparedOperator().GetRotation();
}
[[nodiscard]] physics::RigidRotation rotation() const && = delete;
[[nodiscard]] StellarCheckpoint<ProblemType> capture() const {
(void)RequireStorage();
throw std::logic_error("Capturing a self-contained StellarCheckpoint is not implemented.");
}
private:
template <DiscretizedStellarEquilibriumProblem> friend class solver::StellarEquilibriumEvaluationReport;
using Storage = solver::detail::StellarStructureStorage<ProblemType>;
StellarCheckpointView(
std::weak_ptr<const Storage> storage,
const std::uint64_t generation
) noexcept
: m_storage(std::move(storage)),
m_generation(generation) {
}
[[nodiscard]] std::shared_ptr<const Storage> RequireStorage() const {
return solver::detail::RequireCurrentView(m_storage, m_generation, false);
}
std::weak_ptr<const Storage> m_storage;
std::uint64_t m_generation;
};
template <DiscretizedStellarEquilibriumProblem Problem>
[[noreturn]] void serialize(
const StellarStructure<Problem> &,
const std::filesystem::path &
) {
throw std::logic_error("Serializing a StellarStructure is not implemented.");
}
template <DiscretizedStellarEquilibriumProblem Problem>
[[noreturn]] void serialize(
const StellarStructureView<Problem> &view,
const std::filesystem::path &
) {
(void)view.state();
throw std::logic_error("Serializing a StellarStructureView is not implemented.");
}
template <DiscretizedStellarEquilibriumProblem Problem>
[[noreturn]] void serialize(
const StellarCheckpoint<Problem> &,
const std::filesystem::path &
) {
throw std::logic_error("Serializing a StellarCheckpoint is not implemented.");
}
template <DiscretizedStellarEquilibriumProblem Problem>
[[noreturn]] void serialize(
const StellarCheckpointView<Problem> &view,
const std::filesystem::path &
) {
(void)view.state();
throw std::logic_error("Serializing a StellarCheckpointView is not implemented.");
}
} // namespace mean_field::equilibrium
export namespace mean_field::solver {
template <equilibrium::DiscretizedStellarEquilibriumProblem Problem>
class StellarEquilibriumEvaluationReport final {
public:
using ProblemType = std::remove_cvref_t<Problem>;
using StructureView = equilibrium::StellarStructureView<ProblemType>;
using CheckpointView = equilibrium::StellarCheckpointView<ProblemType>;
StellarEquilibriumEvaluationReport(const StellarEquilibriumEvaluationReport &) = default;
StellarEquilibriumEvaluationReport &operator=(const StellarEquilibriumEvaluationReport &) = default;
StellarEquilibriumEvaluationReport(StellarEquilibriumEvaluationReport &&) noexcept = default;
StellarEquilibriumEvaluationReport &operator=(StellarEquilibriumEvaluationReport &&) noexcept = default;
~StellarEquilibriumEvaluationReport() = default;
[[nodiscard]] bool converged() const noexcept {
return m_converged;
}
[[nodiscard]] const StellarEquilibriumEvaluationDiagnostics &diagnostics() const & noexcept {
return m_diagnostics;
}
[[nodiscard]] const StellarEquilibriumEvaluationDiagnostics &diagnostics() const && = delete;
[[nodiscard]] int completedNonlinearIterations() const noexcept {
return m_diagnostics.acceptedNonlinearIterations;
}
[[nodiscard]] double initialResidualNorm() const noexcept {
return m_diagnostics.initialResidualNorm;
}
[[nodiscard]] double finalResidualNorm() const noexcept {
return m_diagnostics.finalResidualNorm;
}
[[nodiscard]] const StellarEquilibriumFailureReport &failure() const & {
if (!m_failure.has_value()) {
throw std::logic_error("A converged stellar-equilibrium report has no failure record.");
}
return *m_failure;
}
[[nodiscard]] const StellarEquilibriumFailureReport &failure() const && = delete;
[[nodiscard]] StructureView structureView() const {
if (!m_converged) {
throw std::logic_error("A failed stellar-equilibrium report cannot certify a structure view.");
}
StructureView view{m_storage, m_generation};
if (!view.valid()) {
throw std::logic_error("The stellar-equilibrium structure view has been invalidated.");
}
return view;
}
[[nodiscard]] CheckpointView checkpointView() const {
CheckpointView view{m_storage, m_generation};
if (!view.valid()) {
throw std::logic_error("The stellar-equilibrium checkpoint view has been invalidated.");
}
return view;
}
[[nodiscard]] CheckpointView lastAcceptedCheckpointView() const {
return checkpointView();
}
private:
friend struct detail::StellarEvaluationReportAccess<ProblemType>;
using Storage = detail::StellarStructureStorage<ProblemType>;
StellarEquilibriumEvaluationReport(
const bool converged,
StellarEquilibriumEvaluationDiagnostics diagnostics,
std::optional<StellarEquilibriumFailureReport> failure,
std::weak_ptr<const Storage> storage,
const std::uint64_t generation
)
: m_converged(converged),
m_diagnostics(std::move(diagnostics)),
m_failure(std::move(failure)),
m_storage(std::move(storage)),
m_generation(generation) {
}
bool m_converged;
StellarEquilibriumEvaluationDiagnostics m_diagnostics;
std::optional<StellarEquilibriumFailureReport> m_failure;
std::weak_ptr<const Storage> m_storage;
std::uint64_t m_generation;
};
} // namespace mean_field::solver
namespace mean_field::solver::detail {
template <typename Problem> struct StellarEvaluationReportAccess final {
using ProblemType = std::remove_cvref_t<Problem>;
using Report = StellarEquilibriumEvaluationReport<ProblemType>;
using Storage = StellarStructureStorage<ProblemType>;
[[nodiscard]] static Report Success(
const std::shared_ptr<Storage> &storage,
StellarEquilibriumEvaluationDiagnostics diagnostics
) {
if (storage == nullptr) {
throw std::invalid_argument("A stellar-equilibrium report requires owned result storage.");
}
storage->certification = StellarViewCertification::structure;
return Report{true, std::move(diagnostics), std::nullopt, storage, storage->viewGeneration};
}
[[nodiscard]] static Report Failure(
const std::shared_ptr<Storage> &storage,
StellarEquilibriumEvaluationDiagnostics diagnostics,
const StellarEquilibriumFailureReason reason,
std::string message
) {
if (storage == nullptr) {
throw std::invalid_argument("A stellar-equilibrium report requires owned result storage.");
}
storage->certification = StellarViewCertification::checkpoint;
StellarEquilibriumFailureReport failure{
.reason = reason,
.message = std::move(message),
.completedNonlinearIterations = diagnostics.acceptedNonlinearIterations,
.initialResidualNorm = diagnostics.initialResidualNorm,
.finalResidualNorm = diagnostics.finalResidualNorm
};
return Report{
false, std::move(diagnostics), std::optional<StellarEquilibriumFailureReport>{std::move(failure)},
storage, storage->viewGeneration
};
}
};
} // namespace mean_field::solver::detail