cuIBM
A GPU-based Immersed Boundary Method code
NavierStokesSolver.cu
Go to the documentation of this file.
1 
7 #include <sys/stat.h>
8 
9 #include "NavierStokesSolver.h"
10 #include "io/io.h"
11 
12 
13 //##############################################################################
14 // INITIALISE
15 //##############################################################################
16 
23 template <typename memoryType>
25 {
26  paramDB = pDB;
27  domInfo = dInfo;
28 } // NavierStokesSolver
29 
30 
34 template <typename memoryType>
36 {
37  printf("Initializing Navier-Stokes solver ...\n");
38 
39  int nx = domInfo->nx,
40  ny = domInfo->ny;
41 
42  int numUV = (nx-1)*ny + nx*(ny-1),
43  numP = nx*ny;
44 
45  initialiseCommon();
46  initialiseArrays(numUV, numP);
47  assembleMatrices();
48 } // initialise
49 
50 
54 template <typename memoryType>
56 {
57  printf("Initializing common parts ...\n");
58  logger.startTimer("initialiseCommon");
59 
60  QCoeff = 1.0;
61  subStep = 0;
62 
63  timeScheme convScheme = (*paramDB)["simulation"]["convTimeScheme"].get<timeScheme>(),
64  diffScheme = (*paramDB)["simulation"]["diffTimeScheme"].get<timeScheme>();
65  intgSchm.initialise(convScheme, diffScheme);
66 
67  // set initial timeStep
68  timeStep = (*paramDB)["simulation"]["startStep"].get<int>();
69  // get folder path
70  std::string folder = (*paramDB)["inputs"]["caseFolder"].get<std::string>();
71 
72  // writes the grids information to a file
73  io::writeGrid(folder, *domInfo);
74 
75  // opens the file to which the number of iterations at every step is written
76  std::stringstream out;
77  out << folder << "/iterations";
78  if (timeStep == 0)
79  {
80  iterationsFile.open(out.str().c_str());
81  }
82  else
83  {
84  iterationsFile.open(out.str().c_str(), std::ofstream::app);
85  }
86 
87  logger.stopTimer("initialiseCommon");
88 } // initialiseCommon
89 
90 
97 template <typename memoryType>
99 {
100  printf("Initializing arrays ...\n");
101  logger.startTimer("initialiseArrays");
102 
103  q.resize(numQ);
104  qStar.resize(numQ);
105  qOld.resize(numQ);
106  rn.resize(numQ);
107  H.resize(numQ);
108  bc1.resize(numQ);
109  rhs1.resize(numQ);
110  temp1.resize(numQ);
111 
112  cusp::blas::fill(rn, 0.0);
113  cusp::blas::fill(H, 0.0);
114  cusp::blas::fill(bc1, 0.0);
115  cusp::blas::fill(rhs1, 0.0);
116  cusp::blas::fill(temp1, 0.0);
117 
118  lambda.resize(numLambda);
119  bc2.resize(numLambda);
120  rhs2.resize(numLambda);
121  temp2.resize(numLambda);
122 
123  cusp::blas::fill(lambda, 0.0);
124  cusp::blas::fill(bc2, 0.0);
125  cusp::blas::fill(rhs2, 0.0);
126  cusp::blas::fill(temp2, 0.0);
127 
128  initialiseFluxes();
129  initialiseBoundaryArrays();
130 
131  generateRN();
132  cusp::blas::scal(H, 1.0/intgSchm.gamma[subStep]);
133 
134  logger.stopTimer("initialiseArrays");
135 } // initialiseArrays
136 
137 
144 template<>
146 {
147  int nx = domInfo->nx,
148  ny = domInfo->ny;
149  vecH qHost((nx-1)*ny+nx*(ny-1));
150 
151  // creating raw pointers
152  real *qHost_r = thrust::raw_pointer_cast(&(qHost[0]));
153  initialiseFluxes(qHost_r);
154  q = qHost;
155  qStar=q;
156 } // initialiseFluxes
157 
158 
164 template <typename memoryType>
166 {
167  if (timeStep != 0)
168  {
169  // case directory
170  std::string caseFolder = (*paramDB)["inputs"]["caseFolder"].get<std::string>();
171  // read velocity fluxes from file
172  io::readData(caseFolder, timeStep, q, "q");
173  return;
174  }
175 
176  int nx = domInfo->nx,
177  ny = domInfo->ny,
178  numU = (nx-1)*ny;
179 
180  real xmin = domInfo->x[0],
181  xmax = domInfo->x[nx],
182  ymin = domInfo->y[0],
183  ymax = domInfo->y[ny];
184 
185  real uInitial = (*paramDB)["flow"]["uInitial"].get<real>(),
186  uPerturb = (*paramDB)["flow"]["uPerturb"].get<real>(),
187  vInitial = (*paramDB)["flow"]["vInitial"].get<real>(),
188  vPerturb = (*paramDB)["flow"]["vPerturb"].get<real>();
189 
190  for(int j=0; j<ny; j++)
191  {
192  for(int i=0; i<nx-1; i++)
193  {
194  q[j*(nx-1) + i] = ( uInitial + uPerturb * cos( 0.5*M_PI*(2*domInfo->xu[i]-xmax-xmin)/(xmax-xmin) ) * sin( M_PI * (2*domInfo->yu[j]-ymax-ymin)/(ymax-ymin) ) ) * domInfo->dy[j];
195  }
196  }
197  for(int j=0; j<ny-1; j++)
198  {
199  for(int i=0; i<nx; i++)
200  {
201  q[j*nx + i + numU] = ( vInitial + vPerturb * cos( 0.5*M_PI*(2*domInfo->yv[j]-ymax-ymin)/(ymax-ymin) ) * sin( M_PI * (2*domInfo->xv[i]-xmax-xmin)/(xmax-xmin) ) ) * domInfo->dx[i];
202  }
203  }
204 } // initialiseFluxes
205 
206 
210 template <typename memoryType>
212 {
213  int nx = domInfo->nx,
214  ny = domInfo->ny;
215 
216  boundaryCondition **bcInfo = (*paramDB)["flow"]["boundaryConditions"].get<boundaryCondition **>();
217 
218  // resize boundary arrays by the number of velocity points on boundaries (u and v points)
219  bc[XMINUS].resize(2*ny-1);
220  bc[XPLUS].resize(2*ny-1);
221  bc[YMINUS].resize(2*nx-1);
222  bc[YPLUS].resize(2*nx-1);
223 
225  for(int i=0; i<nx-1; i++)
226  {
227  bc[YMINUS][i] = bcInfo[YMINUS][0].value;
228  bc[YPLUS][i] = bcInfo[YPLUS][0].value;
229  bc[YMINUS][i+nx-1] = bcInfo[YMINUS][1].value;
230  bc[YPLUS][i+nx-1] = bcInfo[YPLUS][1].value;
231  }
232  bc[YMINUS][2*nx-2] = bcInfo[YMINUS][1].value;
233  bc[YPLUS][2*nx-2] = bcInfo[YPLUS][1].value;
234 
236  for(int i=0; i<ny-1; i++)
237  {
238  bc[XMINUS][i] = bcInfo[XMINUS][0].value;
239  bc[XPLUS][i] = bcInfo[XPLUS][0].value;
240  bc[XMINUS][i+ny] = bcInfo[XMINUS][1].value;
241  bc[XPLUS][i+ny] = bcInfo[XPLUS][1].value;
242  }
243  bc[XMINUS][ny-1] = bcInfo[XMINUS][0].value;
244  bc[XPLUS][ny-1] = bcInfo[XPLUS][0].value;
245 } // initialiseBoundaryArrays
246 
247 
248 //##############################################################################
249 // TIME STEPPING
250 //##############################################################################
251 
255 template <typename memoryType>
257 {
258  qOld = q;
259  for(subStep=0; subStep < intgSchm.subSteps; subStep++)
260  {
261  updateSolverState();
262 
263  // Set up and solve the first system for the intermediate velocity
264  generateRN();
265  generateBC1();
266  assembleRHS1();
267  solveIntermediateVelocity();
268 
269  // Set up and solve the Poisson system
270  generateBC2();
271  assembleRHS2();
272  solvePoisson();
273 
274  // Projection step
275  projectionStep();
276  }
277 
278  timeStep++;
279 } // stepTime
280 
281 
285 template <typename memoryType>
287 {
288 // generateA(intgSchm.alphaImplicit[subStep]);
289 // updateQ(intgSchm.gamma[i]);
290 // updateBoundaryConditions();
291 } // updateSolverState
292 
293 
299 template <typename memoryType>
301 {
302 // cusp::blas::scal(Q.values, gamma/QCoeff);
303 // QCoeff = gamma;
304 } // updateQ
305 
306 
310 template <typename memoryType>
312 {
313 } // updateBoundaryConditions
314 
315 
321 template <typename memoryType>
323 {
324  int startStep = (*paramDB)["simulation"]["startStep"].get<int>();
325  int nt = (*paramDB)["simulation"]["nt"].get<int>();
326  return (timeStep < startStep+nt) ? false : true;
327 } // finished
328 
329 
330 //##############################################################################
331 // ASSEMBLE MATRICES
332 //##############################################################################
333 
337 template <typename memoryType>
339 {
340  printf("Initializing matrices ...\n");
341  logger.startTimer("assembleMatrices");
342 
343  generateM();
344  generateL();
345  generateA(intgSchm.alphaImplicit[subStep]);
346  PC1 = new preconditioner< cusp::coo_matrix<int, real, memoryType> >(A, (*paramDB)["velocitySolve"]["preconditioner"].get<preconditionerType>());
347  generateBN();
348 
349  logger.stopTimer("assembleMatrices");
350 
351  generateQT();
352  generateC(); // QT*BN*Q
353 
354  logger.startTimer("preconditioner2");
355  PC2 = new preconditioner< cusp::coo_matrix<int, real, memoryType> >(C, (*paramDB)["PoissonSolve"]["preconditioner"].get<preconditionerType>());
356  logger.stopTimer("preconditioner2");
357 } // assembleMatrices
358 
359 
367 template <typename memoryType>
369 {
370  BN = Minv; // 1st-order
371 } // generateBN
372 
373 
374 /*
375 template <typename memoryType>
376 template <>
377 void NavierStokesSolver<memoryType>::generateBN<3>()
378 {
379  Matrix temp1, temp2;
380  cusp::multiply(Minv, L, temp1);
381  cusp::multiply(temp1, Minv, BN);
382  cusp::add(Minv, BN, BN);
383  cusp::multiply(temp1, BN, temp2);
384  cusp::add(Minv, temp2, BN);
385 }*/
386 
387 
391 template <>
393 {
394  logger.startTimer("generateC");
395 
396  cooD temp; // Should this temp matrix be created each time step?
397  cusp::multiply(QT, BN, temp);
398  cusp::multiply(temp, Q, C);
399  C.values[0] += C.values[0];
400 
401  logger.stopTimer("generateC");
402 } // generateC
403 
404 
405 //##############################################################################
406 // GENERATE VECTORS
407 //##############################################################################
408 
412 template <typename memoryType>
414 {
415  logger.startTimer("assembleRHS1");
416 
417  cusp::blas::axpby(rn, bc1, rhs1, 1.0, 1.0);
418 
419  logger.stopTimer("assembleRHS1");
420 } // assembleRHS1
421 
422 
426 template <typename memoryType>
428 {
429  logger.startTimer("assembleRHS2");
430 
431  cusp::multiply(QT, qStar, temp2);
432  cusp::blas::axpby(temp2, bc2, rhs2, 1.0, -1.0);
433 
434  logger.stopTimer("assembleRHS2");
435 } // assembleRHS2
436 
437 
438 //##############################################################################
439 // LINEAR SOLVES
440 //##############################################################################
441 
445 template <typename memoryType>
447 {
448  logger.startTimer("solveIntermediateVel");
449 
450  std::string krylov = (*paramDB)["velocitySolve"]["solver"].get<std::string>();
451  int maxIte = (*paramDB)["velocitySolve"]["maxIterations"].get<int>();
452  real rTol = (*paramDB)["velocitySolve"]["rTol"].get<real>();
453  real aTol = (*paramDB)["velocitySolve"]["aTol"].get<real>();
454  bool monitor = (*paramDB)["velocitySolve"]["monitor"].get<bool>();
455 
456  cusp::monitor<real> sys1Mon(rhs1, maxIte, rTol, aTol, monitor);
457 
458  if (krylov == "CG")
459  cusp::krylov::cg(A, qStar, rhs1, sys1Mon, *PC1);
460  else if (krylov == "BICGSTAB")
461  cusp::krylov::bicgstab(A, qStar, rhs1, sys1Mon, *PC1);
462  else if (krylov == "GMRES")
463  {
464  int restart = (*paramDB)["velocitySolve"]["restart"].get<int>();
465  cusp::krylov::gmres(A, qStar, rhs1, restart, sys1Mon, *PC1);
466  }
467  else
468  {
469  printf("Error: Unknown Krylov solver '%s' for velocity system!\n", krylov.c_str());
470  exit(-1);
471  }
472 
473  iterationCount1 = sys1Mon.iteration_count();
474  if (!sys1Mon.converged())
475  {
476  std::cout << "Error: Solve for q* failed at time step " << timeStep << std::endl;
477  std::cout << "Iterations : " << iterationCount1 << std::endl;
478  std::cout << "Residual norm: " << sys1Mon.residual_norm() << std::endl;
479  std::cout << "Tolerance : " << sys1Mon.tolerance() << std::endl;
480  exit(-1);
481  }
482 
483  logger.stopTimer("solveIntermediateVel");
484 } // solveIntermediateVelocity
485 
486 
490 template <typename memoryType>
492 {
493  logger.startTimer("solvePoisson");
494 
495  std::string krylov = (*paramDB)["PoissonSolve"]["solver"].get<std::string>();
496  int maxIte = (*paramDB)["PoissonSolve"]["maxIterations"].get<int>();
497  real rTol = (*paramDB)["PoissonSolve"]["rTol"].get<real>();
498  real aTol = (*paramDB)["PoissonSolve"]["aTol"].get<real>();
499  bool monitor = (*paramDB)["PoissonSolve"]["monitor"].get<bool>();
500 
501  cusp::monitor<real> sys2Mon(rhs2, maxIte, rTol, aTol, monitor);
502 
503  if (krylov == "CG")
504  cusp::krylov::cg(C, lambda, rhs2, sys2Mon, *PC2);
505  else if (krylov == "BICGSTAB")
506  cusp::krylov::bicgstab(C, lambda, rhs2, sys2Mon, *PC2);
507  else if (krylov == "GMRES")
508  {
509  int restart = (*paramDB)["PoissonSolve"]["restart"].get<int>();
510  cusp::krylov::gmres(C, lambda, rhs2, restart, sys2Mon, *PC2);
511  }
512  else
513  {
514  printf("Error: Unknown Krylov solver '%s' for Poisson system!\n", krylov.c_str());
515  exit(-1);
516  }
517 
518  iterationCount2 = sys2Mon.iteration_count();
519  if (!sys2Mon.converged())
520  {
521  std::cout << "Error: Solve for Lambda failed at time step " << timeStep << std::endl;
522  std::cout << "Iterations : " << iterationCount2 << std::endl;
523  std::cout << "Residual norm: " << sys2Mon.residual_norm() << std::endl;
524  std::cout << "Tolerance : " << sys2Mon.tolerance() << std::endl;
525  exit(-1);
526  }
527 
528  logger.stopTimer("solvePoisson");
529 } // solvePoisson
530 
531 
535 template <typename memoryType>
537 {
538  logger.startTimer("projectionStep");
539 
540  cusp::multiply(Q, lambda, temp1);
541  cusp::multiply(BN, temp1, q);
542  cusp::blas::axpby(qStar, q, q, 1.0, -1.0);
543 
544  logger.stopTimer("projectionStep");
545 } // projectionStep
546 
547 
548 //##############################################################################
549 // OUTPUT
550 //##############################################################################
551 
556 template <typename memoryType>
558 {
559  int nsave = (*paramDB)["simulation"]["nsave"].get<int>();
560  std::string folder = (*paramDB)["inputs"]["caseFolder"].get<std::string>();
561 
562  // write the velocity fluxes and the pressure values
563  if (timeStep % nsave == 0)
564  {
565  io::writeData(folder, timeStep, q, lambda, *domInfo);
566  }
567 
568  // write the number of iterations for each solve
569  iterationsFile << timeStep << '\t' << iterationCount1 << '\t' << iterationCount2 << std::endl;
570 } // writeCommon
571 
572 
576 template <typename memoryType>
578 {
579  logger.startTimer("output");
580 
581  writeCommon();
582 
583  logger.stopTimer("output");
584 } // writeData
585 
586 
590 template <typename memoryType>
592 {
593  io::printTimingInfo(logger);
594  iterationsFile.close();
595 } // shutDown
596 
597 
598 // include inline files
606 
607 
608 // specialization of the class NavierStokesSolver
609 template class NavierStokesSolver<device_memory>;
bool finished()
Evaluates the condition required to stop the simulation.
NavierStokesSolver(parameterDB *pDB=NULL, domain *dInfo=NULL)
Constructor. Copies the database and information about the computational grid.
Implementation of the methods to generate inhomogeneous boundary conditions from the discrete Laplaci...
double real
Is a float or a double depending on the machine precision.
Definition: types.h:116
void stepTime()
Calculates the variables at the next time step.
void readData(std::string &caseFolder, int timeStep, real *x, std::string name)
Reads numerical data at a given time-step.
Definition: io.cu:571
Stores the preconditioner for a given system.
right boundary
Definition: types.h:50
void initialiseCommon()
Initializes parameters common to all Navier-Stokes solvers.
void generateBN()
Generates approximate inverse of the matrix resulting from implicit velocity terms.
bottom boundary
Definition: types.h:51
Declaration of the functions of the namespace io.
timeScheme
Specifies the numerical scheme used for time-integration.
Definition: types.h:60
Implementation of the methods to generate the mass matrix and its inverse.
preconditionerType
Specifies the type of preconditioner.
Definition: types.h:104
std::map< std::string, componentParameter > parameterDB
Map from a string to a componentParameter.
Definition: parameterDB.h:64
void printTimingInfo(Logger &logger)
Prints the time spent to execute tasks.
Definition: io.cu:469
cusp::array1d< real, host_memory > vecH
Cusp 1D array stored in the host.
Definition: types.h:154
virtual void shutDown()
Prints timing information and closes the different files.
Stores information about the computational grid.
Definition: domain.h:16
void initialiseArrays(int numQ, int numLambda)
Initializes all arrays required to solve the Navier-Stokes equations.
left boundary
Definition: types.h:49
Implementation of the methods to generate the explicit terms of the momentum equation.
virtual void solvePoisson()
Solves the Poisson system.
__global__ void generateA(int *ARows, int *ACols, real *AVals, real *MVals, int *LRows, int *LCols, real *LVals, int ASize, real alpha)
Generates a block of the matrix resulting from implicit terms in the momentum equation.
Definition: generateA.cu:38
virtual void writeCommon()
Writes numerical solution at current time-step, as well as the number of iterations performed in each...
virtual void generateC()
void writeGrid(std::string &caseFolder, domain &D)
Writes grid-points coordinates into the file grid.
Definition: io.cu:482
cusp::coo_matrix< int, real, device_memory > cooD
COO matrix stored on the device.
Definition: types.h:133
virtual void writeData()
Writes data into files.
void initialiseBoundaryArrays()
Initializes boundary velocity arrays with values stored in the database.
top boundary
Definition: types.h:52
virtual void solveIntermediateVelocity()
Solves for the intermediate flux velocity.
void updateBoundaryConditions()
Doing nothing.
virtual void projectionStep()
Projects the flux onto the divergence-free field.
void assembleMatrices()
Assembles matrices of the intermediate flux solver and the Poisson solver.
virtual void assembleRHS1()
Assembles the right hand-side of the system for the intermediate flux.
void generateQT(int *QTRows, int *QTCols, real *QTVals, int nx, int ny)
Generates the divergence matrix (on the host).
Definition: generateQT.cu:110
void updateQ(real gamma)
Doing nothing.
Solves the Navier-Stokes equations in a rectangular domain.
virtual void updateSolverState()
Doing nothing. Used in immersed boundary methods when the body moves.
void assembleRHS2()
Assembles the right hand-side of the Poisson system.
Stores the type of boundary condition and its value.
virtual void initialise()
Initializes parameters, arrays and matrices required for the simulation.
real value
numerical value associated with the boundary condition
void writeData(std::string &caseFolder, int n, Vector &q, Vector &lambda, domain &D)