|
1 /** |
|
2 * Copyright 2012 the V8 project authors. All rights reserved. |
|
3 * Copyright 2009 Oliver Hunt <http://nerget.com> |
|
4 * |
|
5 * Permission is hereby granted, free of charge, to any person |
|
6 * obtaining a copy of this software and associated documentation |
|
7 * files (the "Software"), to deal in the Software without |
|
8 * restriction, including without limitation the rights to use, |
|
9 * copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the |
|
11 * Software is furnished to do so, subject to the following |
|
12 * conditions: |
|
13 * |
|
14 * The above copyright notice and this permission notice shall be |
|
15 * included in all copies or substantial portions of the Software. |
|
16 * |
|
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
|
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
|
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
|
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
|
24 * OTHER DEALINGS IN THE SOFTWARE. |
|
25 */ |
|
26 |
|
27 var NavierStokes = new BenchmarkSuite('NavierStokes', 1484000, |
|
28 [new Benchmark('NavierStokes', |
|
29 runNavierStokes, |
|
30 setupNavierStokes, |
|
31 tearDownNavierStokes)]); |
|
32 |
|
33 var solver = null; |
|
34 |
|
35 function runNavierStokes() |
|
36 { |
|
37 solver.update(); |
|
38 } |
|
39 |
|
40 function setupNavierStokes() |
|
41 { |
|
42 solver = new FluidField(null); |
|
43 solver.setResolution(128, 128); |
|
44 solver.setIterations(20); |
|
45 solver.setDisplayFunction(function(){}); |
|
46 solver.setUICallback(prepareFrame); |
|
47 solver.reset(); |
|
48 } |
|
49 |
|
50 function tearDownNavierStokes() |
|
51 { |
|
52 solver = null; |
|
53 } |
|
54 |
|
55 function addPoints(field) { |
|
56 var n = 64; |
|
57 for (var i = 1; i <= n; i++) { |
|
58 field.setVelocity(i, i, n, n); |
|
59 field.setDensity(i, i, 5); |
|
60 field.setVelocity(i, n - i, -n, -n); |
|
61 field.setDensity(i, n - i, 20); |
|
62 field.setVelocity(128 - i, n + i, -n, -n); |
|
63 field.setDensity(128 - i, n + i, 30); |
|
64 } |
|
65 } |
|
66 |
|
67 var framesTillAddingPoints = 0; |
|
68 var framesBetweenAddingPoints = 5; |
|
69 |
|
70 function prepareFrame(field) |
|
71 { |
|
72 if (framesTillAddingPoints == 0) { |
|
73 addPoints(field); |
|
74 framesTillAddingPoints = framesBetweenAddingPoints; |
|
75 framesBetweenAddingPoints++; |
|
76 } else { |
|
77 framesTillAddingPoints--; |
|
78 } |
|
79 } |
|
80 |
|
81 // Code from Oliver Hunt (http://nerget.com/fluidSim/pressure.js) starts here. |
|
82 function FluidField(canvas) { |
|
83 function addFields(x, s, dt) |
|
84 { |
|
85 for (var i=0; i<size ; i++ ) x[i] += dt*s[i]; |
|
86 } |
|
87 |
|
88 function set_bnd(b, x) |
|
89 { |
|
90 if (b===1) { |
|
91 for (var i = 1; i <= width; i++) { |
|
92 x[i] = x[i + rowSize]; |
|
93 x[i + (height+1) *rowSize] = x[i + height * rowSize]; |
|
94 } |
|
95 |
|
96 for (var j = 1; i <= height; i++) { |
|
97 x[j * rowSize] = -x[1 + j * rowSize]; |
|
98 x[(width + 1) + j * rowSize] = -x[width + j * rowSize]; |
|
99 } |
|
100 } else if (b === 2) { |
|
101 for (var i = 1; i <= width; i++) { |
|
102 x[i] = -x[i + rowSize]; |
|
103 x[i + (height + 1) * rowSize] = -x[i + height * rowSize]; |
|
104 } |
|
105 |
|
106 for (var j = 1; j <= height; j++) { |
|
107 x[j * rowSize] = x[1 + j * rowSize]; |
|
108 x[(width + 1) + j * rowSize] = x[width + j * rowSize]; |
|
109 } |
|
110 } else { |
|
111 for (var i = 1; i <= width; i++) { |
|
112 x[i] = x[i + rowSize]; |
|
113 x[i + (height + 1) * rowSize] = x[i + height * rowSize]; |
|
114 } |
|
115 |
|
116 for (var j = 1; j <= height; j++) { |
|
117 x[j * rowSize] = x[1 + j * rowSize]; |
|
118 x[(width + 1) + j * rowSize] = x[width + j * rowSize]; |
|
119 } |
|
120 } |
|
121 var maxEdge = (height + 1) * rowSize; |
|
122 x[0] = 0.5 * (x[1] + x[rowSize]); |
|
123 x[maxEdge] = 0.5 * (x[1 + maxEdge] + x[height * rowSize]); |
|
124 x[(width+1)] = 0.5 * (x[width] + x[(width + 1) + rowSize]); |
|
125 x[(width+1)+maxEdge] = 0.5 * (x[width + maxEdge] + x[(width + 1) + height * rowSize]); |
|
126 } |
|
127 |
|
128 function lin_solve(b, x, x0, a, c) |
|
129 { |
|
130 if (a === 0 && c === 1) { |
|
131 for (var j=1 ; j<=height; j++) { |
|
132 var currentRow = j * rowSize; |
|
133 ++currentRow; |
|
134 for (var i = 0; i < width; i++) { |
|
135 x[currentRow] = x0[currentRow]; |
|
136 ++currentRow; |
|
137 } |
|
138 } |
|
139 set_bnd(b, x); |
|
140 } else { |
|
141 var invC = 1 / c; |
|
142 for (var k=0 ; k<iterations; k++) { |
|
143 for (var j=1 ; j<=height; j++) { |
|
144 var lastRow = (j - 1) * rowSize; |
|
145 var currentRow = j * rowSize; |
|
146 var nextRow = (j + 1) * rowSize; |
|
147 var lastX = x[currentRow]; |
|
148 ++currentRow; |
|
149 for (var i=1; i<=width; i++) |
|
150 lastX = x[currentRow] = (x0[currentRow] + a*(lastX+x[++currentRow]+x[++lastRow]+x[++nextRow])) * invC; |
|
151 } |
|
152 set_bnd(b, x); |
|
153 } |
|
154 } |
|
155 } |
|
156 |
|
157 function diffuse(b, x, x0, dt) |
|
158 { |
|
159 var a = 0; |
|
160 lin_solve(b, x, x0, a, 1 + 4*a); |
|
161 } |
|
162 |
|
163 function lin_solve2(x, x0, y, y0, a, c) |
|
164 { |
|
165 if (a === 0 && c === 1) { |
|
166 for (var j=1 ; j <= height; j++) { |
|
167 var currentRow = j * rowSize; |
|
168 ++currentRow; |
|
169 for (var i = 0; i < width; i++) { |
|
170 x[currentRow] = x0[currentRow]; |
|
171 y[currentRow] = y0[currentRow]; |
|
172 ++currentRow; |
|
173 } |
|
174 } |
|
175 set_bnd(1, x); |
|
176 set_bnd(2, y); |
|
177 } else { |
|
178 var invC = 1/c; |
|
179 for (var k=0 ; k<iterations; k++) { |
|
180 for (var j=1 ; j <= height; j++) { |
|
181 var lastRow = (j - 1) * rowSize; |
|
182 var currentRow = j * rowSize; |
|
183 var nextRow = (j + 1) * rowSize; |
|
184 var lastX = x[currentRow]; |
|
185 var lastY = y[currentRow]; |
|
186 ++currentRow; |
|
187 for (var i = 1; i <= width; i++) { |
|
188 lastX = x[currentRow] = (x0[currentRow] + a * (lastX + x[currentRow] + x[lastRow] + x[nextRow])) * invC; |
|
189 lastY = y[currentRow] = (y0[currentRow] + a * (lastY + y[++currentRow] + y[++lastRow] + y[++nextRow])) * invC; |
|
190 } |
|
191 } |
|
192 set_bnd(1, x); |
|
193 set_bnd(2, y); |
|
194 } |
|
195 } |
|
196 } |
|
197 |
|
198 function diffuse2(x, x0, y, y0, dt) |
|
199 { |
|
200 var a = 0; |
|
201 lin_solve2(x, x0, y, y0, a, 1 + 4 * a); |
|
202 } |
|
203 |
|
204 function advect(b, d, d0, u, v, dt) |
|
205 { |
|
206 var Wdt0 = dt * width; |
|
207 var Hdt0 = dt * height; |
|
208 var Wp5 = width + 0.5; |
|
209 var Hp5 = height + 0.5; |
|
210 for (var j = 1; j<= height; j++) { |
|
211 var pos = j * rowSize; |
|
212 for (var i = 1; i <= width; i++) { |
|
213 var x = i - Wdt0 * u[++pos]; |
|
214 var y = j - Hdt0 * v[pos]; |
|
215 if (x < 0.5) |
|
216 x = 0.5; |
|
217 else if (x > Wp5) |
|
218 x = Wp5; |
|
219 var i0 = x | 0; |
|
220 var i1 = i0 + 1; |
|
221 if (y < 0.5) |
|
222 y = 0.5; |
|
223 else if (y > Hp5) |
|
224 y = Hp5; |
|
225 var j0 = y | 0; |
|
226 var j1 = j0 + 1; |
|
227 var s1 = x - i0; |
|
228 var s0 = 1 - s1; |
|
229 var t1 = y - j0; |
|
230 var t0 = 1 - t1; |
|
231 var row1 = j0 * rowSize; |
|
232 var row2 = j1 * rowSize; |
|
233 d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) + s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]); |
|
234 } |
|
235 } |
|
236 set_bnd(b, d); |
|
237 } |
|
238 |
|
239 function project(u, v, p, div) |
|
240 { |
|
241 var h = -0.5 / Math.sqrt(width * height); |
|
242 for (var j = 1 ; j <= height; j++ ) { |
|
243 var row = j * rowSize; |
|
244 var previousRow = (j - 1) * rowSize; |
|
245 var prevValue = row - 1; |
|
246 var currentRow = row; |
|
247 var nextValue = row + 1; |
|
248 var nextRow = (j + 1) * rowSize; |
|
249 for (var i = 1; i <= width; i++ ) { |
|
250 div[++currentRow] = h * (u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]); |
|
251 p[currentRow] = 0; |
|
252 } |
|
253 } |
|
254 set_bnd(0, div); |
|
255 set_bnd(0, p); |
|
256 |
|
257 lin_solve(0, p, div, 1, 4 ); |
|
258 var wScale = 0.5 * width; |
|
259 var hScale = 0.5 * height; |
|
260 for (var j = 1; j<= height; j++ ) { |
|
261 var prevPos = j * rowSize - 1; |
|
262 var currentPos = j * rowSize; |
|
263 var nextPos = j * rowSize + 1; |
|
264 var prevRow = (j - 1) * rowSize; |
|
265 var currentRow = j * rowSize; |
|
266 var nextRow = (j + 1) * rowSize; |
|
267 |
|
268 for (var i = 1; i<= width; i++) { |
|
269 u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]); |
|
270 v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]); |
|
271 } |
|
272 } |
|
273 set_bnd(1, u); |
|
274 set_bnd(2, v); |
|
275 } |
|
276 |
|
277 function dens_step(x, x0, u, v, dt) |
|
278 { |
|
279 addFields(x, x0, dt); |
|
280 diffuse(0, x0, x, dt ); |
|
281 advect(0, x, x0, u, v, dt ); |
|
282 } |
|
283 |
|
284 function vel_step(u, v, u0, v0, dt) |
|
285 { |
|
286 addFields(u, u0, dt ); |
|
287 addFields(v, v0, dt ); |
|
288 var temp = u0; u0 = u; u = temp; |
|
289 var temp = v0; v0 = v; v = temp; |
|
290 diffuse2(u,u0,v,v0, dt); |
|
291 project(u, v, u0, v0); |
|
292 var temp = u0; u0 = u; u = temp; |
|
293 var temp = v0; v0 = v; v = temp; |
|
294 advect(1, u, u0, u0, v0, dt); |
|
295 advect(2, v, v0, u0, v0, dt); |
|
296 project(u, v, u0, v0 ); |
|
297 } |
|
298 var uiCallback = function(d,u,v) {}; |
|
299 |
|
300 function Field(dens, u, v) { |
|
301 // Just exposing the fields here rather than using accessors is a measurable win during display (maybe 5%) |
|
302 // but makes the code ugly. |
|
303 this.setDensity = function(x, y, d) { |
|
304 dens[(x + 1) + (y + 1) * rowSize] = d; |
|
305 } |
|
306 this.getDensity = function(x, y) { |
|
307 return dens[(x + 1) + (y + 1) * rowSize]; |
|
308 } |
|
309 this.setVelocity = function(x, y, xv, yv) { |
|
310 u[(x + 1) + (y + 1) * rowSize] = xv; |
|
311 v[(x + 1) + (y + 1) * rowSize] = yv; |
|
312 } |
|
313 this.getXVelocity = function(x, y) { |
|
314 return u[(x + 1) + (y + 1) * rowSize]; |
|
315 } |
|
316 this.getYVelocity = function(x, y) { |
|
317 return v[(x + 1) + (y + 1) * rowSize]; |
|
318 } |
|
319 this.width = function() { return width; } |
|
320 this.height = function() { return height; } |
|
321 } |
|
322 function queryUI(d, u, v) |
|
323 { |
|
324 for (var i = 0; i < size; i++) |
|
325 u[i] = v[i] = d[i] = 0.0; |
|
326 uiCallback(new Field(d, u, v)); |
|
327 } |
|
328 |
|
329 this.update = function () { |
|
330 queryUI(dens_prev, u_prev, v_prev); |
|
331 vel_step(u, v, u_prev, v_prev, dt); |
|
332 dens_step(dens, dens_prev, u, v, dt); |
|
333 displayFunc(new Field(dens, u, v)); |
|
334 } |
|
335 this.setDisplayFunction = function(func) { |
|
336 displayFunc = func; |
|
337 } |
|
338 |
|
339 this.iterations = function() { return iterations; } |
|
340 this.setIterations = function(iters) { |
|
341 if (iters > 0 && iters <= 100) |
|
342 iterations = iters; |
|
343 } |
|
344 this.setUICallback = function(callback) { |
|
345 uiCallback = callback; |
|
346 } |
|
347 var iterations = 10; |
|
348 var visc = 0.5; |
|
349 var dt = 0.1; |
|
350 var dens; |
|
351 var dens_prev; |
|
352 var u; |
|
353 var u_prev; |
|
354 var v; |
|
355 var v_prev; |
|
356 var width; |
|
357 var height; |
|
358 var rowSize; |
|
359 var size; |
|
360 var displayFunc; |
|
361 function reset() |
|
362 { |
|
363 rowSize = width + 2; |
|
364 size = (width+2)*(height+2); |
|
365 dens = new Array(size); |
|
366 dens_prev = new Array(size); |
|
367 u = new Array(size); |
|
368 u_prev = new Array(size); |
|
369 v = new Array(size); |
|
370 v_prev = new Array(size); |
|
371 for (var i = 0; i < size; i++) |
|
372 dens_prev[i] = u_prev[i] = v_prev[i] = dens[i] = u[i] = v[i] = 0; |
|
373 } |
|
374 this.reset = reset; |
|
375 this.setResolution = function (hRes, wRes) |
|
376 { |
|
377 var res = wRes * hRes; |
|
378 if (res > 0 && res < 1000000 && (wRes != width || hRes != height)) { |
|
379 width = wRes; |
|
380 height = hRes; |
|
381 reset(); |
|
382 return true; |
|
383 } |
|
384 return false; |
|
385 } |
|
386 this.setResolution(64, 64); |
|
387 } |