nx_preservation.nx source
↩ module page · 171 lines · 7616 B
1// nx_preservation.nx -- FOOD-SCIENCE SUITE / PRESERVATION rung. The
2// classic food-preservation science the ferment ladder didn't yet cover:
3// thermal death kinetics (D-value / z-value / the 12-D botulinum cook),
4// water activity (aw), and Leistner HURDLE technology -- with a
5// never-poison canning gate.
6//
7// All INTEGER-EXACT (no float, no precision cliff): decimal reduction and
8// z-value scaling are powers of ten (exact), water-activity + pH control
9// are threshold rules (exact). Quantities in milli-units:
10// time -> milli-minutes (mmin)
11// temp -> milli-Celsius (mC)
12// aw, pH -> x1000 (aw 0.850 = 850; pH 4.600 = 4600)
13// log-reductions -> milli-reductions (12 log = 12000)
14//
15// THE never-poison line (mirrors nx_ferment_safety, Global Rule 26): a
16// LOW-ACID food (pH >= 4.6, where C. botulinum can grow) may NEVER be
17// preserved by boiling-water-bath canning -- it MUST be pressure-canned to
18// achieve the 12-D botulinum cook. Structural refusal, liar-killed by the
19// gate (the #1 cause of home-canning botulism).
20//
21// THE measured exceed: a single-factor checker calls a food unsafe unless
22// pH<4.6 OR aw<0.85 alone. The HURDLE model correctly finds foods that
23// are stable because pH AND aw are BOTH moderately reduced together
24// (Leistner) -- how shelf-stable fermented sausage, aged cheese, and many
25// intermediate-moisture foods actually work.
26//
27// Grounding (cited; researcher-groundable like nx_probiome):
28// fda_21cfr113_114_low_acid_acidified_canning (pH 4.6 line)
29// c_botulinum_12D_D121_0.21min_z_10C (thermal death)
30// nchfp_pressure_vs_boiling_water_canning (method rule)
31// fda_water_activity_0.85_not_potentially_hazardous (aw pathogen floor)
32// leistner_hurdle_technology_combined_preservation (hurdle exceed)
33//
34// genealogy_id: nishi_ferment_safety_r0_never_poison + food_preservation_science
35
36import "nx_syscalls.nx"
37
38// ===== Canonical grounded constants ===============================
39
40const PV_BOTULINUM_PH_MILLI: i64 = 4600 // FDA acidified-foods line
41const PV_HURDLE_PH_MILLI: i64 = 5000 // upper pH for a hurdle-stable food
42const PV_AW_NO_GROWTH_MILLI: i64 = 600 // below this: NO microbial growth
43const PV_AW_NO_PATHOGEN_MILLI: i64 = 850 // below this: no pathogen (FDA non-TCS)
44const PV_AW_HURDLE_MILLI: i64 = 910 // upper aw for a hurdle-stable food
45const PV_AW_BOTULINUM_MILLI: i64 = 930 // C. botulinum minimum aw
46const PV_BOT_D121_MMIN: i64 = 210 // C. botulinum D at 121.1 C = 0.21 min
47const PV_BOT_Z_MILLI_C: i64 = 10000 // z-value = 10 C
48const PV_12D_TARGET_MREDUX: i64 = 12000 // 12 log reductions (botulinum cook)
49const PV_PASTEUR_TARGET_MREDUX: i64 = 5000 // 5 log (pasteurization pathogen target)
50const PV_BOT_MIN_F0_MMIN: i64 = 2520 // 12 x 0.21 = 2.52 min minimum process
51
52// ===== Sealed enums ===============================================
53
54const PV_AW_UNSAFE: i64 = 0 // aw >= 0.85: pathogens can grow
55const PV_AW_MOLD_ONLY: i64 = 1 // 0.60 <= aw < 0.85: no pathogen, spoilage molds
56const PV_AW_SHELF: i64 = 2 // aw < 0.60: nothing grows
57
58const PV_STABLE_ACID: i64 = 0 // pH < 4.6 (high-acid)
59const PV_STABLE_DRY: i64 = 1 // aw < 0.60
60const PV_STABLE_LOW_AW: i64 = 2 // aw < 0.85 (no pathogen)
61const PV_STABLE_HURDLE: i64 = 3 // combined pH + aw hurdle (Leistner)
62const PV_UNSTABLE: i64 = 4 // needs refrigeration or a kill-step
63
64const PV_METHOD_WATER_BATH: i64 = 0
65const PV_METHOD_PRESSURE_CAN: i64 = 1
66
67const PV_CAN_OK: i64 = 0
68const PV_CAN_REFUSED_LOW_ACID_WB: i64 = 1 // low-acid via water bath = botulism
69const PV_CAN_REFUSED_BAD_METHOD: i64 = 2
70
71// ===== Thermal death kinetics (integer-exact) =====================
72
73// 10^k for a non-negative integer k (guarded; 10^18 still fits i64).
74func pv_pow10(k: i64) -> i64 {
75 if k < 0 { return 0 }
76 if k > 18 { return 0 }
77 var v: i64 = 1
78 var i: i64 = 0
79 while i < k {
80 v = v * 10
81 i = i + 1
82 }
83 return v
84}
85
86// Log-reductions (x1000) achieved by an isothermal hold at a temperature
87// whose D-value is d_mmin. reductions = hold / D.
88func pv_reductions_milli(hold_mmin: i64, d_mmin: i64) -> i64 {
89 if d_mmin <= 0 { return 0 }
90 return hold_mmin * 1000 / d_mmin
91}
92
93// Does an isothermal process achieve the 12-D botulinum cook?
94func pv_is_12d(hold_mmin: i64, d_mmin: i64) -> i64 {
95 if pv_reductions_milli(hold_mmin, d_mmin) >= PV_12D_TARGET_MREDUX { return 1 }
96 return 0
97}
98
99// Minimum hold (mmin) to achieve a target number of log-reductions.
100func pv_min_process_mmin(d_mmin: i64, target_mredux: i64) -> i64 {
101 return d_mmin * target_mredux / 1000
102}
103
104// D-value after k z-value steps from the reference temperature. Hotter
105// (k>0) -> D shrinks 10x per step; colder (k<0) -> D grows 10x per step.
106// EXACT at integer z-steps (the honest, cliff-free core of z-value math).
107func pv_d_at_z_steps(d_ref_mmin: i64, k: i64) -> i64 {
108 if k == 0 { return d_ref_mmin }
109 if k > 0 { return d_ref_mmin / pv_pow10(k) }
110 return d_ref_mmin * pv_pow10(0 - k)
111}
112
113// ===== Water activity + hurdle stability ==========================
114
115func pv_water_activity_class(aw_milli: i64) -> i64 {
116 if aw_milli < PV_AW_NO_GROWTH_MILLI { return PV_AW_SHELF }
117 if aw_milli < PV_AW_NO_PATHOGEN_MILLI { return PV_AW_MOLD_ONLY }
118 return PV_AW_UNSAFE
119}
120
121// Microbiological shelf-stability from pH + aw. Flat guard layers, most
122// decisive single-factor lines first, then the combined hurdle.
123func pv_shelf_stable(ph_milli: i64, aw_milli: i64) -> i64 {
124 if ph_milli < PV_BOTULINUM_PH_MILLI { return PV_STABLE_ACID }
125 if aw_milli < PV_AW_NO_GROWTH_MILLI { return PV_STABLE_DRY }
126 if aw_milli < PV_AW_NO_PATHOGEN_MILLI { return PV_STABLE_LOW_AW }
127 // Leistner hurdle: neither single line met, but pH AND aw both moderately
128 // reduced -> the combination controls pathogens (aw < 0.93 alone stops
129 // C. botulinum; the lowered pH controls the rest).
130 if ph_milli < PV_HURDLE_PH_MILLI {
131 if aw_milli < PV_AW_HURDLE_MILLI { return PV_STABLE_HURDLE }
132 }
133 return PV_UNSTABLE
134}
135
136// Would a naive SINGLE-FACTOR checker (pH<4.6 OR aw<0.85) call this food
137// stable? Used to demonstrate the hurdle EXCEED.
138func pv_single_factor_stable(ph_milli: i64, aw_milli: i64) -> i64 {
139 if ph_milli < PV_BOTULINUM_PH_MILLI { return 1 }
140 if aw_milli < PV_AW_NO_PATHOGEN_MILLI { return 1 }
141 return 0
142}
143
144// 1 iff the hurdle model finds stability that the single-factor checker misses.
145func pv_hurdle_beats_single(ph_milli: i64, aw_milli: i64) -> i64 {
146 var hurdle: i64 = 0
147 if pv_shelf_stable(ph_milli, aw_milli) != PV_UNSTABLE { hurdle = 1 }
148 let single: i64 = pv_single_factor_stable(ph_milli, aw_milli)
149 if hurdle == 1 { if single == 0 { return 1 } }
150 return 0
151}
152
153// ===== Canning method + the never-poison gate =====================
154
155// The REQUIRED canning method for a food of a given pH.
156func pv_canning_method(ph_milli: i64) -> i64 {
157 if ph_milli < PV_BOTULINUM_PH_MILLI { return PV_METHOD_WATER_BATH }
158 return PV_METHOD_PRESSURE_CAN
159}
160
161// NEVER-POISON gate: refuse a low-acid food processed by boiling-water-bath
162// (it cannot reach the 12-D botulinum cook -> botulism). Sealed verdict.
163func pv_canning_admit(ph_milli: i64, method: i64) -> i64 {
164 if method != PV_METHOD_WATER_BATH {
165 if method != PV_METHOD_PRESSURE_CAN { return PV_CAN_REFUSED_BAD_METHOD }
166 }
167 if ph_milli >= PV_BOTULINUM_PH_MILLI {
168 if method == PV_METHOD_WATER_BATH { return PV_CAN_REFUSED_LOW_ACID_WB }
169 }
170 return PV_CAN_OK
171}