code wiki / (root) / nx_chem_extended.nx

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}