code wiki / (root) / nx_food_chem_react.nx

nx_food_chem_react.nx source

↩ module page · 131 lines · 5232 B

1// nx_food_chem_react.nx -- FOOD-SCIENCE SUITE / COOKING CHEMISTRY rung. 2// Predicts which browning/transformation reaction occurs from the actual 3// conditions (temperature, pH, reactants, oxygen) -- Maillard vs 4// caramelization vs enzymatic browning -- and gives the real thresholds, 5// where a recipe just says "cook until golden". 6// 7// All INTEGER threshold rules (temp in C, pH x1000). Grounded chemistry: 8// - Maillard: amino acids (protein) + REDUCING sugars, dry heat >= ~140 C 9// (below that little browning); alkaline pH accelerates it. 10// - Caramelization: sugar pyrolysis, NO protein needed; per-sugar onset 11// (fructose ~110, glucose/sucrose ~160, maltose ~180 C). 12// - Enzymatic browning: polyphenol oxidase (PPO) on cut produce + O2; 13// stopped by acid (pH<~3), heat (>=70 C denatures PPO), antioxidant 14// (vitamin C), or excluding O2 (submersion). 15// 16// THE exceed: it CLASSIFIES the browning correctly from conditions (a 17// seared steak = Maillard, caramel = caramelization, a cut apple = enzymatic) 18// and prescribes prevention -- generalising beyond a fixed recipe step. 19// 20// Grounding (cited; researcher-groundable): 21// maillard_reaction_amino_reducing_sugar_140C 22// caramelization_onset_temperatures_by_sugar 23// polyphenol_oxidase_enzymatic_browning_control 24// 25// genealogy_id: food_chemistry_reactions + nishi_food_science_suite 26 27import "nx_syscalls.nx" 28const FCR_MAGIC_7000: i64 = 7000 29 30// ===== Thresholds (grounded) ====================================== 31 32const FCR_MAILLARD_TEMP_C: i64 = 140 // dry-heat onset 33const FCR_PPO_INHIBIT_PH: i64 = 3000 // pH < 3.0 (acid) stops PPO 34const FCR_PPO_DENATURE_C: i64 = 70 // >= 70 C denatures PPO (blanch) 35const FCR_ALKALINE_PH: i64 = 7000 // pH > 7 accelerates Maillard 36 37// ===== Sealed enums =============================================== 38 39const FCR_NONE: i64 = 0 40const FCR_MAILLARD: i64 = 1 41const FCR_CARAMELIZATION: i64 = 2 42const FCR_ENZYMATIC_BROWNING: i64 = 3 43 44const FCR_SUGAR_FRUCTOSE: i64 = 0 45const FCR_SUGAR_GLUCOSE: i64 = 1 46const FCR_SUGAR_SUCROSE: i64 = 2 47const FCR_SUGAR_MALTOSE: i64 = 3 48 49// ===== Atomic reaction predicates ================================= 50 51// Caramelization onset temperature for a sugar (C); unknown -> unreachable. 52func fcr_caramelization_temp(sugar_id: i64) -> i64 { 53 if sugar_id == FCR_SUGAR_FRUCTOSE { return 110 } 54 if sugar_id == FCR_SUGAR_GLUCOSE { return 160 } 55 if sugar_id == FCR_SUGAR_SUCROSE { return 160 } 56 if sugar_id == FCR_SUGAR_MALTOSE { return 180 } 57 return 999 58} 59 60func fcr_maillard(has_protein: i64, has_reducing_sugar: i64, temp_c: i64) -> i64 { 61 if has_protein != 1 { return 0 } 62 if has_reducing_sugar != 1 { return 0 } 63 if temp_c < FCR_MAILLARD_TEMP_C { return 0 } 64 return 1 65} 66 67// Relative Maillard rate: rises with temperature above onset, boosted by 68// alkaline pH (integer, unitless relative score). 69func fcr_maillard_rate(temp_c: i64, ph_milli: i64) -> i64 { 70 if temp_c < FCR_MAILLARD_TEMP_C { return 0 } 71 var rate: i64 = temp_c - FCR_MAILLARD_TEMP_C 72 if ph_milli > FCR_ALKALINE_PH { rate = rate * 2 } 73 return rate 74} 75 76func fcr_caramelizes(sugar_id: i64, temp_c: i64) -> i64 { 77 if temp_c >= fcr_caramelization_temp(sugar_id) { return 1 } 78 return 0 79} 80 81func fcr_enzymatic_browning(has_ppo: i64, o2_present: i64, ph_milli: i64, 82 temp_c: i64, has_antioxidant: i64) -> i64 { 83 if has_ppo != 1 { return 0 } 84 if o2_present != 1 { return 0 } 85 if has_antioxidant == 1 { return 0 } 86 if ph_milli < FCR_PPO_INHIBIT_PH { return 0 } // acid inhibits (lemon) 87 if temp_c >= FCR_PPO_DENATURE_C { return 0 } // heat denatures (blanch) 88 return 1 89} 90 91// ===== Scenario classifier ======================================== 92 93// Bundled scenario (>6 loose args would risk the return-register clobber). 94struct NxCookScenario { 95 has_protein: i64, 96 has_reducing_sugar: i64, 97 has_sugar: i64, // any sugar (for caramelization) 98 has_ppo: i64, 99 o2_present: i64, 100 has_antioxidant: i64, 101 ph_milli: i64, 102 temp_c: i64, 103 sugar_id: i64, 104} 105 106func nx_cook_scenario_new() -> *NxCookScenario { 107 let s: *NxCookScenario = (sys_mmap(80)) as *NxCookScenario 108 s.has_protein = 0 109 s.has_reducing_sugar = 0 110 s.has_sugar = 0 111 s.has_ppo = 0 112 s.o2_present = 0 113 s.has_antioxidant = 0 114 s.ph_milli = FCR_MAGIC_7000 115 s.temp_c = 20 116 s.sugar_id = FCR_SUGAR_SUCROSE 117 return s 118} 119 120// The DOMINANT browning reaction for a scenario. 121func fcr_predict_browning(s: *NxCookScenario) -> i64 { 122 // Maillard dominates under dry high heat with protein + reducing sugar. 123 if fcr_maillard(s.has_protein, s.has_reducing_sugar, s.temp_c) == 1 { return FCR_MAILLARD } 124 // Otherwise sugar pyrolysis if hot enough. 125 if s.has_sugar == 1 { 126 if s.temp_c >= fcr_caramelization_temp(s.sugar_id) { return FCR_CARAMELIZATION } 127 } 128 // Otherwise low-temperature enzymatic browning on cut PPO produce. 129 if fcr_enzymatic_browning(s.has_ppo, s.o2_present, s.ph_milli, s.temp_c, s.has_antioxidant) == 1 { return FCR_ENZYMATIC_BROWNING } 130 return FCR_NONE 131}