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