navier-stokes.js (13431B)
1 /** 2 * Copyright 2013 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 * Update 10/21/2013: fixed loop variables at line 119 27 */ 28 29 var NavierStokes = new BenchmarkSuite('NavierStokes', [1484000], 30 [new Benchmark('NavierStokes', 31 true, 32 false, 33 180, 34 runNavierStokes, 35 setupNavierStokes, 36 tearDownNavierStokes, 37 null, 38 16)]); 39 40 var solver = null; 41 var nsFrameCounter = 0; 42 43 function runNavierStokes() 44 { 45 solver.update(); 46 nsFrameCounter++; 47 48 if(nsFrameCounter==15) 49 checkResult(solver.getDens()); 50 } 51 52 function checkResult(dens) { 53 54 this.result = 0; 55 for (var i=7000;i<7100;i++) { 56 this.result+=~~((dens[i]*10)); 57 } 58 59 if (this.result!=77) { 60 throw(new Error("checksum failed")); 61 } 62 } 63 64 function setupNavierStokes() 65 { 66 solver = new FluidField(null); 67 solver.setResolution(128, 128); 68 solver.setIterations(20); 69 solver.setDisplayFunction(function(){}); 70 solver.setUICallback(prepareFrame); 71 solver.reset(); 72 } 73 74 function tearDownNavierStokes() 75 { 76 solver = null; 77 } 78 79 function addPoints(field) { 80 var n = 64; 81 for (var i = 1; i <= n; i++) { 82 field.setVelocity(i, i, n, n); 83 field.setDensity(i, i, 5); 84 field.setVelocity(i, n - i, -n, -n); 85 field.setDensity(i, n - i, 20); 86 field.setVelocity(128 - i, n + i, -n, -n); 87 field.setDensity(128 - i, n + i, 30); 88 } 89 } 90 91 var framesTillAddingPoints = 0; 92 var framesBetweenAddingPoints = 5; 93 94 function prepareFrame(field) 95 { 96 if (framesTillAddingPoints == 0) { 97 addPoints(field); 98 framesTillAddingPoints = framesBetweenAddingPoints; 99 framesBetweenAddingPoints++; 100 } else { 101 framesTillAddingPoints--; 102 } 103 } 104 105 // Code from Oliver Hunt (http://nerget.com/fluidSim/pressure.js) starts here. 106 function FluidField(canvas) { 107 function addFields(x, s, dt) 108 { 109 for (var i=0; i<size ; i++ ) x[i] += dt*s[i]; 110 } 111 112 function set_bnd(b, x) 113 { 114 if (b===1) { 115 for (var i = 1; i <= width; i++) { 116 x[i] = x[i + rowSize]; 117 x[i + (height+1) *rowSize] = x[i + height * rowSize]; 118 } 119 120 for (var j = 1; j <= height; j++) { 121 x[j * rowSize] = -x[1 + j * rowSize]; 122 x[(width + 1) + j * rowSize] = -x[width + j * rowSize]; 123 } 124 } else if (b === 2) { 125 for (var i = 1; i <= width; i++) { 126 x[i] = -x[i + rowSize]; 127 x[i + (height + 1) * rowSize] = -x[i + height * rowSize]; 128 } 129 130 for (var j = 1; j <= height; j++) { 131 x[j * rowSize] = x[1 + j * rowSize]; 132 x[(width + 1) + j * rowSize] = x[width + j * rowSize]; 133 } 134 } else { 135 for (var i = 1; i <= width; i++) { 136 x[i] = x[i + rowSize]; 137 x[i + (height + 1) * rowSize] = x[i + height * rowSize]; 138 } 139 140 for (var j = 1; j <= height; j++) { 141 x[j * rowSize] = x[1 + j * rowSize]; 142 x[(width + 1) + j * rowSize] = x[width + j * rowSize]; 143 } 144 } 145 var maxEdge = (height + 1) * rowSize; 146 x[0] = 0.5 * (x[1] + x[rowSize]); 147 x[maxEdge] = 0.5 * (x[1 + maxEdge] + x[height * rowSize]); 148 x[(width+1)] = 0.5 * (x[width] + x[(width + 1) + rowSize]); 149 x[(width+1)+maxEdge] = 0.5 * (x[width + maxEdge] + x[(width + 1) + height * rowSize]); 150 } 151 152 function lin_solve(b, x, x0, a, c) 153 { 154 if (a === 0 && c === 1) { 155 for (var j=1 ; j<=height; j++) { 156 var currentRow = j * rowSize; 157 ++currentRow; 158 for (var i = 0; i < width; i++) { 159 x[currentRow] = x0[currentRow]; 160 ++currentRow; 161 } 162 } 163 set_bnd(b, x); 164 } else { 165 var invC = 1 / c; 166 for (var k=0 ; k<iterations; k++) { 167 for (var j=1 ; j<=height; j++) { 168 var lastRow = (j - 1) * rowSize; 169 var currentRow = j * rowSize; 170 var nextRow = (j + 1) * rowSize; 171 var lastX = x[currentRow]; 172 ++currentRow; 173 for (var i=1; i<=width; i++) 174 lastX = x[currentRow] = (x0[currentRow] + a*(lastX+x[++currentRow]+x[++lastRow]+x[++nextRow])) * invC; 175 } 176 set_bnd(b, x); 177 } 178 } 179 } 180 181 function diffuse(b, x, x0, dt) 182 { 183 var a = 0; 184 lin_solve(b, x, x0, a, 1 + 4*a); 185 } 186 187 function lin_solve2(x, x0, y, y0, a, c) 188 { 189 if (a === 0 && c === 1) { 190 for (var j=1 ; j <= height; j++) { 191 var currentRow = j * rowSize; 192 ++currentRow; 193 for (var i = 0; i < width; i++) { 194 x[currentRow] = x0[currentRow]; 195 y[currentRow] = y0[currentRow]; 196 ++currentRow; 197 } 198 } 199 set_bnd(1, x); 200 set_bnd(2, y); 201 } else { 202 var invC = 1/c; 203 for (var k=0 ; k<iterations; k++) { 204 for (var j=1 ; j <= height; j++) { 205 var lastRow = (j - 1) * rowSize; 206 var currentRow = j * rowSize; 207 var nextRow = (j + 1) * rowSize; 208 var lastX = x[currentRow]; 209 var lastY = y[currentRow]; 210 ++currentRow; 211 for (var i = 1; i <= width; i++) { 212 lastX = x[currentRow] = (x0[currentRow] + a * (lastX + x[currentRow] + x[lastRow] + x[nextRow])) * invC; 213 lastY = y[currentRow] = (y0[currentRow] + a * (lastY + y[++currentRow] + y[++lastRow] + y[++nextRow])) * invC; 214 } 215 } 216 set_bnd(1, x); 217 set_bnd(2, y); 218 } 219 } 220 } 221 222 function diffuse2(x, x0, y, y0, dt) 223 { 224 var a = 0; 225 lin_solve2(x, x0, y, y0, a, 1 + 4 * a); 226 } 227 228 function advect(b, d, d0, u, v, dt) 229 { 230 var Wdt0 = dt * width; 231 var Hdt0 = dt * height; 232 var Wp5 = width + 0.5; 233 var Hp5 = height + 0.5; 234 for (var j = 1; j<= height; j++) { 235 var pos = j * rowSize; 236 for (var i = 1; i <= width; i++) { 237 var x = i - Wdt0 * u[++pos]; 238 var y = j - Hdt0 * v[pos]; 239 if (x < 0.5) 240 x = 0.5; 241 else if (x > Wp5) 242 x = Wp5; 243 var i0 = x | 0; 244 var i1 = i0 + 1; 245 if (y < 0.5) 246 y = 0.5; 247 else if (y > Hp5) 248 y = Hp5; 249 var j0 = y | 0; 250 var j1 = j0 + 1; 251 var s1 = x - i0; 252 var s0 = 1 - s1; 253 var t1 = y - j0; 254 var t0 = 1 - t1; 255 var row1 = j0 * rowSize; 256 var row2 = j1 * rowSize; 257 d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) + s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]); 258 } 259 } 260 set_bnd(b, d); 261 } 262 263 function project(u, v, p, div) 264 { 265 var h = -0.5 / Math.sqrt(width * height); 266 for (var j = 1 ; j <= height; j++ ) { 267 var row = j * rowSize; 268 var previousRow = (j - 1) * rowSize; 269 var prevValue = row - 1; 270 var currentRow = row; 271 var nextValue = row + 1; 272 var nextRow = (j + 1) * rowSize; 273 for (var i = 1; i <= width; i++ ) { 274 div[++currentRow] = h * (u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]); 275 p[currentRow] = 0; 276 } 277 } 278 set_bnd(0, div); 279 set_bnd(0, p); 280 281 lin_solve(0, p, div, 1, 4 ); 282 var wScale = 0.5 * width; 283 var hScale = 0.5 * height; 284 for (var j = 1; j<= height; j++ ) { 285 var prevPos = j * rowSize - 1; 286 var currentPos = j * rowSize; 287 var nextPos = j * rowSize + 1; 288 var prevRow = (j - 1) * rowSize; 289 var currentRow = j * rowSize; 290 var nextRow = (j + 1) * rowSize; 291 292 for (var i = 1; i<= width; i++) { 293 u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]); 294 v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]); 295 } 296 } 297 set_bnd(1, u); 298 set_bnd(2, v); 299 } 300 301 function dens_step(x, x0, u, v, dt) 302 { 303 addFields(x, x0, dt); 304 diffuse(0, x0, x, dt ); 305 advect(0, x, x0, u, v, dt ); 306 } 307 308 function vel_step(u, v, u0, v0, dt) 309 { 310 addFields(u, u0, dt ); 311 addFields(v, v0, dt ); 312 var temp = u0; u0 = u; u = temp; 313 var temp = v0; v0 = v; v = temp; 314 diffuse2(u,u0,v,v0, dt); 315 project(u, v, u0, v0); 316 var temp = u0; u0 = u; u = temp; 317 var temp = v0; v0 = v; v = temp; 318 advect(1, u, u0, u0, v0, dt); 319 advect(2, v, v0, u0, v0, dt); 320 project(u, v, u0, v0 ); 321 } 322 var uiCallback = function(d,u,v) {}; 323 324 function Field(dens, u, v) { 325 // Just exposing the fields here rather than using accessors is a measurable win during display (maybe 5%) 326 // but makes the code ugly. 327 this.setDensity = function(x, y, d) { 328 dens[(x + 1) + (y + 1) * rowSize] = d; 329 } 330 this.getDensity = function(x, y) { 331 return dens[(x + 1) + (y + 1) * rowSize]; 332 } 333 this.setVelocity = function(x, y, xv, yv) { 334 u[(x + 1) + (y + 1) * rowSize] = xv; 335 v[(x + 1) + (y + 1) * rowSize] = yv; 336 } 337 this.getXVelocity = function(x, y) { 338 return u[(x + 1) + (y + 1) * rowSize]; 339 } 340 this.getYVelocity = function(x, y) { 341 return v[(x + 1) + (y + 1) * rowSize]; 342 } 343 this.width = function() { return width; } 344 this.height = function() { return height; } 345 } 346 function queryUI(d, u, v) 347 { 348 for (var i = 0; i < size; i++) 349 u[i] = v[i] = d[i] = 0.0; 350 uiCallback(new Field(d, u, v)); 351 } 352 353 this.update = function () { 354 queryUI(dens_prev, u_prev, v_prev); 355 vel_step(u, v, u_prev, v_prev, dt); 356 dens_step(dens, dens_prev, u, v, dt); 357 displayFunc(new Field(dens, u, v)); 358 } 359 this.setDisplayFunction = function(func) { 360 displayFunc = func; 361 } 362 363 this.iterations = function() { return iterations; } 364 this.setIterations = function(iters) { 365 if (iters > 0 && iters <= 100) 366 iterations = iters; 367 } 368 this.setUICallback = function(callback) { 369 uiCallback = callback; 370 } 371 var iterations = 10; 372 var visc = 0.5; 373 var dt = 0.1; 374 var dens; 375 var dens_prev; 376 var u; 377 var u_prev; 378 var v; 379 var v_prev; 380 var width; 381 var height; 382 var rowSize; 383 var size; 384 var displayFunc; 385 function reset() 386 { 387 rowSize = width + 2; 388 size = (width+2)*(height+2); 389 dens = new Array(size); 390 dens_prev = new Array(size); 391 u = new Array(size); 392 u_prev = new Array(size); 393 v = new Array(size); 394 v_prev = new Array(size); 395 for (var i = 0; i < size; i++) 396 dens_prev[i] = u_prev[i] = v_prev[i] = dens[i] = u[i] = v[i] = 0; 397 } 398 this.reset = reset; 399 this.getDens = function() 400 { 401 return dens; 402 } 403 this.setResolution = function (hRes, wRes) 404 { 405 var res = wRes * hRes; 406 if (res > 0 && res < 1000000 && (wRes != width || hRes != height)) { 407 width = wRes; 408 height = hRes; 409 reset(); 410 return true; 411 } 412 return false; 413 } 414 this.setResolution(64, 64); 415 }