code wiki / _hdl_build / nx_fluid_gate.nx
nx_fluid_gate.nx source
↩ module page · 113 lines · 5590 B
1// nx_fluid_gate.nx -- ★R6: the height-field FLUID SIMULATION, proven by physics (not "pixels moved"):
2// T1 PROPAGATION: a drop's wavefront radius GROWS over steps (waves travel at finite speed; not static, not instant)
3// T2 ★CONSERVATION: total volume sum(h) is invariant across the sim (mass conservation = the correct-integrator test)
4// T3 REFLECTION + DAMPING: the wave reaches a far boundary (propagation across the domain) AND peak |h| decays
5// over long time (energy dissipates) -- and it stays BOUNDED (no blow-up = numerically stable)
6// T4 determinism + animation strip knowledge/nx_fluid.png (a drop rippling + reflecting across 5 frames)
7// license_tier: ORIGINAL expect_exit: 0
8import "nx_syscalls.nx"
9import "nx_png.nx"
10import "nx_fluid.nx"
11
12func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
13func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 }
14func iabs(v: i64) -> i64 { if v<0 {return 0-v} return v }
15
16func main() -> i64 {
17 hw("=== nx_fluid_gate -- R6: height-field wave simulation, physics-verified ===\n" as *u8)
18 var fails: i64 = 0
19 let N: i64 = fl_n()
20 let cx: i64 = N/2; let cy: i64 = N/2
21 fl_init()
22 fl_drop(cx, cy, 12, 60000)
23
24 let vol0: i64 = fl_volume()
25 // step, sampling wavefront radius, volume, peak-amplitude, and a boundary probe
26 var r_early: i64 = 0
27 var r_late: i64 = 0
28 var volmin: i64 = vol0
29 var volmax: i64 = vol0
30 var reached_edge: i64 = 0
31 var amp_early: i64 = 0
32 var amp_late: i64 = 0
33 var amp_max: i64 = fl_maxabs()
34 var s: i64 = 0
35 while s < 420 {
36 fl_step()
37 let vv: i64 = fl_volume()
38 if vv < volmin { volmin = vv }
39 if vv > volmax { volmax = vv }
40 let ma: i64 = fl_maxabs()
41 if ma > amp_max { amp_max = ma }
42 if s == 5 { amp_early = ma }
43 if s == 400 { amp_late = ma }
44 if s == 40 { r_early = fl_front(cx, cy, 400) }
45 if s == 150 { r_late = fl_front(cx, cy, 400) }
46 if reached_edge == 0 {
47 var e: i64 = 1
48 while e < N-1 {
49 if iabs(fl_h(e, 2)) > 300 { reached_edge = 1; e = N }
50 else { if iabs(fl_h(2, e)) > 300 { reached_edge = 1; e = N } else { e = e + 1 } }
51 }
52 }
53 s = s + 1
54 }
55 let voldrift: i64 = iabs(volmax - volmin)
56 let voltol: i64 = iabs(vol0)/100 + 1000 // <1% of the drop volume
57 let amp0: i64 = fl_maxabs() // (unused ref)
58 hw(" propagation: front r@40="); pn(r_early); hw(" r@150="); pn(r_late); hw("\n" as *u8)
59 hw(" conservation: vol0="); pn(vol0); hw(" drift="); pn(voldrift); hw(" (tol "); pn(voltol); hw(")\n" as *u8)
60 hw(" amplitude: early="); pn(amp_early); hw(" late="); pn(amp_late); hw(" max-ever="); pn(amp_max); hw(" reached-edge="); pn(reached_edge); hw("\n" as *u8)
61
62 var t1: i64 = 0
63 if r_late > r_early + 5 { if r_early > 0 { t1 = 1 } }
64 if t1 == 1 { hw("T1 PASS PROPAGATION: the wavefront travels outward at finite speed\n" as *u8) }
65 else { fails=fails+1; hw("T1 FAIL propagation\n" as *u8) }
66
67 var t2: i64 = 0
68 if voldrift < voltol { t2 = 1 }
69 if t2 == 1 { hw("T2 PASS CONSERVATION: total volume is invariant (correct fluid integrator)\n" as *u8) }
70 else { fails=fails+1; hw("T2 FAIL volume drifts\n" as *u8) }
71
72 var t3: i64 = 0
73 // peak amplitude decays (spreading + damping) and never blows up past the initial drop
74 if reached_edge == 1 { if amp_late*2 < amp_early { if amp_max < amp_early*3 { t3 = 1 } } }
75 if t3 == 1 { hw("T3 PASS REFLECTION+DAMPING: wave crosses the domain, peak amplitude decays, stays bounded\n" as *u8) }
76 else { fails=fails+1; hw("T3 FAIL energy behaviour\n" as *u8) }
77
78 // T4 determinism: re-run the same sim, compare the height field
79 let Hs: i64 = fl_h(cx+20, cy) // a probe value from run 1 (post-420 steps)
80 fl_init()
81 fl_drop(cx, cy, 12, 60000)
82 var s2: i64 = 0
83 while s2 < 420 { fl_step(); s2 = s2 + 1 }
84 var det: i64 = 0
85 if fl_h(cx+20, cy) == Hs { det = 1 }
86 var t4: i64 = 0
87 if det == 1 { t4 = 1 }
88 if t4 == 1 { hw("T4 PASS deterministic\n" as *u8) }
89 else { fails=fails+1; hw("T4 FAIL nondeterministic\n" as *u8) }
90
91 // animation strip: fresh drop, render 5 frames as it ripples + reflects
92 fl_init()
93 fl_drop(cx, cy, 12, 60000)
94 let TW: i64 = 300; let TH: i64 = 300
95 let strip: *i64 = sys_mmap(TW*5*TH*8) as *i64
96 let fb: *i64 = sys_mmap(TW*TH*8) as *i64
97 var fr: i64 = 0
98 while fr < 5 {
99 fl_render(fb, TW, TH, 420, 620, 380)
100 var y: i64 = 0
101 while y < TH { var x: i64=0; while x<TW { strip[y*(TW*5)+fr*TW+x] = fb[y*TW+x]; x=x+1 } y=y+1 }
102 var st: i64 = 0
103 while st < 55 { fl_step(); st = st + 1 }
104 fr = fr + 1
105 }
106 write_png(strip, TW*5, TH, "knowledge/nx_fluid.png" as *u8)
107 hw("T5 animation strip -> knowledge/nx_fluid.png (a drop rippling + reflecting, 5 frames)\n" as *u8)
108
109 if fails == 0 { hw("FLUID-GATE GREEN -- R6: a height-field wave simulation (propagating, volume-conserving, reflecting, damped, stable, deterministic) = real fluid sim, sovereign + integer. Residual vs FLIP: particle splashes/breaking waves.\n" as *u8); sys_exit(0); return 0 }
110 hw("FLUID-GATE RED fails="); pn(fails); hw("\n" as *u8)
111 sys_exit(1)
112 return 1
113}