module; #include #include #include #include #include #include #include #include #include export module mean_field:preconditioning.equilibrium_coordinates; export import :preconditioning.specification_border; export namespace mean_field::preconditioning { namespace detail { template struct EquilibriumCoordinateListIsSubset; template struct EquilibriumCoordinateListIsSubset, Universe> : std::bool_constant<(utils::blocks::contains_type_v && ...)> { }; } // namespace detail template concept EquilibriumCoordinateComponentFor = PreconditionerComponent && utils::blocks::block_form_is_valid_v
&& std::remove_cvref_t::CorrectionBlocks::size == Form::value_block_count && std::remove_cvref_t::ResidualBlocks::size == Form::residual_block_count && detail::EquilibriumCoordinateListIsSubset< typename std::remove_cvref_t::CorrectionBlocks, typename Form::value_blocks>::value && detail::EquilibriumCoordinateListIsSubset< typename std::remove_cvref_t::ResidualBlocks, typename Form::residual_blocks>::value; struct EquilibriumCoordinateRange final { int equilibriumOffset{0}; int preconditionerOffset{0}; int size{0}; constexpr bool operator==(const EquilibriumCoordinateRange &) const = default; }; struct EquilibriumCoordinateMapStatistics final { std::uint64_t residualPacks{0}; std::uint64_t residualUnpacks{0}; std::uint64_t correctionPacks{0}; std::uint64_t correctionUnpacks{0}; }; template requires EquilibriumCoordinateComponentFor class EquilibriumPreconditionerCoordinateMap final { private: using ComponentType = std::remove_cvref_t; using Layout = utils::blocks::form_layout; static constexpr std::size_t correctionBlockCount = ComponentType::CorrectionBlocks::size; static constexpr std::size_t residualBlockCount = ComponentType::ResidualBlocks::size; public: explicit EquilibriumPreconditionerCoordinateMap(const Layout &layout) : m_correctionRanges(MakeCorrectionRanges( layout, typename ComponentType::CorrectionBlocks{} )), m_residualRanges(MakeResidualRanges( layout, typename ComponentType::ResidualBlocks{} )), m_equilibriumStateSize(layout.value_offsets().Last()), m_equilibriumResidualSize(layout.residual_offsets().Last()), m_preconditionerCorrectionSize(TotalSize(m_correctionRanges)), m_preconditionerResidualSize(TotalSize(m_residualRanges)) { if (m_preconditionerCorrectionSize != m_equilibriumStateSize || m_preconditionerResidualSize != m_equilibriumResidualSize) { throw std::logic_error( "The typed preconditioner coordinate map does not span the complete equilibrium operator." ); } } void PackResidual( const mfem::Vector &equilibriumResidual, mfem::Vector &preconditionerResidual ) const { VerifySizes( equilibriumResidual, m_equilibriumResidualSize, preconditionerResidual, m_preconditionerResidualSize, "residual pack" ); EquilibriumToPreconditioner(equilibriumResidual, preconditionerResidual, m_residualRanges); ++m_statistics.residualPacks; } void UnpackResidual( const mfem::Vector &preconditionerResidual, mfem::Vector &equilibriumResidual ) const { VerifySizes( preconditionerResidual, m_preconditionerResidualSize, equilibriumResidual, m_equilibriumResidualSize, "residual unpack" ); PreconditionerToEquilibrium(preconditionerResidual, equilibriumResidual, m_residualRanges); ++m_statistics.residualUnpacks; } void PackCorrection( const mfem::Vector &equilibriumCorrection, mfem::Vector &preconditionerCorrection ) const { VerifySizes( equilibriumCorrection, m_equilibriumStateSize, preconditionerCorrection, m_preconditionerCorrectionSize, "correction pack" ); EquilibriumToPreconditioner(equilibriumCorrection, preconditionerCorrection, m_correctionRanges); ++m_statistics.correctionPacks; } void UnpackCorrection( const mfem::Vector &preconditionerCorrection, mfem::Vector &equilibriumCorrection ) const { VerifySizes( preconditionerCorrection, m_preconditionerCorrectionSize, equilibriumCorrection, m_equilibriumStateSize, "correction unpack" ); PreconditionerToEquilibrium(preconditionerCorrection, equilibriumCorrection, m_correctionRanges); ++m_statistics.correctionUnpacks; } [[nodiscard]] int EquilibriumStateSize() const noexcept { return m_equilibriumStateSize; } [[nodiscard]] int EquilibriumResidualSize() const noexcept { return m_equilibriumResidualSize; } [[nodiscard]] int PreconditionerCorrectionSize() const noexcept { return m_preconditionerCorrectionSize; } [[nodiscard]] int PreconditionerResidualSize() const noexcept { return m_preconditionerResidualSize; } [[nodiscard]] const std::array< EquilibriumCoordinateRange, correctionBlockCount> & GetCorrectionRanges() const noexcept { return m_correctionRanges; } [[nodiscard]] const std::array< EquilibriumCoordinateRange, residualBlockCount> & GetResidualRanges() const noexcept { return m_residualRanges; } [[nodiscard]] const EquilibriumCoordinateMapStatistics &GetStatistics() const noexcept { return m_statistics; } private: template [[nodiscard]] static std::array< EquilibriumCoordinateRange, sizeof...(Blocks)> MakeCorrectionRanges( const Layout &layout, utils::blocks::type_list ) { std::array ranges{}; int preconditionerOffset = 0; std::size_t range = 0; ( [&] { constexpr int equilibriumBlock = utils::blocks::type_index_v; const int size = layout.size(utils::blocks::value_block{}); ranges[range++] = { .equilibriumOffset = layout.offset(utils::blocks::value_block{}), .preconditionerOffset = preconditionerOffset, .size = size }; preconditionerOffset += size; }(), ...); return ranges; } template [[nodiscard]] static std::array< EquilibriumCoordinateRange, sizeof...(Blocks)> MakeResidualRanges( const Layout &layout, utils::blocks::type_list ) { std::array ranges{}; int preconditionerOffset = 0; std::size_t range = 0; ( [&] { constexpr int equilibriumBlock = utils::blocks::type_index_v; const int size = layout.size(utils::blocks::residual_block{}); ranges[range++] = { .equilibriumOffset = layout.offset(utils::blocks::residual_block{}), .preconditionerOffset = preconditionerOffset, .size = size }; preconditionerOffset += size; }(), ...); return ranges; } template [[nodiscard]] static int TotalSize( const std::array< EquilibriumCoordinateRange, Size> &ranges ) noexcept { int size = 0; for (const auto &range : ranges) { size += range.size; } return size; } template static void EquilibriumToPreconditioner( const mfem::Vector &equilibrium, mfem::Vector &preconditioner, const std::array< EquilibriumCoordinateRange, Size> &ranges ) { for (const auto &range : ranges) { for (int index = 0; index < range.size; ++index) { preconditioner(range.preconditionerOffset + index) = equilibrium(range.equilibriumOffset + index); } } } template static void PreconditionerToEquilibrium( const mfem::Vector &preconditioner, mfem::Vector &equilibrium, const std::array< EquilibriumCoordinateRange, Size> &ranges ) { for (const auto &range : ranges) { for (int index = 0; index < range.size; ++index) { equilibrium(range.equilibriumOffset + index) = preconditioner(range.preconditionerOffset + index); } } } static void VerifySizes( const mfem::Vector &source, const int expectedSourceSize, const mfem::Vector &destination, const int expectedDestinationSize, const char *operation ) { if (source.Size() != expectedSourceSize || destination.Size() != expectedDestinationSize) { throw std::invalid_argument( std::string("The equilibrium preconditioner ") + operation + " received an incompatible vector." ); } } std::array m_correctionRanges; std::array m_residualRanges; int m_equilibriumStateSize; int m_equilibriumResidualSize; int m_preconditionerCorrectionSize; int m_preconditionerResidualSize; mutable EquilibriumCoordinateMapStatistics m_statistics; }; struct PreparedStellarPreconditionerStatistics final { std::uint64_t applications{0}; std::uint64_t residualCoordinateMappings{0}; std::uint64_t correctionCoordinateMappings{0}; }; template requires EquilibriumCoordinateComponentFor::FormType> class PreparedStellarPreconditioner final : public mfem::Solver { private: using ProblemType = std::remove_cvref_t; using BlockType = std::remove_cvref_t; public: using Form = typename ProblemType::FormType; using BackendType = typename BlockType::BackendType; using GroupedPreconditioner = PreparedSpecificationBorderBlock; using CoordinateMap = EquilibriumPreconditionerCoordinateMap; PreparedStellarPreconditioner( const ProblemType &problem, BlockType block ) : mfem::Solver(problem.StateSize()), m_grouped( problem, std::move(block) ), m_coordinates(problem.GetManifest().layout()), m_groupedResidual(m_coordinates.PreconditionerResidualSize()), m_groupedCorrection(m_coordinates.PreconditionerCorrectionSize()) { if (problem.StateSize() != problem.EquationSize() || m_grouped.Height() != problem.StateSize() || m_grouped.Width() != problem.EquationSize()) { throw std::logic_error( "The prepared stellar preconditioner is incompatible with the complete equilibrium operator." ); } } PreparedStellarPreconditioner(const PreparedStellarPreconditioner &) = delete; PreparedStellarPreconditioner &operator=(const PreparedStellarPreconditioner &) = delete; PreparedStellarPreconditioner(PreparedStellarPreconditioner &&) = delete; PreparedStellarPreconditioner &operator=(PreparedStellarPreconditioner &&) = delete; void SetOperator(const mfem::Operator &operation) override { if (operation.Height() != Height() || operation.Width() != Width()) { throw std::invalid_argument( "The prepared stellar preconditioner received an incompatible equilibrium operator." ); } m_grouped.SetOperator(operation); } void Mult( const mfem::Vector &equilibriumResidual, mfem::Vector &equilibriumCorrection ) const override { if (equilibriumResidual.Size() != Width() || equilibriumCorrection.Size() != Height()) { throw std::invalid_argument( "The prepared stellar preconditioner requires compatible, preallocated equilibrium vectors." ); } m_coordinates.PackResidual(equilibriumResidual, m_groupedResidual); ++m_statistics.residualCoordinateMappings; m_grouped.Mult(m_groupedResidual, m_groupedCorrection); m_coordinates.UnpackCorrection(m_groupedCorrection, equilibriumCorrection); ++m_statistics.correctionCoordinateMappings; ++m_statistics.applications; } [[nodiscard]] SpecificationBorderBlockPreparationReport Refresh() { return m_grouped.Refresh(); } [[nodiscard]] bool IsCurrent() const { return m_grouped.IsCurrent(); } [[nodiscard]] const BlockType &GetBlock() const noexcept { return m_grouped.GetBlock(); } [[nodiscard]] const GroupedPreconditioner &GetGroupedPreconditioner() const noexcept { return m_grouped; } [[nodiscard]] const CoordinateMap &GetCoordinateMap() const noexcept { return m_coordinates; } [[nodiscard]] const PreparedStellarPreconditionerStatistics &GetStatistics() const noexcept { return m_statistics; } private: GroupedPreconditioner m_grouped; CoordinateMap m_coordinates; mutable mfem::Vector m_groupedResidual; mutable mfem::Vector m_groupedCorrection; mutable PreparedStellarPreconditionerStatistics m_statistics; }; template < equilibrium::DiscretizedStellarEquilibriumProblem Problem, SpecificationBorderBlockType Block> requires EquilibriumCoordinateComponentFor< Block, typename std::remove_cvref_t::FormType> [[nodiscard]] auto prepare( const Problem &problem, Block block ) { return PreparedStellarPreconditioner{problem, std::move(block)}; } } // namespace mean_field::preconditioning