.. _program_listing_file_src_pcms_field_value_view.hpp: Program Listing for File value_view.hpp ======================================= |exhale_lsh| :ref:`Return to documentation for file ` (``src/pcms/field/value_view.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #ifndef PCMS_FIELD_VALUE_VIEW_HPP #define PCMS_FIELD_VALUE_VIEW_HPP #include "pcms/field/coordinate_system.hpp" #include "pcms/utility/arrays.h" #include "pcms/utility/assert.h" #include #include #include #include #include #include #include namespace pcms { enum class FieldValueType { Scalar, Vector, Tensor }; // Index variance: Contravariant == components on the basis dx/dq^i, // Covariant == components on the dual grad q^i. enum class Variance : std::uint8_t { Contravariant, Covariant }; // How the components are scaled relative to the coordinate-induced basis. // // each component physical coordinates all have the same units // the natural coordinates are the standard, unscaled coordinate bases enum class ComponentScaling : std::uint8_t { Physical, Natural }; class VarianceSignature { public: static constexpr int max_rank = 4; VarianceSignature() = default; VarianceSignature(std::initializer_list entries) { if (entries.size() > static_cast(max_rank)) { throw pcms_error("VarianceSignature: rank must be 0.." + std::to_string(max_rank) + ", got " + std::to_string(entries.size())); } for (Variance v : entries) { entries_[static_cast(rank_++)] = v; } } [[nodiscard]] int Rank() const noexcept { return rank_; } [[nodiscard]] Variance operator[](int i) const noexcept { return entries_[static_cast(i)]; } friend bool operator==(const VarianceSignature& a, const VarianceSignature& b) noexcept { if (a.rank_ != b.rank_) { return false; } for (int i = 0; i < a.rank_; ++i) { if (a.entries_[static_cast(i)] != b.entries_[static_cast(i)]) { return false; } } return true; } private: std::array entries_{}; int rank_ = 0; }; struct ValueBasis { std::shared_ptr system = nullptr; ComponentScaling scaling = ComponentScaling::Physical; VarianceSignature variance{}; [[nodiscard]] int Rank() const noexcept { return variance.Rank(); } friend bool operator==(const ValueBasis&, const ValueBasis&) = default; }; [[nodiscard]] inline FieldValueType ValueTypeOfRank(int rank) { switch (rank) { case 0: return FieldValueType::Scalar; case 1: return FieldValueType::Vector; case 2: return FieldValueType::Tensor; default: break; } throw pcms_error("ValueTypeOfRank: no FieldValueType label for rank " + std::to_string(rank)); } [[nodiscard]] inline std::optional DerivedComponentScaling( const CoordinateSystem& system) noexcept { if (!system.HasOrthogonalBasis()) { return ComponentScaling::Natural; } if (system.HasUnitScaleFactors()) { return ComponentScaling::Physical; } return std::nullopt; } [[nodiscard]] inline bool SameValueBasis(const ValueBasis& a, const ValueBasis& b) noexcept { if (a.Rank() == 0 && b.Rank() == 0) { return true; } if (a.Rank() != b.Rank() || !SameCoordinateSystem(a.system, b.system) || a.scaling != b.scaling) { return false; } return a.scaling == ComponentScaling::Physical || a.variance == b.variance; } namespace detail { inline void CheckWrittenValueBasis(const char* context, const ValueBasis& claimed, const ValueBasis& written) { if (!SameValueBasis(claimed, written)) { throw pcms_error(std::string(context) + ": the output view's declared value type/basis does not " "match the basis this call writes"); } } inline void ValidateValueBasis(const ValueBasis& basis) { if (basis.Rank() == 0) { return; } if (basis.Rank() > 2) { throw pcms_error("ValidateValueBasis: rank-" + std::to_string(basis.Rank()) + " values are not yet supported (maximum supported rank " "is 2)"); } if (basis.system == nullptr) { throw pcms_error( "ValidateValueBasis: Vector/Tensor values require a basis coordinate " "system"); } if (basis.scaling == ComponentScaling::Physical && !basis.system->HasOrthogonalBasis()) { throw pcms_error( "ValidateValueBasis: coordinate system '" + std::string(basis.system->Kind()) + "' has a non-orthogonal basis, so physical components are undefined; " "use ComponentScaling::Natural, or borrow an orthogonal system's basis"); } } inline void ValidateValueSemantics(const ValueBasis& basis, int num_components) { ValidateValueBasis(basis); const int rank = basis.Rank(); if (rank == 0) { return; } int expected = 1; for (int i = 0; i < rank; ++i) { expected *= basis.system->Dimension(); } if (num_components != expected) { throw pcms_error("ValidateValueSemantics: rank-" + std::to_string(rank) + " values on coordinate system '" + std::string(basis.system->Kind()) + "' (dimension " + std::to_string(basis.system->Dimension()) + ") require " + std::to_string(expected) + " components, got " + std::to_string(num_components)); } } inline ValueBasis MakeValueBasis( const std::shared_ptr& space_system, VarianceSignature variance, std::shared_ptr system, // null == the space's own std::optional scaling) { if (variance.Rank() == 0) { return ValueBasis{}; } if (system == nullptr) { system = space_system; } ComponentScaling resolved; if (scaling.has_value()) { resolved = *scaling; } else if (auto derived = DerivedComponentScaling(*system)) { resolved = *derived; } else { throw pcms_error( "CreateFunction: coordinate system '" + std::string(system->Kind()) + "' admits both physical and natural components and they differ, so " "the scaling must be stated (pass ComponentScaling::Physical or " "ComponentScaling::Natural)"); } return ValueBasis{std::move(system), resolved, variance}; } } // namespace detail namespace values { inline const VarianceSignature Scalar{}; inline const VarianceSignature Vector{Variance::Contravariant}; inline const VarianceSignature Covector{Variance::Covariant}; inline const VarianceSignature Tensor{Variance::Contravariant, Variance::Contravariant}; [[nodiscard]] inline VarianceSignature Of( std::initializer_list variance) { return VarianceSignature{variance}; } } // namespace values // view for field values that is tagged with the coordinate system template > class ValueView { public: using element_type = ElementType; ValueView(ValueBasis basis, Rank2View values) : basis_(std::move(basis)), values_(values) { detail::ValidateValueSemantics(basis_, static_cast(values_.extent(1))); } template && std::is_same_v>> ValueView(const ValueView& other) : basis_(other.GetBasis()), values_(other.GetValues()) { } [[nodiscard]] int Rank() const noexcept { return basis_.Rank(); } [[nodiscard]] FieldValueType GetValueType() const { return ValueTypeOfRank(basis_.Rank()); } [[nodiscard]] const ValueBasis& GetBasis() const noexcept { return basis_; } [[nodiscard]] Rank2View GetValues() const noexcept { return values_; } [[nodiscard]] size_t extent(size_t r) const noexcept { return values_.extent(r); } [[nodiscard]] size_t size() const noexcept { return values_.size(); } // Index through GetValues(): a ValueView carries a shared_ptr and must not // be captured by a device kernel. decltype(auto) operator()(size_t i, size_t c) const = delete; private: ValueBasis basis_; Rank2View values_; }; } // namespace pcms #endif // PCMS_FIELD_VALUE_VIEW_HPP