nx_chem_solution.nx source
↩ module page · 79 lines · 3134 B
1// nx_chem_solution.nx -- FOOD-SCIENCE SUITE / SOLUTION CHEMISTRY rung. The
2// aqueous chemistry food runs on: buffers (Henderson-Hasselbalch), the
3// pH/pOH relation, molar concentration, dilution (C1V1 = C2V2), and
4// titratable acidity. pH is the master variable of preservation and
5// fermentation, so this grounds the whole safety story in chemistry.
6//
7// INTEGER-EXACT. pH/pKa x1000; concentration in mM; volume in mL; mass in
8// mg; molar mass x10. Henderson-Hasselbalch is exact at decade buffer
9// ratios (log10 of a power of ten is an integer): pH = pKa + log_ratio.
10//
11// THE exceed: buffer pH, dilutions, and titratable acidity are COMPUTED
12// from chemistry; and cs_is_high_acid ties straight to the preservation /
13// canning botulinum line (pH < 4.6) -- one chemical source of truth.
14//
15// grounded: henderson_hasselbalch_equation + molar_concentration
16// + c1v1_c2v2_dilution + titratable_acidity_titration
17// genealogy_id: solution_chemistry + nishi_food_science_suite
18
19import "nx_syscalls.nx"
20const CS_MAGIC_14000: i64 = 14000
21const CS_MAGIC_10000: i64 = 10000
22const CS_MAGIC_100000: i64 = 100000
23
24const CS_HIGH_ACID_PH_MILLI: i64 = 4600 // FDA acidified-foods line
25
26// ===== Buffers (Henderson-Hasselbalch) ============================
27//
28// pH = pKa + log10([A-]/[HA]). log_ratio is the integer base-10 log of the
29// conjugate-base : acid ratio (0 for 1:1, +1 for 10:1, -1 for 1:10) -- exact.
30
31func cs_buffer_ph_milli(pka_milli: i64, log_ratio: i64) -> i64 {
32 return pka_milli + log_ratio * 1000
33}
34
35// Buffer capacity is maximal when pH == pKa (1:1 ratio).
36func cs_buffer_ph_at_pka(pka_milli: i64) -> i64 {
37 return cs_buffer_ph_milli(pka_milli, 0)
38}
39
40// pOH from pH (at 25 C, pH + pOH = 14).
41func cs_poh_milli(ph_milli: i64) -> i64 {
42 return CS_MAGIC_14000 - ph_milli
43}
44
45// Ties to preservation: is the food high-acid (botulinum cannot grow)?
46func cs_is_high_acid(ph_milli: i64) -> i64 {
47 if ph_milli < CS_HIGH_ACID_PH_MILLI { return 1 }
48 return 0
49}
50
51// ===== Concentration + dilution ===================================
52
53// Molar concentration in mM = mass(mg)/molar_mass(g/mol) per volume(mL).
54// mM = mass_mg * 10000 / (mw_x10 * vol_ml).
55func cs_molarity_mm(mass_mg: i64, mw_x10: i64, vol_ml: i64) -> i64 {
56 let denom: i64 = mw_x10 * vol_ml
57 if denom <= 0 { return 0 }
58 return mass_mg * CS_MAGIC_10000 / denom
59}
60
61// Final concentration after diluting c1 (in v1) up to volume v2: c1*v1/v2.
62func cs_dilute_final_conc(c1: i64, v1: i64, v2: i64) -> i64 {
63 if v2 <= 0 { return 0 }
64 return c1 * v1 / v2
65}
66
67// Stock volume needed to make v2 of target concentration c2 from stock c1.
68func cs_dilute_volume_needed(c1: i64, c2: i64, v2: i64) -> i64 {
69 if c1 <= 0 { return 0 }
70 return c2 * v2 / c1
71}
72
73// ===== Titratable acidity =========================================
74
75// Acid mass (mg) from a titration: V(mL) * Normality(x1000) * milliequiv
76// weight(mg/meq, x100) / 100000. (Citric acid meq wt ~ 64.04 mg/meq.)
77func cs_titratable_acid_mg(v_ml: i64, normality_x1000: i64, meqwt_x100: i64) -> i64 {
78 return v_ml * normality_x1000 * meqwt_x100 / CS_MAGIC_100000
79}