nx_kinetics.nx source
↩ module page · 80 lines · 4626 B
1// nx_kinetics.nx -- Simulates chemical reaction kinetics with three modes: first-order decay, reversible reactions, and zero-order control.
2const O_MAGIC_4096: i64 = 4096
3// nx_kinetics.nx -- BROWSER-RUNNABLE chemical reaction-kinetics simulator (base-relative, integer/no-float, no JS
4// logic). SAME code runs native (base = mmap -> PNG) and in the browser. The CHEMISTRY pillar: first-order decay
5// A->B (d[A]/dt=-k[A]) whose hallmark is a CONSTANT half-life (the exponential law), a reversible reaction A<=>B
6// that settles to equilibrium [B]/[A]=kf/kr, and a zero-order control (constant rate) whose half-life SHRINKS. Mass
7// is conserved exactly ([A]+[B]=const). Validated by nx_kinetics_gate. The render draws the [A] decay curve filling
8// left-to-right plus live [A]/[B] bars. nx_wasm auto-exports every func. license_tier: ORIGINAL
9const W: i64 = 256
10const H: i64 = 256
11const O_FB: i64 = 0
12const O_A: i64 = 524288 // [A] concentration (scaled, A0=1e6)
13const O_B: i64 = 524296 // [B]
14const O_MODE: i64 = 524304 // 0=first-order decay, 1=reversible, 2=zero-order (control)
15const O_KNUM: i64 = 524312 // forward rate numerator
16const O_KDEN: i64 = 524320 // rate denominator
17const O_KRNUM: i64 = 524328 // reverse rate numerator (reversible)
18const O_STEP: i64 = 524336
19const O_HIST: i64 = 524344 // [A] history (one sample per tick)
20const HISTN: i64 = 236
21const A0: i64 = 1000000
22const ZCONST: i64 = 700 // zero-order constant decrement
23const STEPS_PER_TICK: i64 = 20
24
25func rgb(r: i64, g: i64, b: i64) -> i64 { return (r & 255) | ((g & 255) << 8) | ((b & 255) << 16) }
26func putpx(base: i64, px: i64, py: i64, c: i64) -> i64 {
27 if px<0 { return 0 } if px>=W { return 0 } if py<0 { return 0 } if py>=H { return 0 }
28 let fb: *i64 = (base + O_FB) as *i64; fb[py*W+px]=c; return 0
29}
30func clear_fb(base: i64, c: i64) -> i64 { let fb: *i64=(base+O_FB) as *i64; var i: i64=0; while i<W*H { fb[i]=c; i=i+1 } return 0 }
31func vbar(base: i64, x0: i64, x1: i64, h: i64, c: i64) -> i64 {
32 var y: i64=H-10-h; while y<H-10 { var x: i64=x0; while x<x1 { putpx(base,x,y,c); x=x+1 } y=y+1 } return 0
33}
34
35func init_impl(base: i64) -> i64 {
36 let A: *i64=(base+O_A) as *i64; let B: *i64=(base+O_B) as *i64; let M: *i64=(base+O_MODE) as *i64
37 let KN: *i64=(base+O_KNUM) as *i64; let KD: *i64=(base+O_KDEN) as *i64; let KR: *i64=(base+O_KRNUM) as *i64
38 A[0]=A0; B[0]=0; M[0]=0; KN[0]=1; KD[0]=1000; KR[0]=1
39 let stp: *i64=(base+O_STEP) as *i64; stp[0]=0
40 let HI: *i64=(base+O_HIST) as *i64; var i: i64=0; while i<HISTN { HI[i]=0-1; i=i+1 }
41 return 0
42}
43func step_one_impl(base: i64) -> i64 {
44 let A: *i64=(base+O_A) as *i64; let B: *i64=(base+O_B) as *i64; let M: *i64=(base+O_MODE) as *i64
45 let KN: *i64=(base+O_KNUM) as *i64; let KD: *i64=(base+O_KDEN) as *i64; let KR: *i64=(base+O_KRNUM) as *i64
46 if M[0]==2 { var d: i64=ZCONST; if A[0]<d { d=A[0] } A[0]=A[0]-d; B[0]=B[0]+d; return 0 }
47 if M[0]==1 { let df: i64=A[0]*KN[0]/KD[0]; let dr: i64=B[0]*KR[0]/KD[0]; A[0]=A[0]-df+dr; B[0]=B[0]+df-dr; return 0 }
48 let d: i64=A[0]*KN[0]/KD[0]; A[0]=A[0]-d; B[0]=B[0]+d // first-order
49 return 0
50}
51func tick_impl(base: i64, cmd: i64) -> i64 {
52 if cmd==3 { init_impl(base); return 0 }
53 let stp: *i64=(base+O_STEP) as *i64
54 if stp[0]>=HISTN { init_impl(base); return 0 } // loop the animation
55 var s: i64=0; while s<STEPS_PER_TICK { step_one_impl(base); s=s+1 }
56 let A: *i64=(base+O_A) as *i64; let HI: *i64=(base+O_HIST) as *i64
57 HI[stp[0]]=A[0]; stp[0]=stp[0]+1
58 return 0
59}
60func render_impl(base: i64) -> i64 {
61 let A: *i64=(base+O_A) as *i64; let B: *i64=(base+O_B) as *i64; let HI: *i64=(base+O_HIST) as *i64
62 clear_fb(base, rgb(12,14,22))
63 var gx: i64=0; while gx<W { putpx(base, gx, H-10, rgb(60,66,80)); gx=gx+1 } // baseline
64 // [A] decay curve (history)
65 var x: i64=0
66 while x<HISTN { let a: i64=HI[x]; if a>=0 { let py: i64=(H-12) - a*(H-30)/A0; putpx(base, x+2, py, rgb(120,180,250)); putpx(base, x+2, py+1, rgb(80,120,190)) } x=x+1 }
67 // live [A] (blue) and [B] (green) bars on the right
68 vbar(base, W-26, W-16, A[0]*(H-30)/A0, rgb(90,150,235))
69 vbar(base, W-13, W-3, B[0]*(H-30)/A0, rgb(90,210,140))
70 return 0
71}
72
73// ---- wasm interface ----
74func ww() -> i64 { return W }
75func hh() -> i64 { return H }
76func fb_off() -> i64 { return O_FB }
77func init() -> i64 { return init_impl(0) }
78func tick(cmd: i64) -> i64 { return tick_impl(0, cmd) }
79func render() -> i64 { return render_impl(0) }
80func mem_bytes() -> i64 { return O_HIST + HISTN*8 + O_MAGIC_4096 }