nx_soda_flavor.nx source
↩ module page · 167 lines · 7594 B
1// nx_soda_flavor.nx -- CRAFT-SODA rung F0: the FLAVOR-DESIGN model.
2//
3// The operator's curiosity point (3): "the best flavoring like a crystallized
4// lemon/lime so it's SMOOTH + EVEN + STRONG WITHOUT sugar or other things
5// hiding it." The grounded answer (WebSearch 2026-07-10):
6// - CITRIC acid = sharp/pointy/immediate sourness (a stronger acid -> a
7// bigger [H+] spike); MALIC acid = rounded/smooth/lingering (a weaker
8// acid). BLENDING them is the craft smoothness knob.
9// - a WHISPER of sweet AROMA (vanilla/ester) triggers olfactory-gustatory
10// synergism -> the brain perceives roundness/sweetness from aroma ALONE,
11// no sugar, and aroma ENHANCES clarity rather than masking it.
12// - SUGAR mutes + hides flavor; the operator forbids it.
13//
14// This organ composes two others -- nx_taste_transduce (the sour receptor
15// Hill curve) and nx_soda_carbonation (carbonic-acid [H+]) -- so perceived
16// sourness is the real receptor response to the COMBINED acid+CO2 stimulus,
17// not a naive sum. Everything integer-exact, no float.
18//
19// MEASURED EXCEEDS (gate-checked, see nx_soda_flavor_test):
20// - MALIC IS SMOOTHER: at equal molarity malic yields strictly less [H+]
21// (a softer sour) than citric -- why it rounds a blend.
22// - THE BLEND BEATS EITHER ALONE: a ~50:50 citric:malic blend scores higher
23// roundness than pure citric (harsh/pointy) OR pure malic (flat).
24// - AROMA ROUNDS WITHOUT SUGAR: a whisper of sweet aroma raises roundness
25// while clarity stays perfect (sugar = 0).
26// - CARBONATION IS A FLAVOR LEVER: adding CO2 raises BOTH perceived sourness
27// and aroma-lifted intensity.
28// - LIAR-KILL: a recipe that reaches for SUGAR to fake smoothness is
29// rejected as dishonest -- "nothing hiding it" enforced by construction
30// (sugar earns ZERO roundness credit; it only mutes clarity).
31//
32// genealogy_id: nishi_taste_transduce_2026 + citric_malic_acidulant_profiles
33// + olfactory_gustatory_synergism + nishi_soda_carbonation_2026
34
35import "nx_syscalls.nx"
36import "nx_taste_transduce.nx"
37import "nx_soda_carbonation.nx"
38
39// ===== scaled weak-acid constants (Ka1 * 1e9) ===================
40// [H+]_uM = isqrt(Ka_scaled * conc_mM). Citric (pKa1 3.13) is a STRONGER
41// acid than malic (pKa1 3.40): more [H+] per mole -> a sharper, pointier sour.
42const SODA_CITRIC_KA_SCALED: i64 = 740000
43const SODA_MALIC_KA_SCALED: i64 = 400000
44const SODA_SUGAR_TRACE_GL: i64 = 2 // g/L: above this, "sugar-free" is a lie
45
46// ===== recipe =====================================================
47struct NxSodaRecipe {
48 citric_mM: i64,
49 malic_mM: i64,
50 aroma_milli: i64, // sweet-aroma whisper dose (vanilla/ester)
51 sugar_g_per_l: i64, // the forbidden crutch
52 carbonation_volumes_milli: i64,
53 temp_c: i64,
54}
55
56// ===== acid -> sour stimulus ([H+], micromolar) =================
57func soda_citric_hplus_um(conc_mM: i64) -> i64 {
58 if conc_mM <= 0 { return 0 }
59 return soda_isqrt(SODA_CITRIC_KA_SCALED * conc_mM)
60}
61func soda_malic_hplus_um(conc_mM: i64) -> i64 {
62 if conc_mM <= 0 { return 0 }
63 return soda_isqrt(SODA_MALIC_KA_SCALED * conc_mM)
64}
65
66// perceived sourness (milli) of an [H+] stimulus, via the REAL sour receptor
67// Hill curve from nx_taste_transduce (threshold + saturation, not a linear add).
68func soda_sour_from_hplus(hplus_um: i64) -> i64 {
69 let r: *NxTasteReceptor = nx_taste_receptor(NX_TQ_SOUR)
70 return nx_taste_response_milli(r, hplus_um)
71}
72
73// ===== roundness / smoothness ===================================
74// the sweet-aroma whisper: a small dose earns full roundness credit; over-
75// aromatizing reads as artificial and the credit decays (a whisper, not a wall).
76func soda_aroma_round_bonus(aroma_milli: i64) -> i64 {
77 if aroma_milli <= 0 { return 0 }
78 if aroma_milli <= 200 { return aroma_milli }
79 return 200 - (aroma_milli - 200) / 4
80}
81
82// roundness (milli): a tent peaking where malic ~= citric (the blend rounds
83// citric's point without losing its brightness) PLUS the aroma whisper.
84// Sugar earns NOTHING here -- roundness cannot be bought with sweetener.
85func soda_smoothness_milli(citric_mM: i64, malic_mM: i64, aroma_milli: i64) -> i64 {
86 let total: i64 = citric_mM + malic_mM
87 if total <= 0 { return 0 }
88 let malic_frac: i64 = malic_mM * 1000 / total
89 var dev: i64 = malic_frac - 500
90 if dev < 0 { dev = 0 - dev }
91 var blend: i64 = 1000 - 2 * dev
92 if blend < 0 { blend = 0 }
93 var s: i64 = blend + soda_aroma_round_bonus(aroma_milli)
94 if s > 1000 { s = 1000 }
95 return s
96}
97
98// clarity (milli): sugar-free water is a clean canvas (1000); every gram of
99// sugar mutes + hides the citrus, dropping clarity.
100func soda_clarity_milli(sugar_g_per_l: i64) -> i64 {
101 if sugar_g_per_l <= 0 { return 1000 }
102 var c: i64 = 1000 - sugar_g_per_l * 8
103 if c < 0 { c = 0 }
104 return c
105}
106
107// ===== LIAR-KILL: honest-sugarfree predicate ====================
108// the operator's law: strong+smooth WITHOUT sugar hiding it. A recipe leaning
109// on sugar is rejected (0); a trace from the flavor crystals is tolerated.
110func soda_is_honest_sugarfree(sugar_g_per_l: i64) -> i64 {
111 if sugar_g_per_l <= SODA_SUGAR_TRACE_GL { return 1 }
112 return 0
113}
114
115// ===== recipe-level scores ======================================
116func soda_recipe_sour_milli(r: *NxSodaRecipe) -> i64 {
117 let carb_co2: i64 = r.carbonation_volumes_milli * SODA_MG_PER_VOLUME / 1000
118 let hp: i64 = soda_citric_hplus_um(r.citric_mM) + soda_malic_hplus_um(r.malic_mM) + soda_hplus_um(carb_co2)
119 return soda_sour_from_hplus(hp)
120}
121func soda_recipe_smoothness_milli(r: *NxSodaRecipe) -> i64 {
122 return soda_smoothness_milli(r.citric_mM, r.malic_mM, r.aroma_milli)
123}
124func soda_recipe_clarity_milli(r: *NxSodaRecipe) -> i64 {
125 return soda_clarity_milli(r.sugar_g_per_l)
126}
127func soda_recipe_honest(r: *NxSodaRecipe) -> i64 {
128 return soda_is_honest_sugarfree(r.sugar_g_per_l)
129}
130// intensity (milli): acid load + aroma, LIFTED by carbonation (the bubbles
131// carry citrus oil to the nose) -- so more fizz = stronger flavor per gram.
132func soda_recipe_intensity_milli(r: *NxSodaRecipe) -> i64 {
133 let base: i64 = r.citric_mM * 10 + r.malic_mM * 10 + r.aroma_milli
134 let enh: i64 = 1000 + r.carbonation_volumes_milli / 10
135 return base * enh / 1000
136}
137
138// ===== GENERATIVE design ========================================
139// design a SMOOTH, STRONG, SUGAR-FREE lemon/lime at a target sourness: hold
140// malic at ~45% (the roundness sweet-spot), grow the total acid until the
141// sour receptor reaches target, add an aroma whisper, sugar = 0 by construction.
142func soda_design_smooth(target_sour_milli: i64, temp_c: i64) -> *NxSodaRecipe {
143 let carb_vol: i64 = 4000
144 let carb_hplus: i64 = soda_hplus_um(carb_vol * SODA_MG_PER_VOLUME / 1000)
145 var a: i64 = 1
146 var chosen: i64 = 0
147 var found: i64 = 0
148 while a <= 80 {
149 let cit: i64 = a * 55 / 100
150 let mal: i64 = a * 45 / 100
151 let hp: i64 = soda_citric_hplus_um(cit) + soda_malic_hplus_um(mal) + carb_hplus
152 let sour: i64 = soda_sour_from_hplus(hp)
153 if found == 0 {
154 if sour >= target_sour_milli { chosen = a; found = 1 }
155 }
156 a = a + 1
157 }
158 if found == 0 { chosen = 80 }
159 let r: *NxSodaRecipe = (sys_mmap(48)) as *NxSodaRecipe
160 r.citric_mM = chosen * 55 / 100
161 r.malic_mM = chosen * 45 / 100
162 r.aroma_milli = 150
163 r.sugar_g_per_l = 0
164 r.carbonation_volumes_milli = carb_vol
165 r.temp_c = temp_c
166 return r
167}