Internal Energy Balance¶
The equation of the internal energy is
where \(A\) is the advective terms:
while \(D_i\) is the diffusive term involving spatial differentiation in the \(i\)-th direction:
See the spatial discretization.
The temporal discretization for each Runge-Kutta iteration leads to
when all diffusive terms are treated explicitly, while
when all diffusive terms are treated implicitly.
Diffusive terms are sometimes partially treated implicitly (c.f., van der Poel et al., Comput. Fluids (116), 2015). When only the diffusive term in \(x\) direction is implicitly treated (the default configuration in this project), we have
First, the explicit and implicit terms are calculated and stored to the corresponding buffers:
290int compute_rhs_t (
291 const domain_t * domain,
292 fluid_t * fluid
293) {
294 if (!laplacians.is_initialised) {
295 if (0 != init_laplacians(domain)) {
296 return 1;
297 }
298 }
299 const double * restrict ux = fluid->ux.data;
300 const double * restrict uy = fluid->uy.data;
301#if NDIMS == 3
302 const double * restrict uz = fluid->uz.data;
303#endif
304 const double * restrict t = fluid-> t.data;
305 // buffer for explicit terms
306 double * restrict srca = fluid->srct.alpha.data;
307 // buffer for implicit terms
308 double * restrict srcg = fluid->srct.gamma.data;
309 const double diffusivity = fluid_compute_temperature_diffusivity(fluid);
310 // advective contributions, always explicit
311 advection_x(domain, t, ux, srca);
312 advection_y(domain, t, uy, srca);
313#if NDIMS == 3
314 advection_z(domain, t, uz, srca);
315#endif
316 // diffusive contributions, can be explicit or implicit
317 diffusion_x(domain, diffusivity, t, param_t_implicit_x ? srcg : srca);
318 diffusion_y(domain, diffusivity, t, param_t_implicit_y ? srcg : srca);
319#if NDIMS == 3
320 diffusion_z(domain, diffusivity, t, param_t_implicit_z ? srcg : srca);
321#endif
322 return 0;
323}
The buffers are used to compute \(\Delta T\):
358 // Runge-Kutta coefficients, alpha, beta, gamma
359 const double coef_a = rkcoefs[rkstep].alpha;
360 const double coef_b = rkcoefs[rkstep].beta ;
361 const double coef_g = rkcoefs[rkstep].gamma;
362 // Runge-Kutta buffers, alpha, beta, gamma
363 const double * restrict srcta = fluid->srct.alpha.data;
364 const double * restrict srctb = fluid->srct.beta .data;
365 const double * restrict srctg = fluid->srct.gamma.data;
366 const int isize = domain->mysizes[0];
367 const int jsize = domain->mysizes[1];
368#if NDIMS == 3
369 const int ksize = domain->mysizes[2];
370#endif
371 double * restrict dtemp = linear_system.x1pncl;
372#if NDIMS == 2
373 const size_t nitems = isize * jsize;
374#else
375 const size_t nitems = isize * jsize * ksize;
376#endif
377 // compute T(new) - T(old)
378 for (size_t n = 0; n < nitems; n++) {
379 dtemp[n] =
380 + coef_a * dt * srcta[n]
381 + coef_b * dt * srctb[n]
382 + coef_g * dt * srctg[n];
383 }
When necessary, linear systems are solved to take care of the implicit treatments:
390if (param_t_implicit_x) {
391 // prepare tri-diagonal coefficients
392 tdm_info_t * tdm_info = linear_system.tdm_x;
393 int size = 0;
394 double * restrict tdm_l = NULL;
395 double * restrict tdm_c = NULL;
396 double * restrict tdm_u = NULL;
397 tdm.get_size(tdm_info, &size);
398 tdm.get_l(tdm_info, &tdm_l);
399 tdm.get_c(tdm_info, &tdm_c);
400 tdm.get_u(tdm_info, &tdm_u);
401 const laplacian_t * lapx = laplacians.lapx;
402 for (int i = 0; i < size; i++) {
403 tdm_l[i] = - prefactor * lapx[i].l;
404 tdm_c[i] = 1. - prefactor * lapx[i].c;
405 tdm_u[i] = - prefactor * lapx[i].u;
406 }
407 // solve all
408 tdm.solve(tdm_info, linear_system.x1pncl);
409}
411if (param_t_implicit_y) {
412 // make all y data accessible
413 sdecomp.transpose.execute(
414 linear_system.transposer_x1_to_y1,
415 linear_system.x1pncl,
416 linear_system.y1pncl
417 );
418 // prepare tri-diagonal coefficients
419 tdm_info_t * tdm_info = linear_system.tdm_y;
420 int size = 0;
421 double * restrict tdm_l = NULL;
422 double * restrict tdm_c = NULL;
423 double * restrict tdm_u = NULL;
424 tdm.get_size(tdm_info, &size);
425 tdm.get_l(tdm_info, &tdm_l);
426 tdm.get_c(tdm_info, &tdm_c);
427 tdm.get_u(tdm_info, &tdm_u);
428 const laplacian_t * lapy = &laplacians.lapy;
429 for (int j = 0; j < size; j++) {
430 tdm_l[j] = - prefactor * (*lapy).l;
431 tdm_c[j] = 1. - prefactor * (*lapy).c;
432 tdm_u[j] = - prefactor * (*lapy).u;
433 }
434 // solve all
435 tdm.solve(tdm_info, linear_system.y1pncl);
436 // recover original memory alignment
437 sdecomp.transpose.execute(
438 linear_system.transposer_y1_to_x1,
439 linear_system.y1pncl,
440 linear_system.x1pncl
441 );
442}
445if (param_t_implicit_z) {
446 // make all z data accessible
447 sdecomp.transpose.execute(
448 linear_system.transposer_x1_to_z2,
449 linear_system.x1pncl,
450 linear_system.z2pncl
451 );
452 // prepare tri-diagonal coefficients
453 tdm_info_t * tdm_info = linear_system.tdm_z;
454 int size = 0;
455 double * restrict tdm_l = NULL;
456 double * restrict tdm_c = NULL;
457 double * restrict tdm_u = NULL;
458 tdm.get_size(tdm_info, &size);
459 tdm.get_l(tdm_info, &tdm_l);
460 tdm.get_c(tdm_info, &tdm_c);
461 tdm.get_u(tdm_info, &tdm_u);
462 const laplacian_t * lapz = &laplacians.lapz;
463 for (int k = 0; k < size; k++) {
464 tdm_l[k] = - prefactor * (*lapz).l;
465 tdm_c[k] = 1. - prefactor * (*lapz).c;
466 tdm_u[k] = - prefactor * (*lapz).u;
467 }
468 // solve all
469 tdm.solve(tdm_info, linear_system.z2pncl);
470 // recover original memory alignment
471 sdecomp.transpose.execute(
472 linear_system.transposer_z2_to_x1,
473 linear_system.z2pncl,
474 linear_system.x1pncl
475 );
476}
Finally the temperature field is updated:
481 const int isize = domain->mysizes[0];
482 const int jsize = domain->mysizes[1];
483#if NDIMS == 3
484 const int ksize = domain->mysizes[2];
485#endif
486 const double * restrict dtemp = linear_system.x1pncl;
487 double * restrict t = fluid->t.data;
488 BEGIN
489#if NDIMS == 2
490 T(i, j) += dtemp[cnt];
491#else
492 T(i, j, k) += dtemp[cnt];
493#endif
494 END
495 if (0 != fluid_update_boundaries_t(domain, &fluid->t)) {
496 return 1;
497 }