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}