code wiki / (root) / nx_thermal_profile_test.nx

nx_thermal_profile_test.nx source

↩ module page · 176 lines · 9667 B

1// nx_thermal_profile_test.nx -- gate for nx_thermal_profile. The headline is 2// T4: on a realistic retort curve the come-up and cool-down together deliver 3// MORE lethality than the hold itself, so a hold-only calculation does not 4// merely round down -- it discards the majority of the process. 5// expect_exit: 0 license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_pow10.nx" 8import "nx_thermal_process.nx" 9import "nx_thermal_profile.nx" 10 11func t_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 12func t_putn(v: i64) -> i64 { let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } let t: *u8 = sys_mmap(28); 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 { bb[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, bb, k); return 0 } 13 14// A realistic retort: 10 min come-up 100->121.1 C, 2.52 min hold (the 12D 15// cook), 10 min cool-down back to 100 C. 16func build_retort() -> *NxThermalProfile { 17 let p: *NxThermalProfile = nx_thermal_profile_new(64) 18 tpr_add_ramp(p, 100000, 121100, 600000, 10) 19 tpr_add_segment(p, TP_REF_STERIL_MILLI_C, 151200) 20 tpr_add_ramp(p, 121100, 100000, 600000, 10) 21 return p 22} 23 24func main() -> i64 { 25 var pass: i64 = 0 26 var total: i64 = 0 27 28 // --- T1 a single-segment profile reproduces tp_f0_ms exactly --- 29 total = total + 1 30 let one: *NxThermalProfile = nx_thermal_profile_new(4) 31 tpr_add_segment(one, TP_REF_STERIL_MILLI_C, 151200) 32 let f_prof: i64 = tpr_f0_ms(one) 33 let f_direct: i64 = tp_f0_ms(151200, TP_REF_STERIL_MILLI_C) 34 t_puts("T1 one segment: profile F0=" as *u8); t_putn(f_prof); t_puts(" direct F0=" as *u8); t_putn(f_direct); t_puts(" (identical): " as *u8) 35 if f_prof == f_direct { if f_prof == 151200 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 36 37 // --- T2 the retort profile is assembled as expected --- 38 total = total + 1 39 let r: *NxThermalProfile = build_retort() 40 let nseg: i64 = r.n 41 let ttime: i64 = tpr_total_time_ms(r) 42 let peak: i64 = tpr_peak_temp_milli(r) 43 t_puts("T2 segments=" as *u8); t_putn(nseg); t_puts(" total time=" as *u8); t_putn(ttime); t_puts(" ms peak=" as *u8); t_putn(peak); t_puts(" milli-C: " as *u8) 44 var ok2: i64 = 1 45 if nseg != 21 { ok2 = 0 } 46 if ttime != 1351200 { ok2 = 0 } 47 if peak != TP_REF_STERIL_MILLI_C { ok2 = 0 } 48 if ok2 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 49 50 // --- T3 the whole profile clears the 12D cook comfortably --- 51 total = total + 1 52 let f0: i64 = tpr_f0_ms(r) 53 let adeq: i64 = tpr_bot_cook_adequate(r) 54 let need: i64 = tp_bot_12d_ms() 55 t_puts("T3 profile F0=" as *u8); t_putn(f0); t_puts(" ms vs 12D floor " as *u8); t_putn(need); t_puts(" adequate=" as *u8); t_putn(adeq); t_puts(": " as *u8) 56 var ok3: i64 = 1 57 if f0 < 350000 { ok3 = 0 } 58 if f0 > 450000 { ok3 = 0 } 59 if adeq != 1 { ok3 = 0 } 60 if ok3 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 61 62 // --- T4 THE FINDING: the ramps out-deliver the hold --- 63 // Hold-only credits 151200 ms. The ramps add more than that again, so a 64 // hold-only process authority is not conservative-by-a-little: it throws 65 // away most of the lethality and prescribes over-processing. 66 total = total + 1 67 let credit: i64 = tpr_ramp_credit_ms(r, 151200) 68 t_puts("T4 hold-only credit=151200 ms, ramps add " as *u8); t_putn(credit); t_puts(" ms MORE than the hold itself: " as *u8) 69 var ok4: i64 = 1 70 if credit <= 151200 { ok4 = 0 } 71 if credit < 190000 { ok4 = 0 } 72 if credit > 300000 { ok4 = 0 } 73 if ok4 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 74 75 // --- T5 a come-up ALONE already approaches a 12D cook --- 76 total = total + 1 77 let up: *NxThermalProfile = nx_thermal_profile_new(16) 78 tpr_add_ramp(up, 100000, 121100, 600000, 10) 79 let f_up: i64 = tpr_f0_ms(up) 80 t_puts("T5 10-min come-up alone F0=" as *u8); t_putn(f_up); t_puts(" ms vs 12D " as *u8); t_putn(need); t_puts(" (same order of magnitude): " as *u8) 81 var ok5: i64 = 1 82 if f_up < 90000 { ok5 = 0 } 83 if f_up > 160000 { ok5 = 0 } 84 if ok5 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 85 86 // --- T6 temperature dominates time: the SAME 22.5 minutes held 15 C 87 // cooler delivers almost nothing --- 88 total = total + 1 89 let cool: *NxThermalProfile = nx_thermal_profile_new(4) 90 tpr_add_segment(cool, 106000, 1351200) 91 let f_cool: i64 = tpr_f0_ms(cool) 92 t_puts("T6 same 1351200 ms held at 106 C: F0=" as *u8); t_putn(f_cool); t_puts(" ms vs the retort's " as *u8); t_putn(f0); t_puts(" (far less, and inadequate): " as *u8) 93 var ok6: i64 = 1 94 if f_cool >= f0 { ok6 = 0 } 95 if tp_bot_cook_adequate(f_cool) != 0 { ok6 = 0 } 96 if ok6 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 97 98 // --- T7 lethality is ADDITIVE over segments --- 99 total = total + 1 100 let a1: *NxThermalProfile = nx_thermal_profile_new(4) 101 tpr_add_segment(a1, 118000, 300000) 102 let a2: *NxThermalProfile = nx_thermal_profile_new(4) 103 tpr_add_segment(a2, 115000, 200000) 104 let both: *NxThermalProfile = nx_thermal_profile_new(4) 105 tpr_add_segment(both, 118000, 300000) 106 tpr_add_segment(both, 115000, 200000) 107 let sum: i64 = tpr_f0_ms(a1) + tpr_f0_ms(a2) 108 let joint: i64 = tpr_f0_ms(both) 109 t_puts("T7 F0(A)+F0(B)=" as *u8); t_putn(sum); t_puts(" F0(A then B)=" as *u8); t_putn(joint); t_puts(" (equal): " as *u8) 110 if sum == joint { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 111 112 // --- T8 more ramp steps converge (the approximation is stable) --- 113 total = total + 1 114 let c5: *NxThermalProfile = nx_thermal_profile_new(128) 115 tpr_add_ramp(c5, 100000, 121100, 600000, 5) 116 let c20: *NxThermalProfile = nx_thermal_profile_new(128) 117 tpr_add_ramp(c20, 100000, 121100, 600000, 20) 118 let f5: i64 = tpr_f0_ms(c5) 119 let f20: i64 = tpr_f0_ms(c20) 120 var spread: i64 = f20 - f5 121 if spread < 0 { spread = 0 - spread } 122 let pct: i64 = spread * 100 / f20 123 t_puts("T8 ramp with 5 steps=" as *u8); t_putn(f5); t_puts(" with 20 steps=" as *u8); t_putn(f20); t_puts(" spread=" as *u8); t_putn(pct); t_puts("% (<15): " as *u8) 124 if pct < 15 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 125 126 // --- T9 FAIL-CLOSED: capacity overflow REFUSES, never truncates --- 127 // A silently truncated profile reports LESS lethality than the process 128 // delivers, and the operator would respond by cooking longer. 129 total = total + 1 130 let small: *NxThermalProfile = nx_thermal_profile_new(2) 131 tpr_add_segment(small, 120000, 1000) 132 tpr_add_segment(small, 120000, 1000) 133 let third: i64 = tpr_add_segment(small, 120000, 1000) 134 let s_valid: i64 = small.valid 135 let s_f0: i64 = tpr_f0_ms(small) 136 t_puts("T9 third segment into cap-2: rc=" as *u8); t_putn(third); t_puts(" valid=" as *u8); t_putn(s_valid); t_puts(" F0=" as *u8); t_putn(s_f0); t_puts(" want -1/0/-1: " as *u8) 137 var ok9: i64 = 1 138 if third != TPR_REFUSED { ok9 = 0 } 139 if s_valid != 0 { ok9 = 0 } 140 if s_f0 != TPR_REFUSED { ok9 = 0 } 141 if ok9 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 142 143 // --- T10 malformed segments and empty profiles refuse --- 144 total = total + 1 145 let z: *NxThermalProfile = nx_thermal_profile_new(4) 146 let rc_zero: i64 = tpr_add_segment(z, 120000, 0) 147 let empty: *NxThermalProfile = nx_thermal_profile_new(4) 148 let e_f0: i64 = tpr_f0_ms(empty) 149 let ramp0: *NxThermalProfile = nx_thermal_profile_new(4) 150 let rc_ramp: i64 = tpr_add_ramp(ramp0, 100000, 121100, 600000, 0) 151 t_puts("T10 zero-duration=" as *u8); t_putn(rc_zero); t_puts(" empty-profile F0=" as *u8); t_putn(e_f0); t_puts(" zero-step ramp=" as *u8); t_putn(rc_ramp); t_puts(" (all -1): " as *u8) 152 var ok10: i64 = 1 153 if rc_zero != TPR_REFUSED { ok10 = 0 } 154 if e_f0 != TPR_REFUSED { ok10 = 0 } 155 if rc_ramp != TPR_REFUSED { ok10 = 0 } 156 if ok10 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 157 158 // --- T11 log reductions of the whole profile against C. botulinum --- 159 total = total + 1 160 let logs: i64 = tpr_log_reductions_q3(r, TP_BOT_D_REF_MS, TP_REF_STERIL_MILLI_C, TP_BOT_Z_MILLI) 161 t_puts("T11 retort delivers logs x1000 = " as *u8); t_putn(logs); t_puts(" vs the 12000 that 12D means (>12000): " as *u8) 162 if logs > 12000 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 163 164 // --- T12 a pasteurisation profile integrates on its own z --- 165 total = total + 1 166 let zp: i64 = tp_pasteurization_z_milli() 167 let ht: *NxThermalProfile = nx_thermal_profile_new(8) 168 tpr_add_segment(ht, tp_sched_temp_milli(TP_SCHED_MIX_HTST), 15000) 169 let leth: i64 = tpr_lethality_ms(ht, tp_sched_temp_milli(TP_SCHED_MIX_HTST), zp) 170 t_puts("T12 ice-cream-mix HTST leg integrated at its own reference = " as *u8); t_putn(leth); t_puts(" ms, want 15000: " as *u8) 171 if leth == 15000 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 172 173 t_puts("THERMAL-PROFILE-GATE passed " as *u8); t_putn(pass); t_puts("/" as *u8); t_putn(total) 174 if pass == total { t_puts(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 175 t_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 176}