nx_water_awg_test.nx source
↩ module page · 56 lines · 3924 B
1// nx_water_awg_test.nx -- the atmospheric-water-generation GATE for nx_water_awg: the psychrometric
2// condensation yield and energy model, the technology-class envelopes, and the harvester recommender
3// (humid -> condensation, desert -> sorbent, moderate -> passive capillary, too dry -> none).
4//
5// MIGRATED OFF A HAND-ROLLED VERDICT 2026-08-14 -- see the note in nx_water_quality_test for the full
6// reasoning. Short version: the exit code used to be the failed assertion NUMBER, so nothing outside
7// could read the outcome (no verdict line, no harness.jrnl frame) and the run stopped at the first
8// failure, hiding every later one. It now inherits nx_gate_verdict, so every tooth runs and the exit
9// code carries the verdict. Migrated per-tooth by hand: the automated D001 candidate collapses N teeth
10// into one boolean and reports passed 1/1, which cannot see a dropped conjunct.
11//
12// THE ENVELOPE TEETH ARE THE POINT. A recommender that always answered CONDENSATION would satisfy the
13// humid case; the desert, moderate and too-dry teeth are what force it to actually discriminate, and
14// the WA_NONE tooth is the one that proves it will REFUSE rather than invent a harvester for a climate
15// that cannot feed one.
16// license_tier: ORIGINAL expect_exit: 0
17
18import "nx_syscalls.nx"
19import "nx_gate_verdict.nx"
20import "nx_water_awg.nx"
21
22func main() -> i64 {
23 gv_head("nx_water_awg gate -- psychrometrics, technology envelopes, climate-to-harvester choice" as *u8)
24 let ctr: *i64 = gv_ctr()
25
26 // --- saturation vapour density table ---
27 gv_check("sat-vapour-density-30C-is-304" as *u8, wa_sat_vapor_density_x10(30) == 304, ctr)
28 gv_check("sat-vapour-density-20C-is-173" as *u8, wa_sat_vapor_density_x10(20) == 173, ctr)
29
30 // --- psychrometric condensation yield ---
31 gv_check("absolute-humidity-30C-70pct-is-212" as *u8, wa_abs_humidity_x10(30, 70) == 212, ctr)
32 gv_check("extractable-30C-70pct-coil-5C-is-144" as *u8, wa_extractable_x10(30, 70, 5) == 144, ctr)
33 // COOL AND DRY MUST YIELD NOTHING. Without this the model could return a positive extraction for
34 // every input and still pass every other yield tooth.
35 gv_check("neg-control-cool-dry-air-condenses-nothing" as *u8, wa_extractable_x10(20, 30, 5) == 0, ctr)
36 gv_check("daily-yield-100m3h-is-34560ml" as *u8, wa_daily_yield_ml(100, 144) == 34560, ctr)
37 gv_check("specific-energy-humid-case-is-208-wh-per-litre" as *u8, wa_specific_energy_whl(144) == 208, ctr)
38
39 // --- technology-class envelopes ---
40 gv_check("sorbent-MOF-works-at-20pct-RH" as *u8, wa_class_works(WA_SORBENT, 20) == 1, ctr)
41 // The paired opposite: the SAME humidity must be outside refrigeration's envelope. One envelope
42 // tooth alone proves nothing about whether the classes are actually distinguished.
43 gv_check("neg-control-refrigeration-does-NOT-work-at-20pct-RH" as *u8, wa_class_works(WA_CONDENSATION, 20) == 0, ctr)
44 gv_check("passive-capillary-costs-zero-energy" as *u8, wa_class_energy_whl(WA_PASSIVE_CAP) == 0, ctr)
45
46 // --- the recommender picks the right harvester per climate ---
47 gv_check("humid-climate-recommends-condensation" as *u8, wa_recommend(30, 70) == WA_CONDENSATION, ctr)
48 gv_check("desert-climate-recommends-sorbent" as *u8, wa_recommend(25, 20) == WA_SORBENT, ctr)
49 gv_check("moderate-climate-recommends-passive-capillary" as *u8, wa_recommend(20, 35) == WA_PASSIVE_CAP, ctr)
50 // REFUSAL OVER FABRICATION: a climate too dry for every class must return NONE, not the least-bad
51 // guess. This is the tooth that separates an honest recommender from one that always answers.
52 gv_check("neg-control-too-dry-for-any-class-returns-NONE" as *u8, wa_recommend(20, 10) == WA_NONE, ctr)
53
54 return gv_verdict("nx_water_awg_test" as *u8, ctr,
55 "psychrometric yield and energy, paired technology envelopes, and a climate recommender that refuses rather than guesses" as *u8)
56}