code wiki / (root) / nx_kinetics.nx

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 }