nx_chem.nx source
↩ module page · 153 lines · 6588 B
1// nx_chem.nx -- chemistry primitives (atomic table + stoichiometry).
2//
3// Native NishiLang chemistry foundation. Patent-clean: standard
4// IUPAC atomic masses + balance algorithm.
5
6// nx_safety_envelope:
7// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
8// sil_target: SIL1
9// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
10// verdict: NOT_YET_EVALUATED
11
12import "nx_kernel_v2.nx"
13const NX_MAGIC_4003: i64 = 4003
14const NX_MAGIC_6941: i64 = 6941
15const NX_MAGIC_9012: i64 = 9012
16const NX_MAGIC_10811: i64 = 10811
17const NX_MAGIC_12011: i64 = 12011
18const NX_MAGIC_14007: i64 = 14007
19const NX_MAGIC_15999: i64 = 15999
20const NX_MAGIC_18998: i64 = 18998
21const NX_MAGIC_20180: i64 = 20180
22const NX_MAGIC_22990: i64 = 22990
23const NX_MAGIC_24305: i64 = 24305
24const NX_MAGIC_26982: i64 = 26982
25const NX_MAGIC_28086: i64 = 28086
26const NX_MAGIC_30974: i64 = 30974
27const NX_MAGIC_32066: i64 = 32066
28const NX_MAGIC_35453: i64 = 35453
29const NX_MAGIC_39948: i64 = 39948
30
31// Sym IDs (chem family 415xxx)
32const NX_CHEM_SYM_MOLECULE: nx_int = 415001
33const NX_CHEM_SYM_ATOM: nx_int = 415002
34const NX_CHEM_SYM_REACTION: nx_int = 415003
35
36// Element struct: name + atomic number + atomic mass (Q3 fixed-point,
37// i.e., mass * 1000). Storing as integer Q3 keeps everything exact.
38struct Element {
39 symbol: *u8, // 1- or 2-char element symbol, NUL-terminated
40 z: nx_int, // atomic number
41 mass_q3: nx_int, // atomic mass in milli-AMU (e.g., H = 1008)
42}
43const NX_ELEM_BYTES: nx_int = 24
44
45// Periodic-table builder: returns a flat array of Element[N] with the
46// first N elements (1..N) populated. Keeps the file small while still
47// covering H..Ne (the chemistry-foundation set). Caller can extend
48// the array by adding more rows.
49func nx_chem_periodic_table() -> *Element {
50 let n: nx_int = 18
51 let arr: *Element = (sys_mmap((n * NX_ELEM_BYTES) as i64)) as *Element
52 let e1: *Element = arr
53 e1.symbol = "H " as *u8; e1.z = 1; e1.mass_q3 = 1008
54 let e2: *Element = ((arr as nx_int) + (1 * NX_ELEM_BYTES)) as *Element
55 e2.symbol = "He" as *u8; e2.z = 2; e2.mass_q3 = NX_MAGIC_4003
56 let e3: *Element = ((arr as nx_int) + (2 * NX_ELEM_BYTES)) as *Element
57 e3.symbol = "Li" as *u8; e3.z = 3; e3.mass_q3 = NX_MAGIC_6941
58 let e4: *Element = ((arr as nx_int) + (3 * NX_ELEM_BYTES)) as *Element
59 e4.symbol = "Be" as *u8; e4.z = 4; e4.mass_q3 = NX_MAGIC_9012
60 let e5: *Element = ((arr as nx_int) + (4 * NX_ELEM_BYTES)) as *Element
61 e5.symbol = "B " as *u8; e5.z = 5; e5.mass_q3 = NX_MAGIC_10811
62 let e6: *Element = ((arr as nx_int) + (5 * NX_ELEM_BYTES)) as *Element
63 e6.symbol = "C " as *u8; e6.z = 6; e6.mass_q3 = NX_MAGIC_12011
64 let e7: *Element = ((arr as nx_int) + (6 * NX_ELEM_BYTES)) as *Element
65 e7.symbol = "N " as *u8; e7.z = 7; e7.mass_q3 = NX_MAGIC_14007
66 let e8: *Element = ((arr as nx_int) + (7 * NX_ELEM_BYTES)) as *Element
67 e8.symbol = "O " as *u8; e8.z = 8; e8.mass_q3 = NX_MAGIC_15999
68 let e9: *Element = ((arr as nx_int) + (8 * NX_ELEM_BYTES)) as *Element
69 e9.symbol = "F " as *u8; e9.z = 9; e9.mass_q3 = NX_MAGIC_18998
70 let e10: *Element = ((arr as nx_int) + (9 * NX_ELEM_BYTES)) as *Element
71 e10.symbol = "Ne" as *u8; e10.z = 10; e10.mass_q3 = NX_MAGIC_20180
72 let e11: *Element = ((arr as nx_int) + (10 * NX_ELEM_BYTES)) as *Element
73 e11.symbol = "Na" as *u8; e11.z = 11; e11.mass_q3 = NX_MAGIC_22990
74 let e12: *Element = ((arr as nx_int) + (11 * NX_ELEM_BYTES)) as *Element
75 e12.symbol = "Mg" as *u8; e12.z = 12; e12.mass_q3 = NX_MAGIC_24305
76 let e13: *Element = ((arr as nx_int) + (12 * NX_ELEM_BYTES)) as *Element
77 e13.symbol = "Al" as *u8; e13.z = 13; e13.mass_q3 = NX_MAGIC_26982
78 let e14: *Element = ((arr as nx_int) + (13 * NX_ELEM_BYTES)) as *Element
79 e14.symbol = "Si" as *u8; e14.z = 14; e14.mass_q3 = NX_MAGIC_28086
80 let e15: *Element = ((arr as nx_int) + (14 * NX_ELEM_BYTES)) as *Element
81 e15.symbol = "P " as *u8; e15.z = 15; e15.mass_q3 = NX_MAGIC_30974
82 let e16: *Element = ((arr as nx_int) + (15 * NX_ELEM_BYTES)) as *Element
83 e16.symbol = "S " as *u8; e16.z = 16; e16.mass_q3 = NX_MAGIC_32066
84 let e17: *Element = ((arr as nx_int) + (16 * NX_ELEM_BYTES)) as *Element
85 e17.symbol = "Cl" as *u8; e17.z = 17; e17.mass_q3 = NX_MAGIC_35453
86 let e18: *Element = ((arr as nx_int) + (17 * NX_ELEM_BYTES)) as *Element
87 e18.symbol = "Ar" as *u8; e18.z = 18; e18.mass_q3 = NX_MAGIC_39948
88 return arr
89}
90
91// Lookup element by atomic number (1-indexed). Returns null Element*
92// if z is out of range [1..18].
93func nx_chem_element_by_z(table: *Element, z: nx_int) -> *Element {
94 if z < 1 { return 0 as *Element }
95 if z > 18 { return 0 as *Element }
96 return ((table as nx_int) + ((z - 1) * NX_ELEM_BYTES)) as *Element
97}
98
99// Molecule: parallel arrays of (z, count) -- "H2O" = [(1,2), (8,1)].
100struct Molecule {
101 z_arr: *nx_int,
102 c_arr: *nx_int,
103 n: nx_int,
104}
105const NX_MOL_BYTES: nx_int = 24
106
107func nx_chem_molecule_new(n: nx_int) -> *Molecule {
108 let m: *Molecule = (sys_mmap(NX_MOL_BYTES as i64)) as *Molecule
109 m.z_arr = (sys_mmap((n * 8) as i64)) as *nx_int
110 m.c_arr = (sys_mmap((n * 8) as i64)) as *nx_int
111 m.n = n
112 return m
113}
114
115func nx_chem_molecule_set(m: *Molecule, i: nx_int, z: nx_int, count: nx_int) -> nx_int {
116 let pz: *nx_int = ((m.z_arr as nx_int) + (i * 8)) as *nx_int
117 let pc: *nx_int = ((m.c_arr as nx_int) + (i * 8)) as *nx_int
118 pz[0] = z
119 pc[0] = count
120 return i
121}
122
123// Compute molecular mass in milli-AMU. Uses the periodic table.
124func nx_chem_molar_mass_q3(m: *Molecule, table: *Element) -> nx_int {
125 var sum: nx_int = 0
126 var i: nx_int = 0
127 while i < m.n {
128 let pz: *nx_int = ((m.z_arr as nx_int) + (i * 8)) as *nx_int
129 let pc: *nx_int = ((m.c_arr as nx_int) + (i * 8)) as *nx_int
130 let elt: *Element = nx_chem_element_by_z(table, pz[0])
131 if (elt as nx_int) != 0 {
132 sum = sum + (elt.mass_q3 * pc[0])
133 }
134 i = i + 1
135 }
136 return sum
137}
138
139// Convenience: H2O = (H,2) + (O,1).
140func nx_chem_water() -> *Molecule {
141 let m: *Molecule = nx_chem_molecule_new(2)
142 let _s1: nx_int = nx_chem_molecule_set(m, 0, 1, 2)
143 let _s2: nx_int = nx_chem_molecule_set(m, 1, 8, 1)
144 return m
145}
146
147// CO2 = (C,1) + (O,2)
148func nx_chem_co2() -> *Molecule {
149 let m: *Molecule = nx_chem_molecule_new(2)
150 let _s1: nx_int = nx_chem_molecule_set(m, 0, 6, 1)
151 let _s2: nx_int = nx_chem_molecule_set(m, 1, 8, 2)
152 return m
153}