code wiki / (root) / nx_kinetics.nx

nx_kinetics.nx source

↩ module page · 79 lines · 4485 B

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