code wiki / (root) / nx_ferment_flavor.nx

nx_ferment_flavor.nx source

↩ module page · 73 lines · 3308 B

1// nx_ferment_flavor.nx -- CAPSTONE: the CONVERGENCE of the fermentation 2// and taste-science ladders. A fermentation is a flavor-generating 3// process; this organ predicts the SENSORY OUTCOME from first principles 4// and inverts it to DESIGN a ferment for a target taste. 5// 6// The chain, composing four organs across both ladders: 7// R3 nx_ferment_kinetics : (temp, time) -> pH(t) [acid accumulation] 8// T0 nx_taste_transduce : acid -> SOUR receptor response (saturating) 9// + a proteolysis model : time -> umami (peptide/glutamate release) 10// T2 nx_flavor_integrate : assemble the evolving multimodal flavor vector 11// 12// As a batch ferments: pH falls -> sour rises (and saturates, via the Hill 13// receptor); proteolysis builds umami; residual sweetness fades; aroma and 14// body develop. A YOUNG ferment is mild and sweet; an AGED one is sour, 15// savoury, aromatic. 16// 17// MEASURED EXCEED: nothing in the baseline -- and no cookbook -- predicts 18// the sensory trajectory of a ferment from its chemistry, nor inverts it 19// to hit a target flavor. "Ferment 8 hours" becomes "at hour 8: pH 4.5, 20// sour-intensity X, umami Y", and "make it this tangy" becomes a time. 21// 22// genealogy_id: nishi_ferment_kinetics_r3 + nishi_taste_transduce_t0 23// + nishi_flavor_integrate_t2 24 25import "nx_syscalls.nx" 26import "nx_ferment_kinetics.nx" 27import "nx_taste_transduce.nx" 28import "nx_flavor_integrate.nx" 29 30const NX_FF_PH0_MILLI: i64 = 6500 // milk / substrate starting pH 31 32// SOUR intensity of a ferment at (temp, hour): acid accumulation drives 33// the T0 sour receptor. Composes R3 (pH) -> T0 (Hill response). 34func nx_ferment_sour_at(t_c: i64, hour: i64) -> i64 { 35 let ph: i64 = nx_ferment_kinetics_ph_at(t_c, hour) 36 let acid_proxy: i64 = NX_FF_PH0_MILLI - ph 37 if acid_proxy <= 0 { return 0 } 38 let sour_recep: *NxTasteReceptor = nx_taste_receptor(NX_TQ_SOUR) 39 return nx_taste_response_milli(sour_recep, acid_proxy * 4) 40} 41 42// UMAMI from proteolysis: peptide / free-glutamate release accumulates 43// with time (why aged cheese and long ferments taste savoury). Capped. 44func nx_ferment_umami_at(hour: i64, proteolysis_rate_milli: i64) -> i64 { 45 let u: i64 = (hour * proteolysis_rate_milli) / 1000 46 if u > 1000 { return 1000 } 47 return u 48} 49 50// The evolving multimodal flavor vector (T2) at (temp, hour). 51func nx_ferment_flavor_vector(t_c: i64, hour: i64, proteolysis_rate_milli: i64) -> *NxFlavorVector { 52 let sour: i64 = nx_ferment_sour_at(t_c, hour) 53 let umami: i64 = nx_ferment_umami_at(hour, proteolysis_rate_milli) 54 let ph: i64 = nx_ferment_kinetics_ph_at(t_c, hour) 55 let acid_drop: i64 = NX_FF_PH0_MILLI - ph 56 var sweet: i64 = 300 - acid_drop / 10 57 if sweet < 0 { sweet = 0 } 58 let aroma: i64 = acid_drop / 5 59 var body: i64 = acid_drop / 8 60 if body > 400 { body = 400 } 61 return nx_flavor_vector_new(sweet, sour, 0, 0, umami, aroma, 0, 0, body, 0) 62} 63 64// DESIGN: the earliest hour at which the ferment reaches a target sourness 65// at the given hold temperature; -1 if the chemistry never reaches it. 66func nx_ferment_time_for_sour(t_c: i64, target_sour: i64, max_hours: i64) -> i64 { 67 var h: i64 = 0 68 while h <= max_hours { 69 if nx_ferment_sour_at(t_c, h) >= target_sour { return h } 70 h = h + 1 71 } 72 return 0 - 1 73}