CoolProp 8.0.0
An open-source fluid property and humid air property database
SVDSurface.h
Go to the documentation of this file.
1#ifndef COOLPROP_SBTL_SVD_SURFACE_H
2#define COOLPROP_SBTL_SVD_SURFACE_H
3
4#include <memory>
5#include <string>
6#include <utility>
7#include <vector>
8
13
14namespace CoolProp {
15namespace sbtl {
16
17// An SVDSurface aggregates everything needed for a one-input-pair
18// property lookup on a single pure fluid:
19//
20// - a RegionAtlas over the (a, b) plane (LIQUID / VAPOR / SUPER /
21// ...) that tells us which region a query falls into;
22// - one SVDDecomposition per (region, property) pair, heap-allocated
23// so addresses stay stable across vector growth (Phase 2a learned
24// this the hard way — see SVDEvaluator.h's dangling-pointer note);
25// - an SVDEvaluator per decomposition;
26// - metadata: which input pair this surface represents (PT, HP, DU,
27// ...), the fluid name, and the list of output properties.
28//
29// Lookups go:
30//
31// user calls eval(prop, a, b)
32// ↓
33// atlas.find_region(a, b) → region index or -1
34// ↓
35// region.to_normalized(a, b) → (xi, eta) in [0,1]^2
36// ↓
37// SVDEvaluator::eval(xi_or_eta_x, xi_or_eta_y)
38// → exp(Σ S_k u_k v_k)
39// ↓
40// return ρ / h / s / ...
41//
42// The two-axis ordering for the SVD (which coordinate is x_grid and
43// which is y_grid) is fixed by SurfaceSpec at build time — see
44// SVDSurfaceFactory.h. By the convention used in Phase 2a's e2e tool
45// the SVD's x-axis is the *secondary* axis (the η normalized
46// secondary coordinate, in [0, 1]) and the y-axis is the *primary*
47// axis (the ξ primary coordinate, also in [0, 1]).
49{
50 public:
51 // Construct an empty surface. Add regions + per-region per-
52 // property decompositions via add_region_property_svd(). Finalise
53 // with seal() — after which no further mutations are allowed and
54 // the evaluators (lazily-constructed from the heap-allocated
55 // decompositions) become callable.
56 //
57 // The properties argument fixes the property dimension; every
58 // region must register exactly one decomposition per property in
59 // the same order. Reorderable downstream via property indexing.
60 SVDSurface(std::string fluid_name, ::CoolProp::input_pairs input_pair, std::vector<::CoolProp::parameters> properties);
61
62 // Non-copyable; the heap-resident decompositions are unique_ptr
63 // owned. Movable.
64 SVDSurface(const SVDSurface&) = delete;
65 SVDSurface& operator=(const SVDSurface&) = delete;
66 SVDSurface(SVDSurface&&) = default;
68 ~SVDSurface() = default;
69
70 // Add a new region. Returns the region index assigned by the
71 // underlying RegionAtlas. Once a region is added, its property
72 // decompositions must be supplied via add_region_property_svd
73 // before seal() is called.
74 std::size_t add_region(region::Region region);
75
76 // Register one property SVD for an already-added region.
77 // `region_idx` must match the value returned by add_region();
78 // `prop` must be one of the properties passed to the constructor.
79 void add_region_property_svd(std::size_t region_idx, ::CoolProp::parameters prop, svd::SVDDecomposition decomp);
80
81 // Lock the surface for evaluation. Validates that every region
82 // has registered every property. After seal() returns, eval()
83 // and resolve() are callable; add_* methods throw.
84 void seal();
85
86 // Lookup `prop` at (a, b). Returns NaN if (a, b) is outside every
87 // region of the atlas. Throws std::invalid_argument if `prop`
88 // isn't one of the properties this surface stores.
89 [[nodiscard]] double eval(::CoolProp::parameters prop, double a, double b) const;
90
91 // Two-step lookup for callers that want to query several
92 // properties at the same (a, b) without paying for atlas dispatch
93 // each time. Returns (region_idx, xi, eta). region_idx = -1 if
94 // outside every region.
96 {
98 double svd_x; // value to feed into SVDEvaluator's x axis
99 double svd_y; // value to feed into SVDEvaluator's y axis
100 };
101 [[nodiscard]] ResolvedPoint resolve(double a, double b) const noexcept;
102
103 // Fast path after resolve(): evaluate `prop` at a pre-computed
104 // (region, svd_x, svd_y). Caller responsible for ensuring
105 // region_idx >= 0.
106 [[nodiscard]] double eval_with_region(::CoolProp::parameters prop, int region_idx, double svd_x, double svd_y) const;
107
108 // Batched fast path: evaluate `n` properties at the same
109 // (region, svd_x, svd_y). All per-region per-property
110 // SVDEvaluators share the region's (x_grid, y_grid), so the
111 // locate() + Hermite-basis setup is done ONCE and amortized
112 // across the n property evals — a measurable speedup once n is
113 // ≥ 2. out[i] receives the value for props[i]. Throws on
114 // unknown property keys (i.e., props that aren't tabulated on
115 // this surface); see contains_property() for the gate.
116 void eval_with_region_multi(int region_idx, double svd_x, double svd_y, const ::CoolProp::parameters* props, std::size_t n, double* out) const;
117
118 // Convenience predicates.
119 [[nodiscard]] bool contains_property(::CoolProp::parameters prop) const noexcept;
120
121 // Read-only access.
122 [[nodiscard]] const std::string& fluid_name() const noexcept {
123 return fluid_name_;
124 }
125 [[nodiscard]] ::CoolProp::input_pairs input_pair() const noexcept {
126 return input_pair_;
127 }
128 [[nodiscard]] const std::vector<::CoolProp::parameters>& properties() const noexcept {
129 return properties_;
130 }
131 [[nodiscard]] const region::RegionAtlas& atlas() const noexcept {
132 return atlas_;
133 }
134 [[nodiscard]] bool sealed() const noexcept {
135 return sealed_;
136 }
137 [[nodiscard]] std::size_t region_count() const noexcept;
138
139 // Direct access to the heap-resident decomposition for a region /
140 // property — primarily for serialization. Throws if either
141 // index is out of range.
142 [[nodiscard]] const svd::SVDDecomposition& decomposition(std::size_t region_idx, ::CoolProp::parameters prop) const;
143
144 private:
145 // Lookup table: property → index into the per-region property
146 // array. Built once at construction.
147 [[nodiscard]] std::size_t property_index(::CoolProp::parameters prop) const;
148
149 std::string fluid_name_;
150 ::CoolProp::input_pairs input_pair_;
151 std::vector<::CoolProp::parameters> properties_;
152
153 region::RegionAtlas atlas_;
154
155 // Per-region storage. Sized to atlas_.size() after seal().
156 // decomps_[region_idx][property_idx] is the heap-resident SVDDecomposition.
157 std::vector<std::vector<std::unique_ptr<svd::SVDDecomposition>>> decomps_;
158 // Parallel evaluator vector, lazily filled in seal().
159 std::vector<std::vector<std::unique_ptr<svd::SVDEvaluator>>> evaluators_;
160
161 bool sealed_ = false;
162};
163
164} // namespace sbtl
165} // namespace CoolProp
166
167#endif // COOLPROP_SBTL_SVD_SURFACE_H