lbm_reference
LBM_def.h
Go to the documentation of this file.
1 //! \file LBM_def.h
2 //! Implementation of the LBM class
3 
4 //! \date Jan 16, 2009
5 //! \author Florian Rathgeber
6 
7 #ifndef LBM_DEF_H_
8 #define LBM_DEF_H_
9 
10 #ifndef htobe32
11 
12  #if __BYTE_ORDER == __LITTLE_ENDIAN
13  #include <byteswap.h>
14  #define htobe32(x) bswap_32(x)
15  #define htobe64(x) bswap_64(x)
16  #else
17  #define htobe32(x) (x)
18  #define htobe64(x) (x)
19  #endif
20 
21 #endif
22 
23 #include <iomanip>
24 
25 namespace lbm {
26 
27 // ============================ //
28 // Constructors and destructors //
29 // ============================ //
30 
31 template<typename T>
32 LBM<T>::LBM( const std::string configFileName ) : curStep_( 0 ) {
33 
34  try {
35  ConfParser p;
36  ConfBlock base = p.parse( configFileName );
37  std::cout << "Parsed configuration file " << configFileName << std::endl;
38  setup( base );
39  } catch ( std::exception& e ) {
40  std::cerr << e.what() << std::endl;
41  exit( -1 );
42  }
43 }
44 
45 template<typename T>
46 LBM<T>::LBM( ConfBlock& base ) : curStep_( 0 ) {
47  setup( base );
48 }
49 
50 template<typename T>
52  assert ( grid0_ != 0 && grid1_ != 0 );
53  delete grid0_;
54  delete grid1_;
55 }
56 
57 template<typename T>
58 void LBM<T>::run() {
59 
60  T totTime = 0.;
61 
62  // Get effective domain size (without ghost layer)
63  int sizeX = grid0_->getSizeX() - 2;
64  int sizeY = grid0_->getSizeY() - 2;
65  int sizeZ = grid0_->getSizeZ() - 2;
66 
67  int numCells = sizeX * sizeY * sizeZ;
68 
69 #ifdef NSMAGO
70  std::cout << "Starting LBM without Smagorinsky turbulence correction" << std::endl;
71 #else
72  std::cout << "Starting LBM with Smagorinsky turbulence correction"
73  << std::endl;
74 #endif
75 
76  // loop over maxSteps time steps
77  for (int step = 0; step < maxSteps_; ++step) {
78  totTime += runStep();
79  }
80 
81  std::cout << "LBM finished! Processed " << maxSteps_ << " timesteps in ";
82  std::cout << totTime << " secs!" << std::endl;
83  std::cout << "Average speed of " << (maxSteps_ * numCells) / (totTime
84  * 1000000);
85  std::cout << " MLUP/s" << std::endl;
86 
87  if ( prof_.fileName.length() ) writePerformanceSummary();
88 }
89 
90 template<typename T>
91 double LBM<T>::runStep() {
92 
93  // Get effective domain size (without ghost layer)
94  int sizeX = grid0_->getSizeX() - 2;
95  int sizeY = grid0_->getSizeY() - 2;
96  int sizeZ = grid0_->getSizeZ() - 2;
97 
98  int numCells = sizeX * sizeY * sizeZ;
99 
100  struct timeval start, end;
101  T stepTime = 0.;
102 
103  gettimeofday(&start, NULL);
104 
105  // loop over all cells but the ghost layer
106  for (int z = 1; z <= sizeZ - 1; z++) {
107  for (int y = 1; y <= sizeY - 1; y++) {
108  for (int x = 1; x <= sizeX - 1; x++) {
109 
110 #ifdef NSMAGO
111  // Perform actual collision and streaming step
112  collideStream( x, y, z );
113 #else
114  // Perform collision and streaming step with Smagorinsky turbulence
115  // correction
116  collideStreamSmagorinsky( x, y, z );
117 #endif
118 
119  } // x
120  } // y
121  } // z
122 
123 #ifdef LBM_ONLY
124  gettimeofday(&end, NULL);
125  T scTime = getTime(start, end);
126 #endif
127 
128  // Treat no-slip boundary conditions (walls)
129  treatNoslip();
130 
131 #ifdef LBM_ONLY
132  gettimeofday(&start, NULL);
133  T nTime = noslipCells_.size() ? getTime(end, start) : 0.;
134 #endif
135 
136  // Treat velocity cells
137  treatVelocity();
138 
139 #ifdef LBM_ONLY
140  gettimeofday(&end, NULL);
141  T vTime = velocityCells_.size() ? getTime(start, end) : 0.;
142 #endif
143 
144  // Treat inflow boundary conditions
145  treatInflow();
146 
147 #ifdef LBM_ONLY
148  gettimeofday(&start, NULL);
149  T iTime = inflowCells_.size() ? getTime(end, start) : 0.;
150 #endif
151 
152  // Treat outflow boundary conditions
153  treatOutflow();
154 
155 #ifdef LBM_ONLY
156  gettimeofday(&end, NULL);
157  T oTime = outflowCells_.size() ? getTime(start, end) : 0.;
158 #endif
159 
160  // Treat pressure cells
161  treatPressure();
162 
163 #ifdef LBM_ONLY
164  gettimeofday(&start, NULL);
165  T pTime = pressureCells_.size() ? getTime(end, start) : 0.;
166 #endif
167 
168  // Treat curved boundary cells
169  treatCurved();
170 
171 #ifdef LBM_ONLY
172  gettimeofday(&end, NULL);
173  T cTime = curvedCells_.size() ? getTime(start, end) : 0.;
174 #endif
175 
176  // Treat staircase approximated curved boundary cells
177  treatStaircase();
178 
179 #ifdef LBM_ONLY
180  gettimeofday(&start, NULL);
181  T sTime = staircaseCells_.size() ? getTime(end, start) : 0.;
182 #endif
183 
184  moveSphere();
185 
186  gettimeofday(&end, NULL);
187 
188  // exchange grids for current and previous time step
189  Grid<T, Dim> *gridTmp = grid0_;
190  grid0_ = grid1_;
191  grid1_ = gridTmp;
192 
193 #ifdef LBM_ONLY
194  T mTime = sphereObstacles_.size() ? getTime(start, end) : 0.;
195  stepTime = scTime + nTime + vTime + iTime + oTime + pTime + cTime + mTime;
196 
197  if ( prof_.fileName.length() ) {
198  prof_.scTime += scTime;
199  prof_.nTime += nTime;
200  prof_.vTime += vTime;
201  prof_.iTime += iTime;
202  prof_.oTime += oTime;
203  prof_.pTime += pTime;
204  prof_.cTime += cTime;
205  prof_.sTime += sTime;
206  prof_.mTime += mTime;
207  }
208 
209  std::cout << curStep_ << " / " << maxSteps_ << ": " << scTime << " + ";
210  std::cout << nTime << " + " << vTime << " + " << iTime << " + " << oTime;
211  std::cout << " + " << pTime << " + " << cTime << " + " << sTime << " + " << mTime << " = ";
212  std::cout << stepTime << "s -> " << numCells / (stepTime * 1000000);
213  std::cout << " MLUP/s" << std::endl;
214 #else
215  stepTime = getTime(start, end);
216 #endif
217 
218  if ( vtkStep_ != 0 && curStep_ % vtkStep_ == 0 )
219  writeVtkFile();
220 
221  ++curStep_;
222  return stepTime;
223 }
224 
225 template<typename T>
226 inline Vec3<T> LBM<T>::getVelocity( T x, T y, T z ) {
227  int xf = (int) x;
228  int yf = (int) y;
229  int zf = (int) z;
230  int xc = xf + 1;
231  int yc = yf + 1;
232  int zc = zf + 1;
233  T xd = x - xf;
234  T yd = y - yf;
235  T zd = z - zf;
236  T u_x = (1. - xd) * (
237  (1. - yd) * ( u_(xf, yf, zf, 0) * (1. - zd) + u_(xf, yf, zc, 0) * zd )
238  + yd * ( u_(xf, yc, zf, 0) * (1. - zd) + u_(xf, yc, zc, 0) * zd )
239  ) + xd * (
240  (1. - yd) * ( u_(xc, yf, zf, 0) * (1. - zd) + u_(xc, yf, zc, 0) * zd )
241  + yd * ( u_(xc, yc, zf, 0) * (1. - zd) + u_(xc, yc, zc, 0) * zd )
242  );
243  T u_y = (1. - xd) * (
244  (1. - yd) * ( u_(xf, yf, zf, 1) * (1. - zd) + u_(xf, yf, zc, 1) * zd )
245  + yd * ( u_(xf, yc, zf, 1) * (1. - zd) + u_(xf, yc, zc, 1) * zd )
246  ) + xd * (
247  (1. - yd) * ( u_(xc, yf, zf, 1) * (1. - zd) + u_(xc, yf, zc, 1) * zd )
248  + yd * ( u_(xc, yc, zf, 1) * (1. - zd) + u_(xc, yc, zc, 1) * zd )
249  );
250  T u_z = (1. - xd) * (
251  (1. - yd) * ( u_(xf, yf, zf, 2) * (1. - zd) + u_(xf, yf, zc, 2) * zd )
252  + yd * ( u_(xf, yc, zf, 2) * (1. - zd) + u_(xf, yc, zc, 2) * zd )
253  ) + xd * (
254  (1. - yd) * ( u_(xc, yf, zf, 2) * (1. - zd) + u_(xc, yf, zc, 2) * zd )
255  + yd * ( u_(xc, yc, zf, 2) * (1. - zd) + u_(xc, yc, zc, 2) * zd )
256  );
257  return Vec3<T>( u_x, u_y, u_z );
258 }
259 
260 // ========================= //
261 // Internal helper functions //
262 // ========================= //
263 
264 template<typename T>
265 void LBM<T>::setup( ConfBlock& base ) {
266  try {
267 
268  // Read the parameters from the config file
269 
270  std::cout << "Setting up LBM..." << std::endl;
271 
272  ConfBlock* paramBlock = base.find( "domain" );
273  if ( paramBlock == NULL ) {
274  throw "No domain size given.";
275  }
276  int sizeX = paramBlock->getParam<int>( "sizeX" );
277  int sizeY = paramBlock->getParam<int>( "sizeY" );
278  int sizeZ = paramBlock->getParam<int>( "sizeZ" );
279  std::cout << "Read domain specification:" << std::endl;
280  std::cout << "sizeX : " << sizeX << std::endl;
281  std::cout << "sizeY : " << sizeY << std::endl;
282  std::cout << "sizeZ : " << sizeZ << std::endl;
283 
284  paramBlock = base.find( "parameters" );
285  if ( paramBlock == NULL ) {
286  throw "No parameters given.";
287  }
288  omega_ = paramBlock->getParam<T>( "omega" );
289 #ifndef NSMAGO
290  T cSmagorinsky = paramBlock->getParam<T>( "cSmagorinsky" );
291 #endif
292  maxSteps_ = paramBlock->getParam<int>( "maxSteps" );
293  std::cout << "Read parameter specification:" << std::endl;
294  std::cout << "omega : " << omega_ << std::endl;
295 #ifndef NSMAGO
296  std::cout << "Smagorinsky constant : " << cSmagorinsky << std::endl;
297 #endif
298  std::cout << "Number of steps : " << maxSteps_ << std::endl;
299 
300 #ifndef NSMAGO
301  // lattice viscosity
302  nu_ = (2. / omega_ - 1.) * (1. / 6.);
303  // squared Smagorinsky constant
304  cSmagoSqr_ = cSmagorinsky * cSmagorinsky;
305 #endif
306 
307  paramBlock = base.find( "vtk" );
308  if ( paramBlock != NULL ) {
309  vtkStep_ = paramBlock->getParam<int>( "vtkStep" );
310  vtkFileName_ = paramBlock->getParam<std::string>( "vtkFileName" );
311  std::cout << "VTK output specification:" << std::endl;
312  std::cout << "VTK step modulo : " << vtkStep_ << std::endl;
313  std::cout << "VTK output file base name : " << vtkFileName_ << std::endl;
314  } else {
315  std::cout << "No vtk block given in configuration file, no output will be created." << std::endl;
316  vtkStep_ = 0;
317  }
318 
319  paramBlock = base.find( "profiling" );
320  if ( paramBlock != NULL ) {
321  prof_.fileName = paramBlock->getParam<std::string>( "fileName" );
322  std::cout << "Performance summary file name : " << prof_.fileName << std::endl;
323  } else {
324  std::cout << "No profiling block given in configuration file, no performance summary will be created." << std::endl;
325  }
326 
327  std::cout << "Set up the lattices..." << std::endl;
328 
329  // Set up the lattices accordingly
330  grid0_ = new Grid<T,Dim>( sizeX, sizeY, sizeZ );
331  grid1_ = new Grid<T,Dim>( sizeX, sizeY, sizeZ );
332  u_.init( sizeX, sizeY, sizeZ, 0. );
333  rho_.init( sizeX, sizeY, sizeZ, 1. );
334  flag_.init( sizeX, sizeY, sizeZ, UNDEFINED );
335 
336  std::cout << "Set up the boundaries..." << std::endl;
337 
338  // Set up the boundaries
339  paramBlock = base.find( "boundaries" );
340  if ( paramBlock == NULL ) {
341  throw "No boundary description given.";
342  }
343 
344  std::cout << "Bottom..." << std::endl;
345 
346  // Bottom
347  ConfBlock* boundBlock = paramBlock->find( "bottom" );
348  if ( boundBlock != NULL ) setupBoundary( *boundBlock, -1, 1, -1 );
349  // Fill unspecified cells with no slip boundary
350  for ( int z = 2; z < sizeZ - 2; ++z ) {
351  for ( int x = 1; x <= sizeX - 2; ++x ) {
352  if ( flag_( x, 1, z ) == UNDEFINED ) {
353  flag_( x, 1, z ) = NOSLIP;
354  noslipCells_.push_back( Vec3<int>( x, 1, z ) );
355  }
356  }
357  }
358 
359  std::cout << "Top..." << std::endl;
360 
361  // Top
362  boundBlock = paramBlock->find( "top" );
363  if ( boundBlock != NULL ) setupBoundary( *boundBlock, -1, sizeY - 2, -1 );
364  // Fill unspecified cells with no slip boundary
365  for ( int z = 2; z < sizeZ - 2; ++z ) {
366  for ( int x = 1; x <= sizeX - 2; ++x ) {
367  if ( flag_( x, sizeY - 2, z ) == UNDEFINED ) {
368  flag_( x, sizeY - 2, z ) = NOSLIP;
369  noslipCells_.push_back( Vec3<int>( x, sizeY - 2, z ) );
370  }
371  }
372  }
373 
374  std::cout << "Left..." << std::endl;
375 
376  // Left
377  boundBlock = paramBlock->find( "left" );
378  if ( boundBlock != NULL ) setupBoundary( *boundBlock, 1, -1, -1 );
379  // Fill unspecified cells with no slip boundary
380  for ( int z = 2; z < sizeZ - 2; ++z ) {
381  for ( int y = 2; y < sizeY - 2; ++y ) {
382  if ( flag_( 1, y, z ) == UNDEFINED ) {
383  flag_( 1, y, z ) = NOSLIP;
384  noslipCells_.push_back( Vec3<int>( 1, y, z ) );
385  }
386  }
387  }
388 
389  std::cout << "Right..." << std::endl;
390 
391  // Right
392  boundBlock = paramBlock->find( "right" );
393  if ( boundBlock != NULL ) setupBoundary( *boundBlock, sizeX - 2, -1, -1 );
394  // Fill unspecified cells with no slip boundary
395  for ( int z = 2; z < sizeZ - 2; ++z ) {
396  for ( int y = 2; y < sizeY - 2; ++y ) {
397  if ( flag_( sizeX - 2, y, z ) == UNDEFINED ) {
398  flag_( sizeX - 2, y, z ) = NOSLIP;
399  noslipCells_.push_back( Vec3<int>( sizeX - 2, y, z ) );
400  }
401  }
402  }
403 
404  std::cout << "Front..." << std::endl;
405 
406  // Front
407  boundBlock = paramBlock->find( "front" );
408  if ( boundBlock != NULL ) setupBoundary( *boundBlock, -1, -1, 1 );
409  // Fill unspecified cells with no slip boundary
410  for ( int y = 1; y <= sizeY - 2; ++y ) {
411  for ( int x = 1; x <= sizeX - 2; ++x ) {
412  if ( flag_( x, y, 1 ) == UNDEFINED ) {
413  flag_( x, y, 1 ) = NOSLIP;
414  noslipCells_.push_back( Vec3<int>( x, y, 1 ) );
415  }
416  }
417  }
418 
419  std::cout << "Back..." << std::endl;
420 
421  // Back
422  boundBlock = paramBlock->find( "back" );
423  if ( boundBlock != NULL ) setupBoundary( *boundBlock, -1, -1, sizeZ - 2 );
424  // Fill unspecified cells with no slip boundary
425  for ( int y = 1; y <= sizeY - 2; ++y ) {
426  for ( int x = 1; x <= sizeX - 2; ++x ) {
427  if ( flag_( x, y, sizeZ - 2 ) == UNDEFINED ) {
428  flag_( x, y, sizeZ - 2 ) = NOSLIP;
429  noslipCells_.push_back( Vec3<int>( x, y, sizeZ - 2 ) );
430  }
431  }
432  }
433 
434  // Set up the obstacles
435  paramBlock = base.find( "obstacles" );
436  if ( paramBlock == NULL ) {
437  std::cout << "No obstacles defined" << std::endl;
438  } else {
439 
440  std::cout << "Set up the obstacles..." << std::endl;
441 
442  ConfBlock::childIterPair bit = paramBlock->findAll( "cuboid_stationary" );
443  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
444 
445  ConfBlock& b = it->second;
446 
447  int xStart = b.getParam<int>( "xStart" );
448  int xEnd = b.getParam<int>( "xEnd" );
449  int yStart = b.getParam<int>( "yStart" );
450  int yEnd = b.getParam<int>( "yEnd" );
451  int zStart = b.getParam<int>( "zStart" );
452  int zEnd = b.getParam<int>( "zEnd" );
453 
454  std::cout << "Stationary cuboid ranging from <" << xStart << ",";
455  std::cout << yStart << "," << zStart << "> to <" << xEnd << "," << yEnd;
456  std::cout << "," << zEnd << ">" << std::endl;
457 
458  assert( xStart > 1 && xEnd < flag_.getSizeX() - 2 &&
459  yStart > 1 && yEnd < flag_.getSizeY() - 2 &&
460  zStart > 1 && zEnd < flag_.getSizeZ() - 2 );
461  for ( int z = zStart; z <= zEnd; ++z ) {
462  for ( int y = yStart; y <= yEnd; ++y ) {
463  for ( int x = xStart; x <= xEnd; ++x ) {
464  assert( flag_( x, y, z ) == UNDEFINED );
465  flag_( x, y, z ) = NOSLIP;
466  // Only add cells at the obstacle boundary to the noslip
467  // processing vector
468  if ( z == zStart || z == zEnd || y == yStart ||
469  y == yEnd || x == xStart || x == xEnd )
470  noslipCells_.push_back( Vec3<int>( x, y, z ) );
471  }
472  }
473  }
474  }
475 
476  bit = paramBlock->findAll( "sphere_stationary" );
477  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
478 
479  ConfBlock& bl = it->second;
480 
481  T xCenter = bl.getParam<T>( "xCenter" );
482  T yCenter = bl.getParam<T>( "yCenter" );
483  T zCenter = bl.getParam<T>( "zCenter" );
484  T radius = bl.getParam<T>( "radius" );
485 
486  std::cout << "Stationary sphere centered at <" << xCenter << ",";
487  std::cout << yCenter << "," << zCenter << "> with radius " << radius;
488  std::cout << std::endl;
489 
490  T r2 = radius * radius;
491  // Get bounding box of sphere
492  T zStart = floor( zCenter - radius ) + .5;
493  if ( zStart > sizeZ - .5) continue;
494  if ( zStart < 1.5 ) zStart = 1.5;
495  T zEnd = floor( zCenter + radius ) + .5;
496  if ( zEnd < 1.5 ) continue;
497  if ( zEnd > sizeZ - .5) zEnd = sizeZ - .5;
498  T yStart = floor( yCenter - radius ) + .5;
499  if ( yStart > sizeY - .5) continue;
500  if ( yStart < 1.5 ) yStart = 1.5;
501  T yEnd = floor( yCenter + radius ) + .5;
502  if ( yEnd < 1.5 ) continue;
503  if ( yEnd > sizeY - .5) yEnd = sizeY - .5;
504  T xStart = floor( xCenter - radius ) + .5;
505  if ( xStart > sizeX - .5) continue;
506  if ( xStart < 1.5 ) xStart = 1.5;
507  T xEnd = floor( xCenter + radius ) + .5;
508  if ( xEnd < 1.5 ) continue;
509  if ( xEnd > sizeX - .5) xEnd = sizeX - .5;
510 
511  // Go over cubic bounding box of sphere and check which cells are inside
512  for ( T z = zStart; z <= zEnd; z += 1 )
513  for ( T y = yStart; y <= yEnd; y += 1 )
514  for ( T x = xStart; x <= xEnd; x += 1 ) {
515  // Check if current cell center lies within sphere
516  if ( (x - xCenter) * (x - xCenter)
517  + (y - yCenter) * (y - yCenter)
518  + (z - zCenter) * (z - zCenter) < r2 ) {
519  flag_( (int) x, (int) y, (int) z ) = NOSLIP;
520 // std::cout << "Setting cell <" << (int)x << "," << (int)y << "," << (int)z << "> NOSLIP" << std::endl;
521  }
522  }
523  // Go over bounding box again and check which cells are actually
524  // boundary cells
525  for ( int z = zStart; z < zEnd; ++z )
526  for ( int y = yStart; y < yEnd; ++y )
527  for ( int x = xStart; x < xEnd; ++x ) {
528  if ( flag_( x, y, z ) == NOSLIP ) {
529  // Go over all velocity directions
530  bool isBoundary = false;
531  std::vector<T> delta(19, -1.);
532  for (int f = 1; f < Dim; ++f ) {
533  if ( flag_( x + ex[f], y + ey[f], z + ez[f] ) == UNDEFINED ) {
534  isBoundary = true;
535  T xd = xCenter - (x + .5);
536  T yd = yCenter - (y + .5);
537  T zd = zCenter - (z + .5);
538  T b = exn[f] * xd + eyn[f] * yd + ezn[f] * zd;
539  T c = xd * xd + yd * yd + zd * zd - r2;
540  assert( b*b >= c );
541  delta[f] = 1.0 - ( b + sqrt( b * b - c ) ) / le[f];
542  }
543  }
544  if ( isBoundary ) {
545 // std::cout << "Fluid fractions for lattice links of boundary cell <";
546 // std::cout << x << "," << y << "," << z << ">:\n[ ";
547 // for ( uint i = 0; i < delta.size(); ++i ) {
548 // std::cout << delta[i] << " ";
549 // }
550 // std::cout << "]" << std::endl;
551  curvedCells_.push_back( Vec3<int>( x, y, z ) );
552  curvedDeltas_.push_back( delta );
553  }
554  }
555  }
556  }
557 
558  bit = paramBlock->findAll( "sphere_moving" );
559  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
560 
561  ConfBlock& bl = it->second;
562 
563  T xCenter = bl.getParam<T>( "xCenter" );
564  T yCenter = bl.getParam<T>( "yCenter" );
565  T zCenter = bl.getParam<T>( "zCenter" );
566  T radius = bl.getParam<T>( "radius" );
567  T u_x = bl.getParam<T>( "u_x" );
568  T u_y = bl.getParam<T>( "u_y" );
569  T u_z = bl.getParam<T>( "u_z" );
570 
571  std::cout << "Moving sphere centered at <" << xCenter << ",";
572  std::cout << yCenter << "," << zCenter << "> with radius " << radius;
573  std::cout << " and u=<" << u_x << "," << u_y << "," << u_z << ">";
574  std::cout << std::endl;
575 
576  sphereObstacles_.push_back( Sphere<T>( xCenter, yCenter, zCenter, radius, u_x, u_y, u_z ) );
577  }
578 
579  bit = paramBlock->findAll( "sphere_staircase" );
580  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
581 
582  ConfBlock& bl = it->second;
583 
584  T xCenter = bl.getParam<T>( "xCenter" );
585  T yCenter = bl.getParam<T>( "yCenter" );
586  T zCenter = bl.getParam<T>( "zCenter" );
587  T radius = bl.getParam<T>( "radius" );
588  std::cout << "Stationary sphere centered at <" << xCenter << ",";
589  std::cout << yCenter << "," << zCenter << "> with radius " << radius;
590  std::cout << std::endl;
591 
592  T r2 = radius * radius;
593  // Get bounding box of sphere
594  T zStart = floor( zCenter - radius ) + .5;
595  if ( zStart < 1.5 ) zStart = 1.5;
596  T zEnd = floor( zCenter + radius ) + .5;
597  if ( zEnd > sizeZ - .5) zEnd = sizeZ - .5;
598  T yStart = floor( yCenter - radius ) + .5;
599  if ( yStart < 1.5 ) yStart = 1.5;
600  T yEnd = floor( yCenter + radius ) + .5;
601  if ( yEnd > sizeY - .5) yEnd = sizeY - .5;
602  T xStart = floor( xCenter - radius ) + .5;
603  if ( xStart < 1.5 ) xStart = 1.5;
604  T xEnd = floor( xCenter + radius ) + .5;
605  if ( xEnd > sizeX - .5) xEnd = sizeX - .5;
606 
607  // Go over cubic bounding box of sphere and check which cells are inside
608  for ( T z = zStart; z <= zEnd; z += 1 )
609  for ( T y = yStart; y <= yEnd; y += 1 )
610  for ( T x = xStart; x <= xEnd; x += 1 ) {
611  // Check if current cell center lies within sphere
612  if ( (x - xCenter) * (x - xCenter)
613  + (y - yCenter) * (y - yCenter)
614  + (z - zCenter) * (z - zCenter) < r2 ) {
615  flag_( (int) x, (int) y, (int) z ) = NOSLIP;
616 // noslipCells_.push_back( Vec3<int>( (int) x, (int) y, (int) z ) );
617 // std::cout << "Setting cell <" << (int)x << "," << (int)y << "," << (int)z << "> NOSLIP" << std::endl;
618  }
619  }
620  // Go over bounding box again and check which cells are actually
621  // boundary cells
622  for ( int z = zStart; z < zEnd; ++z )
623  for ( int y = yStart; y < yEnd; ++y )
624  for ( int x = xStart; x < xEnd; ++x ) {
625  if ( flag_( x, y, z ) == NOSLIP ) {
626  // Go over all velocity directions
627  bool isBoundary = false;
628  std::vector<T> delta(19, -1.);
629  for (int f = 1; f < Dim; ++f ) {
630  if ( flag_( x + ex[f], y + ey[f], z + ez[f] ) == UNDEFINED ) {
631  isBoundary = true;
632  break;
633  }
634  }
635  if ( isBoundary ) {
636 // std::cout << "Fluid fractions for lattice links of boundary cell <";
637 // std::cout << x << "," << y << "," << z << ">:\n[ ";
638 // for ( uint i = 0; i < delta.size(); ++i ) {
639 // std::cout << delta[i] << " ";
640 // }
641 // std::cout << "]" << std::endl;
642  staircaseCells_.push_back( Vec3<int>( x, y, z ) );
643  }
644  }
645  }
646  }
647 
648  bit = paramBlock->findAll( "inflow" );
649  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
650 
651  ConfBlock& b = it->second;
652 
653  int xStart = b.getParam<int>( "xStart" );
654  int xEnd = b.getParam<int>( "xEnd" );
655  int yStart = b.getParam<int>( "yStart" );
656  int yEnd = b.getParam<int>( "yEnd" );
657  int zStart = b.getParam<int>( "zStart" );
658  int zEnd = b.getParam<int>( "zEnd" );
659  T u_x = b.getParam<T>( "u_x" );
660  T u_y = b.getParam<T>( "u_y" );
661  T u_z = b.getParam<T>( "u_z" );
662  assert( xStart > 1 && xEnd < flag_.getSizeX() - 2 &&
663  yStart > 1 && yEnd < flag_.getSizeY() - 2 &&
664  zStart > 1 && zEnd < flag_.getSizeZ() - 2 );
665  for ( int z = zStart; z <= zEnd; ++z ) {
666  for ( int y = yStart; y <= yEnd; ++y ) {
667  for ( int x = xStart; x <= xEnd; ++x ) {
668  assert( flag_( x, y, z ) == UNDEFINED );
669  flag_( x, y, z ) = NOSLIP;
670  inflowCells_.push_back( Vec3<int>( x, y, z ) );
671  inflowVels_.push_back( Vec3<T>( u_x, u_y, u_z ) );
672  }
673  }
674  }
675  }
676  }
677 
678  std::cout << "Flag fluid cells..." << std::endl;
679 
680  // Non-boundary cells and non-obstacle cells are fluid cells
681  for ( int z = 2; z < sizeZ - 2; ++z )
682  for ( int y = 2; y < sizeY - 2; ++y )
683  for ( int x = 2; x < sizeX - 2; ++x )
684  if ( flag_( x, y, z ) == UNDEFINED ) flag_( x, y, z ) = FLUID;
685 
686  std::cout << "Initialize distribution functions with equilibrium..." << std::endl;
687 
688  // initialize distribution functions with equilibrium
689  for ( int z = 0; z < sizeZ; ++z )
690  for ( int y = 0; y < sizeY; ++y )
691  for ( int x = 0; x < sizeX; ++x )
692  for ( int i = 0; i < Dim; ++i )
693  (*grid0_)( x, y, z, i ) = w[i];
694  for ( int z = 0; z < sizeZ; ++z )
695  for ( int y = 0; y < sizeY; ++y )
696  for ( int x = 0; x < sizeX; ++x )
697  for ( int i = 0; i < Dim; ++i )
698  (*grid1_)( x, y, z, i ) = w[i];
699 
700  } catch ( std::exception& e ) {
701  std::cerr << e.what() << std::endl;
702  exit( -1 );
703  } catch ( const char* e ) {
704  std::cerr << e << std::endl;
705  exit( -1 );
706  }
707 
708  std::cout << "LBM setup finished!" << std::endl;
709 }
710 
711 template<typename T>
712 inline void LBM<T>::setupBoundary( ConfBlock& block, int x, int y, int z ) {
713 
714  ConfBlock::childIterPair bit = block.findAll( "noslip" );
715 
716  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
717 
718  ConfBlock& b = it->second;
719 
720  int xStart = ( x == -1 ) ? b.getParam<int>( "xStart" ) : x;
721  int xEnd = ( x == -1 ) ? b.getParam<int>( "xEnd" ) : x;
722  int yStart = ( y == -1 ) ? b.getParam<int>( "yStart" ) : y;
723  int yEnd = ( y == -1 ) ? b.getParam<int>( "yEnd" ) : y;
724  int zStart = ( z == -1 ) ? b.getParam<int>( "zStart" ) : z;
725  int zEnd = ( z == -1 ) ? b.getParam<int>( "zEnd" ) : z;
726  assert( xStart > 0 && xEnd < flag_.getSizeX() - 1 &&
727  yStart > 0 && yEnd < flag_.getSizeY() - 1 &&
728  zStart > 0 && zEnd < flag_.getSizeZ() - 1 );
729  for ( z = zStart; z <= zEnd; ++z ) {
730  for ( y = yStart; y <= yEnd; ++y ) {
731  for ( x = xStart; x <= xEnd; ++x ) {
732  assert( flag_( x, y, z ) == UNDEFINED );
733  flag_( x, y, z ) = NOSLIP;
734  noslipCells_.push_back( Vec3<int>( x, y, z ) );
735  }
736  }
737  }
738 
739  }
740 
741  bit = block.findAll( "velocity" );
742 
743  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
744 
745  ConfBlock& b = it->second;
746 
747  int xStart = ( x == -1 ) ? b.getParam<int>( "xStart" ) : x;
748  int xEnd = ( x == -1 ) ? b.getParam<int>( "xEnd" ) : x;
749  int yStart = ( y == -1 ) ? b.getParam<int>( "yStart" ) : y;
750  int yEnd = ( y == -1 ) ? b.getParam<int>( "yEnd" ) : y;
751  int zStart = ( z == -1 ) ? b.getParam<int>( "zStart" ) : z;
752  int zEnd = ( z == -1 ) ? b.getParam<int>( "zEnd" ) : z;
753  T u_x = b.getParam<T>( "u_x" );
754  T u_y = b.getParam<T>( "u_y" );
755  T u_z = b.getParam<T>( "u_z" );
756  assert( xStart > 0 && xEnd < flag_.getSizeX() - 1 &&
757  yStart > 0 && yEnd < flag_.getSizeY() - 1 &&
758  zStart > 0 && zEnd < flag_.getSizeZ() - 1 );
759  for ( z = zStart; z <= zEnd; ++z ) {
760  for ( y = yStart; y <= yEnd; ++y ) {
761  for ( x = xStart; x <= xEnd; ++x ) {
762  assert( flag_( x, y, z ) == UNDEFINED );
763  flag_( x, y, z ) = VELOCITY;
764  velocityCells_.push_back( Vec3<int>( x, y, z ) );
765  velocityVels_.push_back( Vec3<T>( u_x, u_y, u_z ) );
766  }
767  }
768  }
769 
770  }
771 
772  bit = block.findAll( "pressure" );
773 
774  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
775 
776  ConfBlock& b = it->second;
777 
778  int xStart = ( x == -1 ) ? b.getParam<int>( "xStart" ) : x;
779  int xEnd = ( x == -1 ) ? b.getParam<int>( "xEnd" ) : x;
780  int yStart = ( y == -1 ) ? b.getParam<int>( "yStart" ) : y;
781  int yEnd = ( y == -1 ) ? b.getParam<int>( "yEnd" ) : y;
782  int zStart = ( z == -1 ) ? b.getParam<int>( "zStart" ) : z;
783  int zEnd = ( z == -1 ) ? b.getParam<int>( "zEnd" ) : z;
784  int xDir = ( x == -1 ) ? 0 : ( ( x > 1 ) ? -1 : 1 );
785  int yDir = ( y == -1 ) ? 0 : ( ( y > 1 ) ? -1 : 1 );
786  int zDir = ( z == -1 ) ? 0 : ( ( z > 1 ) ? -1 : 1 );
787  int f = 0;
788  for ( int i = 0; i < Dim; ++i ) {
789  if ( ex[i] == xDir && ey[i] == yDir && ez[i] == zDir ) {
790  f = i;
791  break;
792  }
793  }
794  assert( f > 0 );
795  assert( xStart > 0 && xEnd < flag_.getSizeX() - 1 &&
796  yStart > 0 && yEnd < flag_.getSizeY() - 1 &&
797  zStart > 0 && zEnd < flag_.getSizeZ() - 1 );
798  for ( z = zStart; z <= zEnd; ++z ) {
799  for ( y = yStart; y <= yEnd; ++y ) {
800  for ( x = xStart; x <= xEnd; ++x ) {
801  assert( flag_( x, y, z ) == UNDEFINED );
802  flag_( x, y, z ) = PRESSURE;
803  pressureCells_.push_back( Vec3<int>( x, y, z ) );
804  pressureDFs_.push_back( f );
805  }
806  }
807  }
808 
809  }
810 
811  bit = block.findAll( "inflow" );
812 
813  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
814 
815  ConfBlock& b = it->second;
816 
817  int xStart = ( x == -1 ) ? b.getParam<int>( "xStart" ) : x;
818  int xEnd = ( x == -1 ) ? b.getParam<int>( "xEnd" ) : x;
819  int yStart = ( y == -1 ) ? b.getParam<int>( "yStart" ) : y;
820  int yEnd = ( y == -1 ) ? b.getParam<int>( "yEnd" ) : y;
821  int zStart = ( z == -1 ) ? b.getParam<int>( "zStart" ) : z;
822  int zEnd = ( z == -1 ) ? b.getParam<int>( "zEnd" ) : z;
823  T u_x = b.getParam<T>( "u_x" );
824  T u_y = b.getParam<T>( "u_y" );
825  T u_z = b.getParam<T>( "u_z" );
826  assert( xStart > 0 && xEnd < flag_.getSizeX() - 1 &&
827  yStart > 0 && yEnd < flag_.getSizeY() - 1 &&
828  zStart > 0 && zEnd < flag_.getSizeZ() - 1 );
829  for ( z = zStart; z <= zEnd; ++z ) {
830  for ( y = yStart; y <= yEnd; ++y ) {
831  for ( x = xStart; x <= xEnd; ++x ) {
832  assert( flag_( x, y, z ) == UNDEFINED );
833  flag_( x, y, z ) = INFLOW;
834  inflowCells_.push_back( Vec3<int>( x, y, z ) );
835  inflowVels_.push_back( Vec3<T>( u_x, u_y, u_z ) );
836  }
837  }
838  }
839 
840  }
841 
842  bit = block.findAll( "outflow" );
843 
844  for ( ConfBlock::childIter it = bit.first; it != bit.second; ++it ) {
845 
846  ConfBlock& b = it->second;
847 
848  int xStart = ( x == -1 ) ? b.getParam<int>( "xStart" ) : x;
849  int xEnd = ( x == -1 ) ? b.getParam<int>( "xEnd" ) : x;
850  int yStart = ( y == -1 ) ? b.getParam<int>( "yStart" ) : y;
851  int yEnd = ( y == -1 ) ? b.getParam<int>( "yEnd" ) : y;
852  int zStart = ( z == -1 ) ? b.getParam<int>( "zStart" ) : z;
853  int zEnd = ( z == -1 ) ? b.getParam<int>( "zEnd" ) : z;
854  int xDir = ( x == -1 ) ? 0 : ( ( x > 1 ) ? -1 : 1 );
855  int yDir = ( y == -1 ) ? 0 : ( ( y > 1 ) ? -1 : 1 );
856  int zDir = ( z == -1 ) ? 0 : ( ( z > 1 ) ? -1 : 1 );
857  int f = 0;
858  for ( int i = 0; i < Dim; ++i ) {
859  if ( ex[i] == xDir && ey[i] == yDir && ez[i] == zDir ) {
860  f = i;
861  break;
862  }
863  }
864  assert( f > 0 );
865  assert( xStart > 0 && xEnd < flag_.getSizeX() - 1 &&
866  yStart > 0 && yEnd < flag_.getSizeY() - 1 &&
867  zStart > 0 && zEnd < flag_.getSizeZ() - 1 );
868  for ( z = zStart; z <= zEnd; ++z ) {
869  for ( y = yStart; y <= yEnd; ++y ) {
870  for ( x = xStart; x <= xEnd; ++x ) {
871  assert( flag_( x, y, z ) == UNDEFINED );
872  flag_( x, y, z ) = OUTFLOW;
873  outflowCells_.push_back( Vec3<int>( x, y, z ) );
874  outflowDFs_.push_back( f );
875  }
876  }
877  }
878 
879  }
880 }
881 
882 template<typename T>
883 inline T LBM<T>::getTime( timeval &start, timeval &end ) {
884  return (T) ( end.tv_sec - start.tv_sec )
885  + (T) ( end.tv_usec - start.tv_usec ) / 1000000.;
886 }
887 
888 template<typename T>
889 inline void LBM<T>::collideStream( int x, int y, int z ) {
890 
891  // calculate rho and u
892  T rho = (*grid0_)( x, y, z, 0 ); // df in center
893  T ux = 0.;
894  T uy = 0.;
895  T uz = 0.;
896  // loop over all velocity directions but center
897  for ( int f = 1; f < Dim; ++f ) {
898  T fi = (*grid0_)( x, y, z, f );
899  rho += fi;
900  ux += ex[f] * fi;
901  uy += ey[f] * fi;
902  uz += ez[f] * fi;
903  }
904  // DEBUG assertions
905  assert ( rho > 0.8 && rho < 1.2 );
906  assert ( fabs(ux) < 2. );
907  assert ( fabs(uy) < 2. );
908  assert ( fabs(uz) < 2. );
909  rho_( x, y, z ) = rho;
910  u_( x, y, z, 0 ) = ux;
911  u_( x, y, z, 1 ) = uy;
912  u_( x, y, z, 2 ) = uz;
913 
914  // collision step: calculate equilibrium distribution values and
915  // perform collision (weighting with current distribution values)
916  // streaming step: stream distribution values to neighboring cells
917  T fc = rho - 1.5 * ( ux * ux + uy * uy + uz * uz );
918  T omegai = 1 - omega_;
919  // treat center value specially
920  (*grid1_)( x, y, z, 0 ) = omegai * (*grid0_)( x, y, z, 0 )
921  + omega_ * w[0] * fc;
922  // loop over all velocity directions but center
923  for ( int f = 1; f < Dim; ++f ) {
924  T eiu = ex[f] * ux + ey[f] * uy + ez[f] * uz;
925  (*grid1_)( x + ex[f], y + ey[f], z + ez[f], f )
926  = omegai * (*grid0_)( x, y, z, f )
927  + omega_ * w[f] * ( fc + 3 * eiu + 4.5 * eiu * eiu);
928  }
929 }
930 
931 #ifndef NSMAGO
932 template<typename T>
933 inline void LBM<T>::collideStreamSmagorinsky( int x, int y, int z ) {
934 
935  // Calculate rho and u
936  T rho = (*grid0_)( x, y, z, 0 ); // df in center
937  T ux = 0.;
938  T uy = 0.;
939  T uz = 0.;
940  // Loop over all velocity directions but center
941  for ( int f = 1; f < Dim; ++f ) {
942  T fi = (*grid0_)( x, y, z, f );
943  rho += fi;
944  ux += ex[f] * fi;
945  uy += ey[f] * fi;
946  uz += ez[f] * fi;
947  }
948  // DEBUG assertions
949  assert ( rho > 0.5 && rho < 1.5 );
950  assert ( fabs(ux) < 2. );
951  assert ( fabs(uy) < 2. );
952  assert ( fabs(uz) < 2. );
953  rho_( x, y, z ) = rho;
954  u_( x, y, z, 0 ) = ux;
955  u_( x, y, z, 1 ) = uy;
956  u_( x, y, z, 2 ) = uz;
957 
958  // Collision step: calculate equilibrium distribution values and
959  // perform collision (weighting with current distribution values)
960  // streaming step: stream distribution values to neighboring cells
961  T fc = rho - 1.5 * ( ux * ux + uy * uy + uz * uz );
962  T feq[19];
963 
964  // Calculate equilibrium distribution functions
965  feq[0] = (1./3.) * fc; // C
966  feq[1] = (1./18.) * ( fc + 3 * uy + 4.5 * uy * uy ); // N
967  feq[2] = (1./18.) * ( fc + 3 * ux + 4.5 * ux * ux ); // E
968  feq[3] = (1./18.) * ( fc - 3 * uy + 4.5 * uy * uy ); // S
969  feq[4] = (1./18.) * ( fc - 3 * ux + 4.5 * ux * ux ); // W
970  feq[5] = (1./18.) * ( fc + 3 * uz + 4.5 * uz * uz ); // T
971  feq[6] = (1./18.) * ( fc - 3 * uz + 4.5 * uz * uz ); // B
972  feq[7] = (1./36.) * ( fc + 3 * ( ux + uy ) + 4.5 * ( ux + uy ) * ( ux + uy ) ); // NE
973  feq[8] = (1./36.) * ( fc + 3 * ( ux - uy ) + 4.5 * ( ux - uy ) * ( ux - uy ) ); // SE
974  feq[9] = (1./36.) * ( fc - 3 * ( ux + uy ) + 4.5 * ( ux + uy ) * ( ux + uy ) ); // SW
975  feq[10] = (1./36.) * ( fc - 3 * ( ux - uy ) + 4.5 * ( ux - uy ) * ( ux - uy ) ); // NW
976  feq[11] = (1./36.) * ( fc + 3 * ( uy + uz ) + 4.5 * ( uy + uz ) * ( uy + uz ) ); // TN
977  feq[12] = (1./36.) * ( fc + 3 * ( ux + uz ) + 4.5 * ( ux + uz ) * ( ux + uz ) ); // TE
978  feq[13] = (1./36.) * ( fc - 3 * ( uy - uz ) + 4.5 * ( uy - uz ) * ( uy - uz ) ); // TS
979  feq[14] = (1./36.) * ( fc - 3 * ( ux - uz ) + 4.5 * ( ux - uz ) * ( ux - uz ) ); // TW
980  feq[15] = (1./36.) * ( fc + 3 * ( uy - uz ) + 4.5 * ( uy - uz ) * ( uy - uz ) ); // BN
981  feq[16] = (1./36.) * ( fc + 3 * ( ux - uz ) + 4.5 * ( ux - uz ) * ( ux - uz ) ); // BE
982  feq[17] = (1./36.) * ( fc - 3 * ( uy + uz ) + 4.5 * ( uy + uz ) * ( uy + uz ) ); // BS
983  feq[18] = (1./36.) * ( fc - 3 * ( ux + uz ) + 4.5 * ( ux + uz ) * ( ux + uz ) ); // BW
984 
985  // Calculate non-equilibrium stress tensor
986  T qo = 0.;
987  for ( int i = 0; i < 3; ++i ) {
988  T qadd = 0.;
989  for ( int f = 1; f < 19; ++f ) {
990  qadd += ep[i][f] * ( (*grid0_)( x, y, z, f ) - feq[f] );
991  }
992  qo += qadd * qadd;
993  }
994  qo *= 2.;
995  for ( int i = 3; i < 6; ++i ) {
996  T qadd = 0.;
997  for ( int f = 7; f < 19; ++f ) {
998  qadd += ep[i][f] * ( (*grid0_)( x, y, z, f ) - feq[f] );
999  }
1000  qo += qadd * qadd;
1001  }
1002  qo = sqrt( qo );
1003 
1004  // Calculate local stress tensor
1005  T s = ( sqrt( nu_ * nu_ + 18. * cSmagoSqr_ * qo ) - nu_ ) / ( 6. * cSmagoSqr_);
1006  // Calculate turbulence modified inverse lattice viscosity
1007  T omega = 1. / ( 3. * ( nu_ + cSmagoSqr_ * s ) + .5 );
1008  T omegai = 1. - omega;
1009 
1010  // Loop over all velocity directions and stream collided distribution value
1011  // to neighboring cells
1012  for ( int f = 0; f < Dim; ++f ) {
1013  (*grid1_)( x + ex[f], y + ey[f], z + ez[f], f )
1014  = omegai * (*grid0_)( x, y, z, f ) + omega * feq[f];
1015  }
1016 }
1017 #endif
1018 
1019 template<typename T>
1020 inline void LBM<T>::treatNoslip() {
1021 
1022  // Iterate over all no-slip boundary cells
1023  std::vector< Vec3<int> >::iterator iter;
1024  for( iter = noslipCells_.begin(); iter != noslipCells_.end(); iter++ ) {
1025 
1026  // Fetch coordinates of current boundary cell
1027  int x = (*iter)[0];
1028  int y = (*iter)[1];
1029  int z = (*iter)[2];
1030 
1031  // Go over all distribution values and stream to inverse distribution
1032  // value of adjacent cell in inverse direction (bounce back)
1033  for ( int f = 1; f < Dim; ++f ) {
1034  (*grid1_)( x - ex[f], y - ey[f], z - ez[f], finv[f] ) = (*grid1_)( x, y, z, f );
1035  }
1036  }
1037 }
1038 
1039 template<typename T>
1040 inline void LBM<T>::treatStaircase() {
1041 
1042  // Iterate over all no-slip boundary cells
1043  std::vector< Vec3<int> >::iterator iter;
1044  for( iter = staircaseCells_.begin(); iter != staircaseCells_.end(); iter++ ) {
1045 
1046  // Fetch coordinates of current boundary cell
1047  int x = (*iter)[0];
1048  int y = (*iter)[1];
1049  int z = (*iter)[2];
1050 
1051  // Go over all distribution values and stream to inverse distribution
1052  // value of adjacent cell in inverse direction (bounce back)
1053  for ( int f = 1; f < Dim; ++f ) {
1054  (*grid1_)( x - ex[f], y - ey[f], z - ez[f], finv[f] ) = (*grid1_)( x, y, z, f );
1055  }
1056  }
1057  }
1058 
1059 template<typename T>
1060 inline void LBM<T>::treatVelocity() {
1061 
1062  // Iterate over all velocity boundary cells
1063  for( int i = 0; i < (int) velocityCells_.size(); ++i ) {
1064 
1065  // Fetch coordinates of current boundary cell
1066  int x = velocityCells_[i][0];
1067  int y = velocityCells_[i][1];
1068  int z = velocityCells_[i][2];
1069  // Fetch velocity of moving wall
1070  T ux = velocityVels_[i][0];
1071  T uy = velocityVels_[i][1];
1072  T uz = velocityVels_[i][2];
1073  // Fetch density of current cell
1074  T rho = 6 * rho_( x, y, z );
1075  // Set velocity of this cell
1076  u_( x, y, z, 0 ) = ux;
1077  u_( x, y, z, 1 ) = uy;
1078  u_( x, y, z, 2 ) = uz;
1079 
1080  // Go over all distribution values, stream to inverse distribution value of
1081  // adjacent cell in inverse direction (bounce back) and modify by velocity
1082  // of moving wall
1083  for ( int f = 1; f < Dim; ++f ) {
1084  int op = finv[f];
1085  (*grid1_)( x - ex[f], y - ey[f], z - ez[f], op )
1086  = (*grid1_)( x, y, z, f )
1087  + rho * w[f] * ( ex[op] * ux + ey[op] * uy + ez[op] * uz );
1088  }
1089  }
1090 }
1091 
1092 template<typename T>
1093 inline void LBM<T>::treatInflow() {
1094 
1095  // Iterate over all inflow boundary cells
1096  for( int i = 0; i < (int) inflowCells_.size(); ++i ) {
1097 
1098  // Fetch coordinates of current boundary cell
1099  int x = inflowCells_[i][0];
1100  int y = inflowCells_[i][1];
1101  int z = inflowCells_[i][2];
1102  // Fetch inflow velocity
1103  T ux = inflowVels_[i][0];
1104  T uy = inflowVels_[i][1];
1105  T uz = inflowVels_[i][2];
1106  // Set velocity of this cell
1107  u_( x, y, z, 0 ) = ux;
1108  u_( x, y, z, 1 ) = uy;
1109  u_( x, y, z, 2 ) = uz;
1110 
1111  // Calculate equilibrium distribution functions with fixed density of 1.0
1112  // and set as distribution values of the inflow cell
1113  T fc = 1. - 1.5 * ( ux * ux + uy * uy + uz * uz );
1114  (*grid1_)( x, y, z, 0 ) = (1./3.) * fc; // C
1115  (*grid1_)( x, y, z, 1 ) = (1./18.) * ( fc + 3 * uy + 4.5 * uy * uy ); // N
1116  (*grid1_)( x, y, z, 2 ) = (1./18.) * ( fc + 3 * ux + 4.5 * ux * ux ); // E
1117  (*grid1_)( x, y, z, 3 ) = (1./18.) * ( fc - 3 * uy + 4.5 * uy * uy ); // S
1118  (*grid1_)( x, y, z, 4 ) = (1./18.) * ( fc - 3 * ux + 4.5 * ux * ux ); // W
1119  (*grid1_)( x, y, z, 5 ) = (1./18.) * ( fc + 3 * uz + 4.5 * uz * uz ); // T
1120  (*grid1_)( x, y, z, 6 ) = (1./18.) * ( fc - 3 * uz + 4.5 * uz * uz ); // B
1121  (*grid1_)( x, y, z, 7 ) = (1./36.) * ( fc + 3 * ( ux + uy ) + 4.5 * ( ux + uy ) * ( ux + uy ) ); // NE
1122  (*grid1_)( x, y, z, 8 ) = (1./36.) * ( fc + 3 * ( ux - uy ) + 4.5 * ( ux - uy ) * ( ux - uy ) ); // SE
1123  (*grid1_)( x, y, z, 9 ) = (1./36.) * ( fc - 3 * ( ux + uy ) + 4.5 * ( ux + uy ) * ( ux + uy ) ); // SW
1124  (*grid1_)( x, y, z, 10 ) = (1./36.) * ( fc - 3 * ( ux - uy ) + 4.5 * ( ux - uy ) * ( ux - uy ) ); // NW
1125  (*grid1_)( x, y, z, 11 ) = (1./36.) * ( fc + 3 * ( uy + uz ) + 4.5 * ( uy + uz ) * ( uy + uz ) ); // TN
1126  (*grid1_)( x, y, z, 12 ) = (1./36.) * ( fc + 3 * ( ux + uz ) + 4.5 * ( ux + uz ) * ( ux + uz ) ); // TE
1127  (*grid1_)( x, y, z, 13 ) = (1./36.) * ( fc - 3 * ( uy - uz ) + 4.5 * ( uy - uz ) * ( uy - uz ) ); // TS
1128  (*grid1_)( x, y, z, 14 ) = (1./36.) * ( fc - 3 * ( ux - uz ) + 4.5 * ( ux - uz ) * ( ux - uz ) ); // TW
1129  (*grid1_)( x, y, z, 15 ) = (1./36.) * ( fc + 3 * ( uy - uz ) + 4.5 * ( uy - uz ) * ( uy - uz ) ); // BN
1130  (*grid1_)( x, y, z, 16 ) = (1./36.) * ( fc + 3 * ( ux - uz ) + 4.5 * ( ux - uz ) * ( ux - uz ) ); // BE
1131  (*grid1_)( x, y, z, 17 ) = (1./36.) * ( fc - 3 * ( uy + uz ) + 4.5 * ( uy + uz ) * ( uy + uz ) ); // BS
1132  (*grid1_)( x, y, z, 18 ) = (1./36.) * ( fc - 3 * ( ux + uz ) + 4.5 * ( ux + uz ) * ( ux + uz ) ); // BW
1133  }
1134 }
1135 
1136 template<typename T>
1137 inline void LBM<T>::treatOutflow() {
1138 
1139  // Iterate over all outflow boundary cells
1140  for( int i = 0; i < (int) outflowCells_.size(); ++i ) {
1141 
1142  // Fetch coordinates of current boundary cell
1143  int x = outflowCells_[i][0];
1144  int y = outflowCells_[i][1];
1145  int z = outflowCells_[i][2];
1146  // Fetch outflow direction
1147  int d = outflowDFs_[i];
1148 
1149  // Go over all distribution values, and copy the distribution values from
1150  // the neighboring cell in outflow direction
1151  for ( int f = 1; f < Dim; ++f ) {
1152  (*grid1_)( x - ex[d], y - ey[d], z - ez[d], f ) = (*grid1_)( x, y, z, f );
1153  }
1154  }
1155 }
1156 
1157 template<typename T>
1158 inline void LBM<T>::treatPressure() {
1159 
1160  // Iterate over all pressure boundary cells
1161  for( uint i = 0; i < pressureCells_.size(); ++i ) {
1162 
1163  // Fetch coordinates of current boundary cell
1164  int x = pressureCells_[i][0];
1165  int y = pressureCells_[i][1];
1166  int z = pressureCells_[i][2];
1167  // Fetch outflow direction
1168  int f = pressureDFs_[i];
1169  // Fetch velocity of current boundary cell
1170  T ux = u_( x, y, z, 0 );
1171  T uy = u_( x, y, z, 1 );
1172  T uz = u_( x, y, z, 2 );
1173 
1174  // Calculate pressure corrected equilibrium distribution functions for
1175  // atmospheric pressure and set as distribution values of the pressure cell
1176  T eiu = ex[f] * ux + ey[f] * uy + ez[f] * uz;
1177  T fc = w[f] * ( 2. - 3. * ( ux * ux + uy * uy + uz * uz ) + 9. * eiu * eiu );
1178  (*grid1_)( x, y, z, f ) = fc - (*grid1_)( x, y, z, finv[f] );
1179  (*grid1_)( x, y, z, finv[f] ) = fc - (*grid1_)( x, y, z, f );
1180  }
1181 }
1182 
1183 template<typename T>
1184 inline void LBM<T>::treatCurved() {
1185 
1186  // Iterate over all curved boundary cells
1187  for ( uint i = 0; i < curvedCells_.size(); ++i ) {
1188  int x = curvedCells_[i][0];
1189  int y = curvedCells_[i][1];
1190  int z = curvedCells_[i][2];
1191  // Go over all lattice links
1192  for ( int f = 1; f < Dim; ++f ) {
1193  T delta = curvedDeltas_[i][f];
1194  // Check whether lattice link crossed obstacle boundary
1195  if ( delta < 0 ) {
1196  continue;
1197  }
1198 /* (*grid1_)( x - ex[f], y - ey[f], z - ez[f], finv[f] ) = (1. / 1. + delta) * (
1199  delta * ( (*grid1_)( x - 2 * ex[f], y - 2 * ey[f], z - 2 * ez[f], finv[f] )
1200  + (*grid1_)( x, y, z, f ) )
1201  + (1. - delta) * (*grid1_)( x - ex[f], y - ey[f], z - ez[f], f ) );*/
1202 
1203  (*grid1_)( x + ex[f], y + ey[f], z + ez[f], f ) = ( 1. / ( 1. + delta) ) * (
1204  delta * ( (*grid1_)( x + 2 * ex[f], y + 2 * ey[f], z + 2 * ez[f], f )
1205  + (*grid1_)( x, y, z, finv[f] ) )
1206  + (1. - delta) * (*grid1_)( x + ex[f], y + ey[f], z + ez[f], finv[f] ) );
1207  }
1208  }
1209 
1210 }
1211 
1212 #define DIST(x,y,z) ((x) - xCenter) * ((x) - xCenter) + ((y) - yCenter) * ((y) - yCenter) + ((z) - zCenter) * ((z) - zCenter)
1213 template<typename T>
1214 inline void LBM<T>::moveSphere() {
1215 
1216  for ( uint i = 0; i < sphereObstacles_.size(); ++i ) {
1217 
1218  T xCenter = sphereObstacles_[i].x;
1219  T yCenter = sphereObstacles_[i].y;
1220  T zCenter = sphereObstacles_[i].z;
1221  T u_x = sphereObstacles_[i].u_x;
1222  T u_y = sphereObstacles_[i].u_y;
1223  T u_z = sphereObstacles_[i].u_z;
1224  T radius = sphereObstacles_[i].r;
1225 // std::cout << curStep_ << ": sphere " << i << " with center <" << xCenter;
1226 // std::cout << "," << yCenter << "," << zCenter << "> and radius " << radius;
1227 // std::cout << std::endl;
1228  T r2 = radius * radius;
1229  // Get bounding box of sphere
1230  T zStart = floor( zCenter - radius ) + .5;
1231  if ( zStart > flag_.getSizeZ() - .5) continue;
1232  if ( zStart < 2.5 ) zStart = 2.5;
1233  T zEnd = floor( zCenter + radius ) + .5;
1234  if ( zEnd < 2.5 ) continue;
1235  if ( zEnd > flag_.getSizeZ() - 1.5) zEnd = flag_.getSizeZ() - 1.5;
1236  T yStart = floor( yCenter - radius ) + .5;
1237  if ( yStart > flag_.getSizeY() - 1.5) continue;
1238  if ( yStart < 2.5 ) yStart = 2.5;
1239  T yEnd = floor( yCenter + radius ) + .5;
1240  if ( yEnd < 2.5 ) continue;
1241  if ( yEnd > flag_.getSizeY() - 1.5) yEnd = flag_.getSizeY() - 1.5;
1242  T xStart = floor( xCenter - radius ) + .5;
1243  if ( xStart > flag_.getSizeX() - 1.5) continue;
1244  if ( xStart < 2.5 ) xStart = 2.5;
1245  T xEnd = floor( xCenter + radius ) + .5;
1246  if ( xEnd < 2.5 ) continue;
1247  if ( xEnd > flag_.getSizeX() - 1.5) xEnd = flag_.getSizeX() - 1.5;
1248 // std::cout << "Bounding box <" << xStart << "," << yStart << "," << zStart;
1249 // std::cout << "> to <" << xEnd << "," << yEnd << "," << zEnd << ">" << std::endl;
1250 
1251  // Go over bounding box and check which cells are actually boundary cells
1252  for ( int z = zStart; z < zEnd; ++z )
1253  for ( int y = yStart; y < yEnd; ++y )
1254  for ( int x = xStart; x < xEnd; ++x ) {
1255  // Check if cell is potential boundary cell
1256 // std::cout << "Point <" << x + 0.5 << "," << y + 0.5 << "," << z + 0.5;
1257 // std::cout << ">, dist " << DIST(x + 0.5, y + 0.5, z + 0.5) << std::endl;
1258  if ( DIST(x + 0.5, y + 0.5, z + 0.5) > r2 ) continue;
1259  // Go over all velocity directions
1260  for (int f = 1; f < Dim; ++f ) {
1261  if ( DIST( x + ex[f] + 0.5, y + ey[f] + 0.5, z + ez[f] + 0.5 ) > r2 ) {
1262  T xd = xCenter - (x + .5);
1263  T yd = yCenter - (y + .5);
1264  T zd = zCenter - (z + .5);
1265  T b = exn[f] * xd + eyn[f] * yd + ezn[f] * zd;
1266  T c = xd * xd + yd * yd + zd * zd - r2;
1267  assert( b*b >= c );
1268  T deltai = ( b + sqrt( b * b - c ) ) / le[f];
1269  T delta = 1.0 - deltai;
1270  T rho = 6 * rho_(x, y, z);
1271 // T rho = 6 * delta * (
1272 // delta * ( rho_(x, y, z) * delta + rho_(x, y, z + ez[f]) * deltai )
1273 // + deltai * ( rho_(x, y + ey[f], z) * delta + rho_(x, y + ey[f], z + ez[f]) * deltai )
1274 // ) + deltai * (
1275 // delta * ( rho_(x + ex[f], y, z) * delta + rho_(x + ex[f], y, z + ez[f]) * deltai )
1276 // + deltai * ( rho_(x + ex[f], y + ey[f], z) * delta + rho_(x + ex[f], y + ey[f], z + ez[f]) * deltai )
1277 // );
1278 
1279 // std::cout << "Cell <" << x << "," << y << "," << z;
1280 // std::cout << ">, DF " << f << ", delta " << delta << std::endl;
1281 // T tmp1 = delta * ( (*grid1_)( x + 2 * ex[f], y + 2 * ey[f], z + 2 * ez[f], f )
1282 // + (*grid1_)( x, y, z, finv[f] ) )
1283 // + (1. - delta) * (*grid1_)( x + ex[f], y + ey[f], z + ez[f], finv[f] );
1284 // T tmp2 = w[f] * rho * ( u_x * ex[f] + u_y * ey[f] + u_z * ez[f]);
1285 // std::cout << "Cell <" << x << "," << y << "," << z;
1286 // std::cout << ">, DF " << f << ", delta " << delta << ", " << tmp1 << "/" << tmp2 << std::endl;
1287  (*grid1_)( x + ex[f], y + ey[f], z + ez[f], f ) = ( 1. / ( 1. + delta) ) * (
1288  delta * ( (*grid1_)( x + 2 * ex[f], y + 2 * ey[f], z + 2 * ez[f], f )
1289  + (*grid1_)( x, y, z, finv[f] ) )
1290  + (1. - delta) * (*grid1_)( x + ex[f], y + ey[f], z + ez[f], finv[f] )
1291  + w[f] * rho * ( u_x * ex[f] + u_y * ey[f] + u_z * ez[f]) );
1292  }
1293  }
1294  }
1295 
1296  sphereObstacles_[i].move();
1297  }
1298 }
1299 
1300 template<typename T>
1302  assert( prof_.fileName.length() );
1303 #ifdef NSMAGO
1304  std::cout << "Writing performance summary file '" << prof_.fileName << ".nosmago'" << std::endl;
1305  std::ofstream f( (prof_.fileName + ".nosmago").c_str(), std::ios::out );
1306 #else
1307  std::cout << "Writing performance summary file '" << prof_.fileName << ".smago'" << std::endl;
1308  std::ofstream f( (prof_.fileName + ".smago").c_str(), std::ios::out );
1309 #endif
1310 
1311  T boundTime = prof_.nTime + prof_.vTime + prof_.iTime + prof_.oTime + prof_.pTime + prof_.cTime + prof_.mTime;
1312  T totTime = boundTime + prof_.scTime;
1313 
1314  // Get effective domain size (without ghost layer)
1315  int sizeX = grid0_->getSizeX() - 2;
1316  int sizeY = grid0_->getSizeY() - 2;
1317  int sizeZ = grid0_->getSizeZ() - 2;
1318 
1319  int numCells = sizeX * sizeY * sizeZ;
1320 
1321  int nCells = noslipCells_.size();
1322  int vCells = velocityCells_.size();
1323  int iCells = inflowCells_.size();
1324  int oCells = outflowCells_.size();
1325  int pCells = pressureCells_.size();
1326  int cCells = curvedCells_.size();
1327  int sCells = staircaseCells_.size();
1328  int bCells = nCells + vCells + iCells + oCells + pCells + cCells + sCells;
1329  int fCells = numCells - bCells;
1330 
1331 // std::ostream &f = std::cout;
1332  f << "Domain size : " << sizeX << " x " << sizeY << " x " << sizeZ << "\n";
1333 // f << "Number of Cells : " << numCells << "\n";
1334  f << "Number of timesteps : " << maxSteps_ << "\n";
1335 #ifdef NSMAGO
1336  f << "Smagorinsky turbulence: no\n";
1337 #else
1338  f << "Smagorinsky turbulence: yes\n";
1339 #endif
1340 // f << "Fluid cells: " << fCells << "\n";
1341 // f << "No-slip boundary cells: " << nCells << "\n";
1342 // f << "Acceleration boundary cells: " << vCells << "\n";
1343 // f << "Inflow boundary cells: " << iCells << "\n";
1344 // f << "Outflow boundary cells: " << oCells << "\n";
1345 // f << "Pressure boundary cells: " << pCells << "\n";
1346 // f << "Curved boundary cells: " << cCells << "\n";
1347 // f.setf( std::ios_base::right );
1348  f << "Cell type |" << std::setw(13) << "Total" << std::setw(13) << "Fluid" << std::setw(13) << "Boundary Tot" << std::setw(13) << "No-slip" << std::setw(13) << "Acceleration" << std::setw(13) << "Inflow" << std::setw(13) << "Outflow" << std::setw(13) << "Pressure" << std::setw(13) << "Curved" << std::setw(13) << "Staircase" << std::setw(13) << "Moving\n";
1349  f << "Number of cells |" << std::setw(13) << numCells << std::setw(13) << fCells << std::setw(13) << bCells << std::setw(13) << nCells << std::setw(13) << vCells << std::setw(13) << iCells << std::setw(13) << oCells << std::setw(13) << pCells << std::setw(13) << cCells << std::setw(13) << sCells << "\n";
1350  f << "Runtime [s] |" << std::setw(13) << totTime << std::setw(13) << prof_.scTime << std::setw(13) << boundTime << std::setw(13) << prof_.nTime << std::setw(13) << prof_.vTime << std::setw(13) << prof_.iTime << std::setw(13) << prof_.oTime << std::setw(13) << prof_.pTime << std::setw(13) << prof_.cTime << std::setw(13) << prof_.sTime << std::setw(13) << prof_.mTime << "\n";
1351  f << "Runtime share |" << std::setw(13) << 1 << std::setw(13) << prof_.scTime / totTime << std::setw(13) << boundTime / totTime << std::setw(13) << prof_.nTime / totTime << std::setw(13) << prof_.vTime / totTime << std::setw(13) << prof_.iTime / totTime << std::setw(13) << prof_.oTime / totTime << std::setw(13) << prof_.pTime / totTime << std::setw(13) << prof_.cTime / totTime << std::setw(13) << prof_.sTime / totTime << std::setw(13) << prof_.mTime / totTime << "\n";
1352 // f << "Runtime share |" << prof_.scTime / totTime << ( nCells ? prof_.nTime / totTime : "n/a" ) << ( vCells ? prof_.vTime / totTime : "n/a" ) << ( iCells ? prof_.iTime / totTime : "n/a" ) << ( oCells ? prof_.oTime / totTime : "n/a" ) << ( pCells ? prof_.pTime / totTime : "n/a" ) << ( cCells ? prof_.cTime / totTime : "n/a" ) << ( prof_.mTime > 0 ? prof_.mTime / totTime : "n/a" ) << "\n";
1353  f << "MLUP/s |" << std::setw(13) << calcMLUP( totTime, numCells ) << std::setw(13) << calcMLUP( prof_.scTime, fCells ) << std::setw(13) << calcMLUP( boundTime, bCells ) << std::setw(13) << calcMLUP( prof_.nTime, nCells ) << std::setw(13) << calcMLUP( prof_.vTime, vCells ) << std::setw(13) << calcMLUP( prof_.iTime, iCells ) << std::setw(13) << calcMLUP( prof_.oTime, oCells ) << std::setw(13) << calcMLUP( prof_.pTime, pCells ) << std::setw(13) << calcMLUP( prof_.cTime, cCells ) << std::setw(13) << calcMLUP( prof_.sTime, sCells ) << "\n";
1354 }
1355 
1356 template<typename T>
1357 std::string LBM<T>::calcMLUP( T time, int cells ) {
1358  if ( cells == 0 ) {
1359  return std::string("n/a");
1360  }
1361  std::stringstream o;
1362  o << cells * maxSteps_ / ( time * 1000000 );
1363  return o.str();
1364 }
1365 
1366 template<>
1368 
1369  // Open file for writing
1370  std::ostringstream oss;
1371  oss << vtkFileName_ << "." << curStep_ << ".vtk";
1372  std::cout << "Writing file '" << oss.str() << "' for time step " << curStep_ << std::endl;
1373  std::ofstream vtkFile( oss.str().c_str(), std::ios::binary | std::ios::out );
1374 
1375  // Get size of domain without ghost layers
1376  int sizeX = grid0_->getSizeX() - 2;
1377  int sizeY = grid0_->getSizeY() - 2;
1378  int sizeZ = grid0_->getSizeZ() - 2;
1379 
1380  // Write file header
1381  vtkFile << "# vtk DataFile Version 2.0\n";
1382  vtkFile << "VTK output file for time step " << curStep_ << "\n\n";
1383  vtkFile << "BINARY\n\n";
1384  vtkFile << "DATASET STRUCTURED_POINTS\n";
1385  vtkFile << "DIMENSIONS " << sizeX << " " << sizeY << " " << sizeZ << "\n";
1386  vtkFile << "ORIGIN 0.0 0.0 0.0\n";
1387  vtkFile << "SPACING 1.0 1.0 1.0\n\n";
1388  vtkFile << "POINT_DATA " << sizeX * sizeY * sizeZ << "\n\n";
1389 
1390  // Write flag field
1391  vtkFile << "SCALARS flags int\n";
1392  vtkFile << "LOOKUP_TABLE default\n";
1393  for ( int z = 1; z <= sizeZ; ++z ) {
1394  for ( int y = 1; y <= sizeY; ++y ) {
1395  for ( int x = 1; x <= sizeX; ++x ) {
1396  // evil hack because vtk requires binary data to be in big endian
1397  uint32_t dump = htobe32( *reinterpret_cast<uint32_t *>( &flag_( x, y, z ) ) );
1398  vtkFile.write( reinterpret_cast<char *>( &dump ), sizeof(int) );
1399  }
1400  }
1401  }
1402 
1403  // Write density field
1404  vtkFile << "SCALARS density double\n";
1405  vtkFile << "LOOKUP_TABLE default\n";
1406  for ( int z = 1; z <= sizeZ; ++z ) {
1407  for ( int y = 1; y <= sizeY; ++y ) {
1408  for ( int x = 1; x <= sizeX; ++x ) {
1409  // evil hack because vtk requires binary data to be in big endian
1410  uint64_t dump = htobe64( *reinterpret_cast<uint64_t *>( &rho_( x, y, z ) ) );
1411  vtkFile.write( reinterpret_cast<char *>( &dump ), sizeof(double) );
1412  }
1413  }
1414  }
1415 
1416  // Write velocity vector field
1417  vtkFile << "VECTORS velocity double\n";
1418  for ( int z = 1; z <= sizeZ; ++z ) {
1419  for ( int y = 1; y <= sizeY; ++y ) {
1420  for ( int x = 1; x <= sizeX; ++x ) {
1421  // evil hack because vtk requires binary data to be in big endian
1422  uint64_t dump0 = htobe64( *reinterpret_cast<uint64_t *>( &u_( x, y, z, 0 ) ) );
1423  uint64_t dump1 = htobe64( *reinterpret_cast<uint64_t *>( &u_( x, y, z, 1 ) ) );
1424  uint64_t dump2 = htobe64( *reinterpret_cast<uint64_t *>( &u_( x, y, z, 2 ) ) );
1425  vtkFile.write( reinterpret_cast<char *>( &dump0 ), sizeof(double) );
1426  vtkFile.write( reinterpret_cast<char *>( &dump1 ), sizeof(double) );
1427  vtkFile.write( reinterpret_cast<char *>( &dump2 ), sizeof(double) );
1428  }
1429  }
1430  }
1431 
1432 }
1433 
1434 template<>
1436 
1437  // Open file for writing
1438  std::ostringstream oss;
1439  oss << vtkFileName_ << "." << curStep_ << ".vtk";
1440  std::cout << "Writing file '" << oss.str() << "' for time step " << curStep_ << std::endl;
1441  std::ofstream vtkFile( oss.str().c_str(), std::ios::binary | std::ios::out );
1442 
1443  // Get size of domain without ghost layers
1444  int sizeX = grid0_->getSizeX() - 2;
1445  int sizeY = grid0_->getSizeY() - 2;
1446  int sizeZ = grid0_->getSizeZ() - 2;
1447 
1448  // Write file header
1449  vtkFile << "# vtk DataFile Version 2.0\n";
1450  vtkFile << "VTK output file for time step " << curStep_ << "\n\n";
1451  vtkFile << "BINARY\n\n";
1452  vtkFile << "DATASET STRUCTURED_POINTS\n";
1453  vtkFile << "DIMENSIONS " << sizeX << " " << sizeY << " " << sizeZ << "\n";
1454  vtkFile << "ORIGIN 0.0 0.0 0.0\n";
1455  vtkFile << "SPACING 1.0 1.0 1.0\n\n";
1456  vtkFile << "POINT_DATA " << sizeX * sizeY * sizeZ << "\n\n";
1457 
1458  // Write flag field
1459  vtkFile << "SCALARS flags int\n";
1460  vtkFile << "LOOKUP_TABLE default\n";
1461  for ( int z = 1; z <= sizeZ; ++z ) {
1462  for ( int y = 1; y <= sizeY; ++y ) {
1463  for ( int x = 1; x <= sizeX; ++x ) {
1464  // evil hack because vtk requires binary data to be in big endian
1465  uint32_t dump = htobe32( *reinterpret_cast<uint32_t *>( &flag_( x, y, z ) ) );
1466  vtkFile.write( reinterpret_cast<char *>( &dump ), sizeof(int) );
1467  }
1468  }
1469  }
1470 
1471  // Write density field
1472  vtkFile << "SCALARS density float\n";
1473  vtkFile << "LOOKUP_TABLE default\n";
1474  for ( int z = 1; z <= sizeZ; ++z ) {
1475  for ( int y = 1; y <= sizeY; ++y ) {
1476  for ( int x = 1; x <= sizeX; ++x ) {
1477  // evil hack because vtk requires binary data to be in big endian
1478  uint32_t dump = htobe32( *reinterpret_cast<uint32_t *>( &rho_( x, y, z ) ) );
1479  vtkFile.write( reinterpret_cast<char *>( &dump ), sizeof(float) );
1480  }
1481  }
1482  }
1483 
1484  // Write velocity vector field
1485  vtkFile << "VECTORS velocity float\n";
1486  for ( int z = 1; z <= sizeZ; ++z ) {
1487  for ( int y = 1; y <= sizeY; ++y ) {
1488  for ( int x = 1; x <= sizeX; ++x ) {
1489  // evil hack because vtk requires binary data to be in big endian
1490  uint32_t dump0 = htobe32( *reinterpret_cast<uint32_t *>( &u_( x, y, z, 0 ) ) );
1491  uint32_t dump1 = htobe32( *reinterpret_cast<uint32_t *>( &u_( x, y, z, 1 ) ) );
1492  uint32_t dump2 = htobe32( *reinterpret_cast<uint32_t *>( &u_( x, y, z, 2 ) ) );
1493  vtkFile.write( reinterpret_cast<char *>( &dump0 ), sizeof(float) );
1494  vtkFile.write( reinterpret_cast<char *>( &dump1 ), sizeof(float) );
1495  vtkFile.write( reinterpret_cast<char *>( &dump2 ), sizeof(float) );
1496  }
1497  }
1498  }
1499 
1500 }
1501 
1502 } // namespace lbm
1503 
1504 #endif /* LBM_DEF_H_ */