82 """The seam is the protocol, not the library behind it.
84 A deliberately absurd backend -- constant density, nothing else -- proves
85 the chain talks to the protocol and never to CoolProp specifically. This is
86 what a REFPROP or Modelica media backend would slot into.
89 class ConstantDensity:
90 """Answers 'rho' with one number and declines everything else."""
94 def __init__(self, value: float) ->
None:
97 def supports(self, prop: str) -> bool:
100 def update(self, pair: StatePair, v1: float, v2: float) ->
None:
103 def value(self, prop: str) -> float:
108 def phase(self) -> Phase:
111 def quality(self) -> float | None:
114 register_backend(
"constant_999",
lambda species: ConstantDensity(999.0))
115 assert "constant_999" in registered_backends()
117 n2 =
Fluid(
"nitrogen", chain=[
"constant_999",
"heos"])
119 assert n2.get(
"rho", p=1.0e7, T=300.0) == 999.0
121 assert n2.get(
"mu", p=1.0e7, T=300.0) == pytest.approx(1.9e-5, rel=0.2)
125 """The Phase 12 principle, working in Phase 01.
127 A measured grid in front of the equation of state overrides it inside the
128 measured region and falls through outside it -- and nobody writes the "do
129 we have data here?" branch, because the chain is that branch.
131 p_grid = np.array([1.0e6, 2.0e6, 3.0e6])
132 T_grid = np.array([280.0, 300.0, 320.0])
136 [[
Fluid(
"nitrogen").get(
"rho", p=p, T=T)
for T
in T_grid]
for p
in p_grid]
139 p_grid, T_grid, {
"rho": truth * 2.0}, source=
"CF-2026-03"
142 n2 =
Fluid(
"nitrogen", chain=[measured,
"bicubic",
"heos"])
143 assert n2.chain == (
"measured",
"bicubic",
"heos")
145 inside = n2.get(
"rho", p=2.0e6, T=300.0)
146 assert inside == pytest.approx(truth[1][1] * 2.0, rel=1e-9)
149 outside = n2.get(
"rho", p=1.0e7, T=300.0)
150 assert outside == pytest.approx(
Fluid(
"nitrogen").get(
"rho", p=1.0e7, T=300.0))
154 """An extrapolated measurement has a measurement's authority and a guess's
155 accuracy, so the backend must decline instead."""
157 [1.0e6, 2.0e6], [280.0, 300.0], {
"rho": np.ones((2, 2))}, source=
"unit-test"
159 with pytest.raises(OutOfRange, match=
"outside the measured grid"):
160 tab.update(StatePair.PT, 9.0e6, 290.0)
172 """Quantities this package never shipped can still flow through it."""
173 spec = register_property(
"fouling_factor",
"m^2.K/W",
"Heat exchanger fouling")
174 assert PROPERTIES[
"fouling_factor"]
is spec
179 {
"fouling_factor": np.full((2, 2), 1.7e-4)},
182 fluid =
Fluid(
"nitrogen", chain=[tab,
"heos"])
183 assert fluid.get(
"fouling_factor", p=5.0e5, T=300.0) == pytest.approx(1.7e-4)
185 with pytest.raises(ValueError, match=
"already registered"):
186 register_property(
"fouling_factor",
"-",
"duplicate")