nx_ice_distribution_test.nx source
↩ module page · 215 lines · 11821 B
1// nx_ice_distribution_test.nx -- gate for the derived geometric coupling.
2//
3// THE CLAIM UNDER TEST is that nx_ice_recrystal's asserted coupling was not
4// merely unproven but WRONG IN A KNOWN DIRECTION, and that the right value
5// falls out of a size distribution as exact arithmetic.
6//
7// T4 is the measurement that matters: the number fraction of crystals lost
8// is far larger than the mass fraction melted, so a coupling of 1.0 -- which
9// is the claim that they are equal -- under-states how fast the mean size
10// grows. T5 puts a number on how wrong, and T6 states the direction as a
11// verdict rather than leaving a reader to work it out.
12//
13// T2 is the conservation law that keeps the rest honest: melting and
14// redepositing must not change total mass, and if it did, every growth
15// figure here would be an artefact of losing or inventing ice.
16// expect_exit: 0 license_tier: ORIGINAL
17import "nx_syscalls.nx"
18import "nx_ice_distribution.nx"
19import "nx_ice_recrystal.nx"
20
21func tw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
22func tn(v: i64) -> i64 { let b: *u8 = sys_mmap(24); var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } let t: *u8 = sys_mmap(24); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, b, k); return 0 }
23func iabs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
24
25func main() -> i64 {
26 var pass: i64 = 0
27 var total: i64 = 0
28
29 let s: *IceDist = dist_reference()
30
31 // --- T1 The reference profile is well formed and ASCENDING, which is a
32 // precondition of the melt walk rather than a convention: melting
33 // the wrong end would silently invert every result below. ---
34 total = total + 1
35 var ok1: i64 = 1
36 if dist_is_ascending(s) != 1 { ok1 = 0 }
37 if dist_total_count(s) <= 0 { ok1 = 0 }
38 if dist_total_mass(s) <= 0 { ok1 = 0 }
39 let mean0: i64 = dist_mean_diameter(s)
40 if mean0 < 150 { ok1 = 0 }
41 if mean0 > 350 { ok1 = 0 }
42 tw("T1 reference profile ascending, " as *u8); tn(dist_total_count(s))
43 tw(" crystals, mean D=" as *u8); tn(mean0); tw(" (0.1um): " as *u8)
44 if ok1 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
45
46 // --- T2 ***MASS CONSERVATION.*** Melting and redepositing moves ice
47 // between crystals; it must not create or destroy any. Every
48 // growth number in this gate is meaningless if this fails. ---
49 total = total + 1
50 let s2: *IceDist = dist_reference()
51 let m_before: i64 = dist_total_mass(s2)
52 dist_apply_cycle(s2, 100)
53 let m_after: i64 = dist_total_mass(s2)
54 let drift: i64 = iabs(m_after - m_before) * 1000 / m_before
55 tw("T2 mass before=" as *u8); tn(m_before); tw(" after a 100permil cycle=" as *u8); tn(m_after)
56 // The tolerance is not a fudge: diameters are quantised to 0.1 um, so a
57 // bin's volume carries up to 3 x 0.05/d relative error after rounding --
58 // about 7 permil for a 20 um crystal, partly cancelling across bins.
59 // Anything beyond that is a leak, not quantisation, and 175 permil is
60 // what this check caught on the first run.
61 tw(", drift=" as *u8); tn(drift); tw(" permil (quantisation budget 15): " as *u8)
62 if drift <= 15 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
63
64 // --- T3 A cycle COARSENS: the mean diameter must rise, and the crystal
65 // count must fall. Those are two independent consequences of the
66 // same mechanism, so checking both is not redundant. ---
67 total = total + 1
68 let mean_after: i64 = dist_mean_diameter(s2)
69 let cnt_after: i64 = dist_total_count(s2)
70 tw("T3 after one cycle: mean D " as *u8); tn(mean0); tw("->" as *u8); tn(mean_after)
71 tw(", count " as *u8); tn(dist_total_count(s)); tw("->" as *u8); tn(cnt_after); tw(": " as *u8)
72 var ok3: i64 = 1
73 if mean_after <= mean0 { ok3 = 0 }
74 if cnt_after >= dist_total_count(s) { ok3 = 0 }
75 if ok3 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
76
77 // --- T4 ***THE MEASUREMENT THAT KILLS THE ASSERTION.*** Melting 100
78 // permil of the MASS removes far more than 100 permil of the
79 // COUNT, because small crystals are numerous and light. A
80 // coupling of 1.0 is precisely the claim that these two numbers
81 // are equal. ---
82 total = total + 1
83 let phi: i64 = 100
84 let nu: i64 = dist_number_fraction_permil(s, phi)
85 tw("T4 melting " as *u8); tn(phi); tw(" permil of the MASS removes " as *u8); tn(nu)
86 tw(" permil of the COUNT -- a coupling of 1.0 claims these are equal: " as *u8)
87 var ok4: i64 = 1
88 if nu <= phi { ok4 = 0 }
89 if nu >= 1000 { ok4 = 0 }
90 if ok4 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
91
92 // --- T5 ***HOW WRONG, AS A NUMBER.*** The derived coupling against the
93 // constant nx_ice_recrystal asserts. ---
94 total = total + 1
95 let derived: i64 = dist_derived_coupling(s, phi)
96 let err: i64 = dist_coupling_error_permil(s, phi, IR_COUPLING)
97 tw("T5 derived coupling=" as *u8); tn(derived)
98 tw(" vs asserted IR_COUPLING=" as *u8); tn(IR_COUPLING)
99 tw(" -> assertion is off by " as *u8); tn(err); tw(" permil: " as *u8)
100 var ok5: i64 = 1
101 if derived <= IR_COUPLING { ok5 = 0 }
102 if err >= 0 { ok5 = 0 }
103 if ok5 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
104
105 // --- T6 ***THE DIRECTION, AS A VERDICT.*** The asserted constant
106 // UNDER-states damage, which is the direction that reports a
107 // product as fine when it is not. A consumer must not have to
108 // infer this from the sign of a number. ---
109 total = total + 1
110 let conservative: i64 = dist_asserted_is_conservative(s, phi, IR_COUPLING)
111 tw("T6 is the asserted coupling conservative? " as *u8); tn(conservative)
112 tw(" (0 = it UNDER-states damage, the unsafe direction): " as *u8)
113 if conservative == 0 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
114
115 // --- T7 NON-VACUITY of T6: the verdict must be capable of saying yes.
116 // Handed a genuinely conservative constant it must report 1, so
117 // T6 is measuring the constant and not hard-wired to complain. ---
118 total = total + 1
119 let big: i64 = derived * 2
120 let conservative2: i64 = dist_asserted_is_conservative(s, phi, big)
121 tw("T7 handed a deliberately large coupling (" as *u8); tn(big)
122 tw(") the same check reports conservative=" as *u8); tn(conservative2); tw(" (want 1): " as *u8)
123 if conservative2 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
124
125 // --- T8 The coupling DEPENDS ON THE DISTRIBUTION, which is the whole
126 // reason it could never have been a universal constant. A
127 // narrow, uniform population loses far less of its count for the
128 // same mass melted than a broad one with a small-crystal tail. ---
129 total = total + 1
130 let narrow: *IceDist = dist_new(3)
131 dist_set(narrow, 0, 240, 1000)
132 dist_set(narrow, 1, 250, 1000)
133 dist_set(narrow, 2, 260, 1000)
134 let c_narrow: i64 = dist_derived_coupling(narrow, phi)
135 tw("T8 broad profile coupling=" as *u8); tn(derived)
136 tw(" vs narrow profile coupling=" as *u8); tn(c_narrow)
137 tw(" -- not a universal constant: " as *u8)
138 var ok8: i64 = 1
139 if c_narrow == DIST_INVALID { ok8 = 0 }
140 if c_narrow >= derived { ok8 = 0 }
141 if ok8 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
142
143 // --- T9 Zero melt is zero growth, exactly. The control that proves the
144 // growth tracks the excursion rather than being applied to every
145 // distribution the model touches. ---
146 total = total + 1
147 let s9: *IceDist = dist_reference()
148 let mean9: i64 = dist_mean_diameter(s9)
149 dist_apply_cycle(s9, 0)
150 var ok9: i64 = 1
151 if dist_mean_diameter(s9) != mean9 { ok9 = 0 }
152 if dist_number_fraction_permil(s, 0) != 0 { ok9 = 0 }
153 if dist_volume_growth_permil(s, 0) != 0 { ok9 = 0 }
154 tw("T9 a zero-amplitude cycle changes nothing at all: " as *u8)
155 if ok9 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
156
157 // --- T10 FAIL-CLOSED, and the reference profile is FLAGGED. A caller
158 // using the built-in shape rather than measured bin counts must
159 // be able to tell -- otherwise a shape-correct answer reads as a
160 // measured one. ---
161 total = total + 1
162 var ok10: i64 = 1
163 if dist_number_fraction_permil(s, 0 - 1) != DIST_INVALID { ok10 = 0 }
164 if dist_number_fraction_permil(s, 1001) != DIST_INVALID { ok10 = 0 }
165 if dist_derived_coupling(s, 0) != DIST_INVALID { ok10 = 0 }
166 if dist_profile_is_reference() != 1 { ok10 = 0 }
167 let bad: *IceDist = dist_new(2)
168 dist_set(bad, 0, 300, 10)
169 dist_set(bad, 1, 100, 10)
170 if dist_count_melted(bad, 100) != DIST_INVALID { ok10 = 0 }
171 tw("T10 out-of-range phi refused, descending bins refused, reference profile flagged: " as *u8)
172 if ok10 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
173
174 // --- T11 CROSS-ORGAN AGREEMENT. Feeding the DERIVED coupling into
175 // nx_ice_recrystal must produce more growth than the asserted one
176 // did, by the margin T5 measured. The two organs are independent
177 // paths to the same quantity, so agreement is evidence. ---
178 total = total + 1
179 let with_asserted: i64 = ir_size_after_cycles(250, phi, 10)
180 let growth_derived: i64 = dist_volume_growth_permil(s, phi)
181 var v: i64 = 250 * 250 * 250
182 var c: i64 = 0
183 while c < 10 { v = v + v * growth_derived / 1000; c = c + 1 }
184 let with_derived: i64 = ir_icbrt(v)
185 tw("T11 after 10 cycles: asserted coupling gives D=" as *u8); tn(with_asserted)
186 tw(", derived coupling gives D=" as *u8); tn(with_derived)
187 tw(" -- the assertion was optimistic: " as *u8)
188 if with_derived > with_asserted { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
189
190 // --- T12 ***THE CORRECTION TO A PUBLISHED NUMBER.*** Wave 26 published
191 // the realistic -20/-16 C case (33 permil of the ice cycling,
192 // 17 excursions over 60 days) using the ASSERTED coupling and
193 // reported 43.6 um. Recomputed with the coupling derived from
194 // the distribution, the real figure is worse. Both are printed
195 // so the size of the correction is visible rather than quietly
196 // replaced. ---
197 total = total + 1
198 let phi_real: i64 = 33
199 let g_real: i64 = dist_volume_growth_permil(s, phi_real)
200 let pub: i64 = ir_size_combined(250, 537500, 60, phi_real, 17)
201 let corrected: i64 = ir_size_combined_growth(250, 537500, 60, g_real, 17)
202 tw("T12 realistic -20/-16C over 60d: published(asserted coupling) D=" as *u8); tn(pub)
203 tw(" corrected(derived coupling) D=" as *u8); tn(corrected)
204 tw(" per-cycle growth=" as *u8); tn(g_real); tw(" permil; grades " as *u8)
205 tn(ir_texture_grade(pub)); tw(" -> " as *u8); tn(ir_texture_grade(corrected)); tw(": " as *u8)
206 var ok12: i64 = 1
207 if g_real <= 0 { ok12 = 0 }
208 if corrected <= pub { ok12 = 0 }
209 if ir_texture_grade(corrected) < ir_texture_grade(pub) { ok12 = 0 }
210 if ok12 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) }
211
212 tw("ICE-DISTRIBUTION-GATE passed " as *u8); tn(pass); tw("/" as *u8); tn(total)
213 if pass == total { tw(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 }
214 tw(" verdict=RED\n" as *u8); sys_exit(1); return 1
215}