nx_chem_extended.nx source
↩ module page · 219 lines · 10322 B
1// nx_chem_extended.nx -- periodic table 1..36 + binary reaction balancer.
2//
3// Per the comparison commit's named LOSE_BIG improvement vs
4// Mathematica: "extend periodic table to 118 + balance reactions".
5// This commit ships 1..36 (covers all of period 1-4: H..Kr) plus a
6// binary reaction balancer. Periods 5-7 are queued.
7//
8// Atomic masses are IUPAC 2021 standard, stored in milli-AMU (Q3
9// fixed point) for exact integer arithmetic.
10
11// nx_safety_envelope:
12// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
13// sil_target: SIL1
14// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
15// verdict: NOT_YET_EVALUATED
16
17import "nx_chem.nx"
18const K_MAGIC_4003: i64 = 4003
19const K_MAGIC_6941: i64 = 6941
20const K_MAGIC_9012: i64 = 9012
21const K_MAGIC_10811: i64 = 10811
22const K_MAGIC_12011: i64 = 12011
23const K_MAGIC_14007: i64 = 14007
24const K_MAGIC_15999: i64 = 15999
25const K_MAGIC_18998: i64 = 18998
26const K_MAGIC_20180: i64 = 20180
27const K_MAGIC_22990: i64 = 22990
28const K_MAGIC_24305: i64 = 24305
29const K_MAGIC_26982: i64 = 26982
30const K_MAGIC_28086: i64 = 28086
31const K_MAGIC_30974: i64 = 30974
32const K_MAGIC_32066: i64 = 32066
33const K_MAGIC_35453: i64 = 35453
34const K_MAGIC_39948: i64 = 39948
35const K_MAGIC_39098: i64 = 39098
36const K_MAGIC_40078: i64 = 40078
37const K_MAGIC_44956: i64 = 44956
38const K_MAGIC_47867: i64 = 47867
39const K_MAGIC_50942: i64 = 50942
40const K_MAGIC_51996: i64 = 51996
41const K_MAGIC_54938: i64 = 54938
42const K_MAGIC_55845: i64 = 55845
43const K_MAGIC_58933: i64 = 58933
44const K_MAGIC_58693: i64 = 58693
45const K_MAGIC_63546: i64 = 63546
46const K_MAGIC_65380: i64 = 65380
47const K_MAGIC_69723: i64 = 69723
48const K_MAGIC_72630: i64 = 72630
49const K_MAGIC_74922: i64 = 74922
50const K_MAGIC_78971: i64 = 78971
51const K_MAGIC_79904: i64 = 79904
52const K_MAGIC_83798: i64 = 83798
53
54// Build the extended periodic table for elements 1..36. Replaces
55// nx_chem_periodic_table()'s 18-element output with a 36-element
56// version that adds K..Kr (period 4). Returns flat *Element array.
57func nx_chem_periodic_table_36() -> *Element {
58 let n: nx_int = 36
59 let arr: *Element = (sys_mmap((n * NX_ELEM_BYTES) as i64)) as *Element
60 // Reuse element setter pattern from nx_chem_periodic_table for 1..18:
61 let e1: *Element = arr
62 e1.symbol = "H " as *u8; e1.z = 1; e1.mass_q3 = 1008
63 let e2: *Element = ((arr as nx_int) + (1 * NX_ELEM_BYTES)) as *Element
64 e2.symbol = "He" as *u8; e2.z = 2; e2.mass_q3 = K_MAGIC_4003
65 let e3: *Element = ((arr as nx_int) + (2 * NX_ELEM_BYTES)) as *Element
66 e3.symbol = "Li" as *u8; e3.z = 3; e3.mass_q3 = K_MAGIC_6941
67 let e4: *Element = ((arr as nx_int) + (3 * NX_ELEM_BYTES)) as *Element
68 e4.symbol = "Be" as *u8; e4.z = 4; e4.mass_q3 = K_MAGIC_9012
69 let e5: *Element = ((arr as nx_int) + (4 * NX_ELEM_BYTES)) as *Element
70 e5.symbol = "B " as *u8; e5.z = 5; e5.mass_q3 = K_MAGIC_10811
71 let e6: *Element = ((arr as nx_int) + (5 * NX_ELEM_BYTES)) as *Element
72 e6.symbol = "C " as *u8; e6.z = 6; e6.mass_q3 = K_MAGIC_12011
73 let e7: *Element = ((arr as nx_int) + (6 * NX_ELEM_BYTES)) as *Element
74 e7.symbol = "N " as *u8; e7.z = 7; e7.mass_q3 = K_MAGIC_14007
75 let e8: *Element = ((arr as nx_int) + (7 * NX_ELEM_BYTES)) as *Element
76 e8.symbol = "O " as *u8; e8.z = 8; e8.mass_q3 = K_MAGIC_15999
77 let e9: *Element = ((arr as nx_int) + (8 * NX_ELEM_BYTES)) as *Element
78 e9.symbol = "F " as *u8; e9.z = 9; e9.mass_q3 = K_MAGIC_18998
79 let e10: *Element = ((arr as nx_int) + (9 * NX_ELEM_BYTES)) as *Element
80 e10.symbol = "Ne" as *u8; e10.z = 10; e10.mass_q3 = K_MAGIC_20180
81 let e11: *Element = ((arr as nx_int) + (10 * NX_ELEM_BYTES)) as *Element
82 e11.symbol = "Na" as *u8; e11.z = 11; e11.mass_q3 = K_MAGIC_22990
83 let e12: *Element = ((arr as nx_int) + (11 * NX_ELEM_BYTES)) as *Element
84 e12.symbol = "Mg" as *u8; e12.z = 12; e12.mass_q3 = K_MAGIC_24305
85 let e13: *Element = ((arr as nx_int) + (12 * NX_ELEM_BYTES)) as *Element
86 e13.symbol = "Al" as *u8; e13.z = 13; e13.mass_q3 = K_MAGIC_26982
87 let e14: *Element = ((arr as nx_int) + (13 * NX_ELEM_BYTES)) as *Element
88 e14.symbol = "Si" as *u8; e14.z = 14; e14.mass_q3 = K_MAGIC_28086
89 let e15: *Element = ((arr as nx_int) + (14 * NX_ELEM_BYTES)) as *Element
90 e15.symbol = "P " as *u8; e15.z = 15; e15.mass_q3 = K_MAGIC_30974
91 let e16: *Element = ((arr as nx_int) + (15 * NX_ELEM_BYTES)) as *Element
92 e16.symbol = "S " as *u8; e16.z = 16; e16.mass_q3 = K_MAGIC_32066
93 let e17: *Element = ((arr as nx_int) + (16 * NX_ELEM_BYTES)) as *Element
94 e17.symbol = "Cl" as *u8; e17.z = 17; e17.mass_q3 = K_MAGIC_35453
95 let e18: *Element = ((arr as nx_int) + (17 * NX_ELEM_BYTES)) as *Element
96 e18.symbol = "Ar" as *u8; e18.z = 18; e18.mass_q3 = K_MAGIC_39948
97 // Period 4: K, Ca, Sc, Ti, V, Cr, Mn, Fe, Co, Ni, Cu, Zn, Ga, Ge, As, Se, Br, Kr
98 let e19: *Element = ((arr as nx_int) + (18 * NX_ELEM_BYTES)) as *Element
99 e19.symbol = "K " as *u8; e19.z = 19; e19.mass_q3 = K_MAGIC_39098
100 let e20: *Element = ((arr as nx_int) + (19 * NX_ELEM_BYTES)) as *Element
101 e20.symbol = "Ca" as *u8; e20.z = 20; e20.mass_q3 = K_MAGIC_40078
102 let e21: *Element = ((arr as nx_int) + (20 * NX_ELEM_BYTES)) as *Element
103 e21.symbol = "Sc" as *u8; e21.z = 21; e21.mass_q3 = K_MAGIC_44956
104 let e22: *Element = ((arr as nx_int) + (21 * NX_ELEM_BYTES)) as *Element
105 e22.symbol = "Ti" as *u8; e22.z = 22; e22.mass_q3 = K_MAGIC_47867
106 let e23: *Element = ((arr as nx_int) + (22 * NX_ELEM_BYTES)) as *Element
107 e23.symbol = "V " as *u8; e23.z = 23; e23.mass_q3 = K_MAGIC_50942
108 let e24: *Element = ((arr as nx_int) + (23 * NX_ELEM_BYTES)) as *Element
109 e24.symbol = "Cr" as *u8; e24.z = 24; e24.mass_q3 = K_MAGIC_51996
110 let e25: *Element = ((arr as nx_int) + (24 * NX_ELEM_BYTES)) as *Element
111 e25.symbol = "Mn" as *u8; e25.z = 25; e25.mass_q3 = K_MAGIC_54938
112 let e26: *Element = ((arr as nx_int) + (25 * NX_ELEM_BYTES)) as *Element
113 e26.symbol = "Fe" as *u8; e26.z = 26; e26.mass_q3 = K_MAGIC_55845
114 let e27: *Element = ((arr as nx_int) + (26 * NX_ELEM_BYTES)) as *Element
115 e27.symbol = "Co" as *u8; e27.z = 27; e27.mass_q3 = K_MAGIC_58933
116 let e28: *Element = ((arr as nx_int) + (27 * NX_ELEM_BYTES)) as *Element
117 e28.symbol = "Ni" as *u8; e28.z = 28; e28.mass_q3 = K_MAGIC_58693
118 let e29: *Element = ((arr as nx_int) + (28 * NX_ELEM_BYTES)) as *Element
119 e29.symbol = "Cu" as *u8; e29.z = 29; e29.mass_q3 = K_MAGIC_63546
120 let e30: *Element = ((arr as nx_int) + (29 * NX_ELEM_BYTES)) as *Element
121 e30.symbol = "Zn" as *u8; e30.z = 30; e30.mass_q3 = K_MAGIC_65380
122 let e31: *Element = ((arr as nx_int) + (30 * NX_ELEM_BYTES)) as *Element
123 e31.symbol = "Ga" as *u8; e31.z = 31; e31.mass_q3 = K_MAGIC_69723
124 let e32: *Element = ((arr as nx_int) + (31 * NX_ELEM_BYTES)) as *Element
125 e32.symbol = "Ge" as *u8; e32.z = 32; e32.mass_q3 = K_MAGIC_72630
126 let e33: *Element = ((arr as nx_int) + (32 * NX_ELEM_BYTES)) as *Element
127 e33.symbol = "As" as *u8; e33.z = 33; e33.mass_q3 = K_MAGIC_74922
128 let e34: *Element = ((arr as nx_int) + (33 * NX_ELEM_BYTES)) as *Element
129 e34.symbol = "Se" as *u8; e34.z = 34; e34.mass_q3 = K_MAGIC_78971
130 let e35: *Element = ((arr as nx_int) + (34 * NX_ELEM_BYTES)) as *Element
131 e35.symbol = "Br" as *u8; e35.z = 35; e35.mass_q3 = K_MAGIC_79904
132 let e36: *Element = ((arr as nx_int) + (35 * NX_ELEM_BYTES)) as *Element
133 e36.symbol = "Kr" as *u8; e36.z = 36; e36.mass_q3 = K_MAGIC_83798
134 return arr
135}
136
137// Lookup by atomic number on the extended table.
138func nx_chem_element_by_z_36(table: *Element, z: nx_int) -> *Element {
139 if z < 1 { return 0 as *Element }
140 if z > 36 { return 0 as *Element }
141 return ((table as nx_int) + ((z - 1) * NX_ELEM_BYTES)) as *Element
142}
143
144// ===== Reaction balancer (binary-synthesis form) ==================
145//
146// For reactions of shape: a*A + b*B -> c*P
147// where A, B, P are molecules with known atom-counts.
148//
149// Conservation requires: for each element z,
150// a * (count of z in A) + b * (count of z in B) = c * (count of z in P).
151//
152// The simplest case (shipped this commit): coefficient 1 each, verify
153// the reaction is ALREADY balanced. Returns 1 if balanced, 0 if not.
154// Generalising to coefficient solving is named follow-up (requires
155// integer linear algebra over rationals).
156func nx_chem_count_atom(m: *Molecule, target_z: nx_int) -> nx_int {
157 var total: nx_int = 0
158 var i: nx_int = 0
159 while i < m.n {
160 let pz: *nx_int = ((m.z_arr as nx_int) + (i * 8)) as *nx_int
161 let pc: *nx_int = ((m.c_arr as nx_int) + (i * 8)) as *nx_int
162 if pz[0] == target_z { total = total + pc[0] }
163 i = i + 1
164 }
165 return total
166}
167
168// Verify a binary reaction: a*A + b*B == c*P (atom-conserving)
169// Returns 1 if balanced, 0 otherwise.
170func nx_chem_reaction_check(
171 a_coef: nx_int, mol_a: *Molecule,
172 b_coef: nx_int, mol_b: *Molecule,
173 c_coef: nx_int, mol_p: *Molecule
174) -> nx_int {
175 var z: nx_int = 1
176 while z <= 36 {
177 let l1: nx_int = a_coef * nx_chem_count_atom(mol_a, z)
178 let l2: nx_int = b_coef * nx_chem_count_atom(mol_b, z)
179 let lhs: nx_int = l1 + l2
180 let rhs: nx_int = c_coef * nx_chem_count_atom(mol_p, z)
181 if lhs != rhs { return 0 }
182 z = z + 1
183 }
184 return 1
185}
186
187// Convenience builders
188func nx_chem_o2() -> *Molecule {
189 let m: *Molecule = nx_chem_molecule_new(1)
190 let _s1: nx_int = nx_chem_molecule_set(m, 0, 8, 2)
191 return m
192}
193
194func nx_chem_h2() -> *Molecule {
195 let m: *Molecule = nx_chem_molecule_new(1)
196 let _s1: nx_int = nx_chem_molecule_set(m, 0, 1, 2)
197 return m
198}
199
200func nx_chem_nacl() -> *Molecule {
201 let m: *Molecule = nx_chem_molecule_new(2)
202 let _s1: nx_int = nx_chem_molecule_set(m, 0, 11, 1)
203 let _s2: nx_int = nx_chem_molecule_set(m, 1, 17, 1)
204 return m
205}
206
207func nx_chem_iron() -> *Molecule {
208 let m: *Molecule = nx_chem_molecule_new(1)
209 let _s1: nx_int = nx_chem_molecule_set(m, 0, 26, 1)
210 return m
211}
212
213// FeO (iron(II) oxide)
214func nx_chem_feo() -> *Molecule {
215 let m: *Molecule = nx_chem_molecule_new(2)
216 let _s1: nx_int = nx_chem_molecule_set(m, 0, 26, 1)
217 let _s2: nx_int = nx_chem_molecule_set(m, 1, 8, 1)
218 return m
219}