98def _time_us(call: object, n: int = ITERATIONS) -> float:
99 """Microseconds per call in the fastest of :data:`BATCHES` batches, after a
100 warm-up that excludes table build.
102 ``call`` takes the iteration index and is expected to use it to move the
103 state point -- see :data:`_STEP_K`. The index runs on across batches, so no
104 batch repeats another's state points.
108 per =
max(1, n // BATCHES)
110 for b
in range(BATCHES):
111 started = time.perf_counter()
112 for i
in range(b * per, (b + 1) * per):
114 best =
min(best, (time.perf_counter() - started) / per)
118@pytest.mark.parametrize("prop", ["rho", "mu", "cp", "Z"])
237 """One measured table, many threads.
239 Unlike the CoolProp backends, a tabulated one cannot be rebuilt per thread
240 -- it is parameterised by its data, not by a species name -- so the single
241 instance is shared and only its cursor is thread-local. This asserts that
248 p_grid = np.array([1.0e6, 2.0e6, 3.0e6])
249 T_grid = np.array([280.0, 300.0, 320.0])
250 values = np.array([[p * T
for T
in T_grid]
for p
in p_grid])
251 tab = TabulatedProperties(p_grid, T_grid, {
"rho": values}, source=
"unit-test")
253 fluid =
Fluid(
"nitrogen", chain=[tab])
254 failures: list[str] = []
255 barrier = threading.Barrier(3)
257 def hammer(p: float, T: float) ->
None:
259 for _
in range(2_000):
260 if fluid.get(
"rho", p=p, T=T) != pytest.approx(p * T, rel=1e-9):
261 failures.append(f
"p={p} T={T}")
265 threading.Thread(target=hammer, args=args)
266 for args
in [(1.0e6, 280.0), (2.0e6, 300.0), (3.0e6, 320.0)]
273 assert not failures, failures[:5]
277 """The property memo is an *exact* cache, and has to stay one.
279 No rounding, no tolerance, no quantised state -- a hit returns the number
280 the backend would have returned, and the only thing saved is asking again.
281 That is what makes it safe to put in front of a solve: it cannot move an
282 answer, only shorten the path to it.
284 Checked against a fluid with no memo of its own, so the comparison is
285 against the backend rather than against another cache.
287 warm =
Fluid(
"nitrogen")
288 for prop
in (
"rho",
"mu",
"cp",
"Z"):
289 first = warm.get(prop, p=1.0e7, T=300.0)
290 again = warm.get(prop, p=1.0e7, T=300.0)
291 cold =
Fluid(
"nitrogen").get(prop, p=1.0e7, T=300.0)
292 assert again == first, f
"{prop}: memo returned a different number"
293 assert first == cold, f
"{prop}: memo diverged from the backend"
304 walked =
Fluid(
"nitrogen")
305 values = [walked.get(
"rho", p=1.0e7, T=300.0 + i * 1.0e-3)
for i
in range(64)]
306 assert len(set(values)) == len(
308 ),
"the memo merged distinct state points; the key is being rounded"
309 fresh =
Fluid(
"nitrogen")
311 fresh.get(
"rho", p=1.0e7, T=300.0 + i * 1.0e-3)
for i
in range(64)
312 ],
"a walked sweep disagreed with a cold one"