1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// SPDX-License-Identifier: GPL-3.0-only
// Copyright (C) 2026 OUTRAM PARK contributors
//
// This file is part of OUTRAM PARK. See `src/lib.rs` for the full licence
// notice.
//! Verification that operator-derived field names cannot grow without bound.
use *;
/// A first application keeps full diagnostic value.
///
/// **Methodology.** Apply [`derived_name`] and [`derived_name2`] to plain field
/// names and compare against the OpenFOAM-style spelling this crate used
/// before the guard existed. Pass criterion: exact string match, so the guard
/// costs nothing in the common case.
///
/// **Results (2026-08-12).** `grad(p)`, `interpolate(T)`, `div(phi,rho)` — all
/// unchanged from the pre-guard names. This matters: the guard would not be
/// worth having if it degraded the names a reader actually sees in a solver's
/// field list.
/// Repeated application reaches a fixed point — the actual guard.
///
/// **Methodology.** Simulate the self-feedback pattern that `fvc::div` permits
/// because it returns the same type it consumes (`psi = fvc::div(&phi, &psi)`),
/// iterating 1000 times and recording the name length at each step. Reference:
/// the requirement that a solver's field name be bounded independently of step
/// count. Pass criterion: the name is identical from step 2 onward and its
/// length never exceeds a small constant.
///
/// **Results (2026-08-12).** The sequence is
/// `rho` (3 chars) -> `div(phi,rho)` (12) -> `div(phi,..)` (11) -> `div(phi,..)`
/// (11) -> ... The length is **11 characters at step 1000**, identical to
/// step 2.
///
/// Without the guard the same 1000 steps would produce a name of roughly
/// 9000 characters and growing linearly forever — the same class of defect as
/// the exponential one that cost this project a 24 GB SIGTERM, differing only
/// in how long it takes to become fatal.
/// The single-operand helper reaches a fixed point too.
///
/// **Methodology.** Iterate [`derived_name`] 1000 times, as a solver would if
/// it reassigned a field from a same-type unary operator. Pass criterion:
/// identical from step 2 onward.
///
/// **Results (2026-08-12).** `p` -> `grad(p)` -> `grad(..)` -> `grad(..)`,
/// stable at **8 characters** through step 1000.
/// Only the derived operand is elided; a plain one survives.
///
/// **Methodology.** Feed a derived name as the FIRST operand and a plain name
/// as the second, and vice versa. Pass criterion: each operand is judged
/// independently, so a plain name is never lost just because its partner was
/// derived.
///
/// **Results (2026-08-12).** `div(..,rho)` and `div(phi,..)` — each side
/// elided independently, as intended. Collapsing both would have thrown away
/// diagnostic information the guard does not need to take.
/// Plain field names are never mistaken for derived ones.
///
/// **Methodology.** Run [`is_derived`] over names taken from real OpenFOAM
/// cases, including the dotted multiphase convention. Pass criterion: only
/// names containing a parenthesis are treated as derived.
///
/// **Results (2026-08-12).** `rho`, `U`, `p`, `T`, `alpha.water`, `p_rgh`,
/// `nut` all report plain; `div(phi,rho)`, `grad(p)`, `interpolate(T)` report
/// derived. A false positive here would silently degrade a legitimate field
/// name, which is why the plain cases are asserted explicitly rather than
/// assumed.