@@ -58,7 +58,9 @@ def __init__(
5858 BCs = None ,
5959 dealiasing = 3 / 2 ,
6060 comm = None ,
61- Lx = 8 ,
61+ Lx = 4 ,
62+ Lz = 1 ,
63+ z0 = 0 ,
6264 ** kwargs ,
6365 ):
6466 """
@@ -73,11 +75,13 @@ def __init__(
7375 dealiasing (float): Dealiasing for evaluating the non-linear part in real space
7476 comm (mpi4py.Intracomm): Space communicator
7577 Lx (float): Horizontal length of the domain
78+ Lz (float): Vertical length of the domain
79+ z0 (float): Position of lower boundary
7680 """
7781 BCs = {} if BCs is None else BCs
7882 BCs = {
7983 'T_top' : 0 ,
80- 'T_bottom' : 2 ,
84+ 'T_bottom' : 1 ,
8185 'v_top' : 0 ,
8286 'v_bottom' : 0 ,
8387 'u_top' : 0 ,
@@ -101,11 +105,16 @@ def __init__(
101105 'dealiasing' ,
102106 'comm' ,
103107 'Lx' ,
108+ 'Lz' ,
109+ 'z0' ,
104110 localVars = locals (),
105111 readOnly = True ,
106112 )
107113
108- bases = [{'base' : 'fft' , 'N' : nx , 'x0' : 0 , 'x1' : self .Lx }, {'base' : 'ultraspherical' , 'N' : nz }]
114+ bases = [
115+ {'base' : 'fft' , 'N' : nx , 'x0' : 0 , 'x1' : self .Lx },
116+ {'base' : 'ultraspherical' , 'N' : nz , 'x0' : self .z0 , 'x1' : self .Lz },
117+ ]
109118 components = ['u' , 'v' , 'T' , 'p' ]
110119 super ().__init__ (bases , components , comm = comm , ** kwargs )
111120
@@ -247,8 +256,8 @@ def u_exact(self, t=0, noise_level=1e-3, seed=99):
247256
248257 # linear temperature gradient
249258 for comp in ['T' , 'v' , 'u' ]:
250- a = (self .BCs [f'{ comp } _top' ] - self .BCs [f'{ comp } _bottom' ]) / 2
251- b = ( self .BCs [f'{ comp } _top ' ] + self . BCs [ f' { comp } _bottom' ]) / 2
259+ a = (self .BCs [f'{ comp } _top' ] - self .BCs [f'{ comp } _bottom' ]) / self . Lz
260+ b = self .BCs [f'{ comp } _bottom ' ] - a * self . z0
252261 me [self .index (comp )] = a * self .Z + b
253262
254263 # perturb slightly
@@ -257,7 +266,7 @@ def u_exact(self, t=0, noise_level=1e-3, seed=99):
257266 noise = self .spectral .u_init
258267 noise [iT ] = rng .random (size = me [iT ].shape )
259268
260- me [iT ] += noise [iT ].real * noise_level * (self .Z - 1 ) * (self .Z + 1 )
269+ me [iT ] += noise [iT ].real * noise_level * (self .Z - self . z0 ) * (self .Z - self . z0 + self . Lz )
261270
262271 if self .spectral_space :
263272 me_hat = self .spectral .u_init_forward
@@ -370,14 +379,41 @@ def compute_vorticity(self, u):
370379 u_hat = u .copy ()
371380 else :
372381 u_hat = self .transform (u )
382+
373383 Dz = self .Dz
374384 Dx = self .Dx
375385 iu , iv = self .index (['u' , 'v' ])
376386
377387 vorticity_hat = self .spectral .u_init_forward
378- vorticity_hat [0 ] = (Dx * u_hat [iv ].flatten () + Dz @ u_hat [iu ].flatten ()).reshape (u [iu ].shape )
388+ vorticity_hat [0 ] = (Dx * u_hat [iv ].flatten () + Dz @ u_hat [iu ].flatten ()).reshape (u_hat [iu ].shape )
379389 return self .itransform (vorticity_hat )[0 ].real
380390
391+ def getOutputFile (self , fileName ):
392+ from pySDC .helpers .fieldsIO import Rectilinear
393+
394+ self .setUpFieldsIO ()
395+
396+ coords = [me .get_1dgrid () for me in self .spectral .axes ]
397+ assert np .allclose ([len (me ) for me in coords ], self .spectral .global_shape [1 :])
398+
399+ fOut = Rectilinear (np .float64 , fileName = fileName )
400+ fOut .setHeader (nVar = len (self .components ) + 1 , coords = coords )
401+ fOut .initialize ()
402+ return fOut
403+
404+ def processSolutionForOutput (self , u ):
405+ vorticity = self .compute_vorticity (u )
406+
407+ if self .spectral_space :
408+ u_real = self .itransform (u ).real
409+ else :
410+ u_real = u .real
411+
412+ me = np .empty (shape = (u_real .shape [0 ] + 1 , * vorticity .shape ))
413+ me [:- 1 ] = u_real
414+ me [- 1 ] = vorticity
415+ return me
416+
381417 def compute_Nusselt_numbers (self , u ):
382418 """
383419 Compute the various versions of the Nusselt number. This reflects the type of heat transport.
@@ -392,52 +428,46 @@ def compute_Nusselt_numbers(self, u):
392428 dict: Nusselt number averaged over the entire volume and horizontally averaged at the top and bottom.
393429 """
394430 iv , iT = self .index (['v' , 'T' ])
395-
396- DzT_hat = self .spectral .u_init_forward
431+ zAxis = self .spectral .axes [- 1 ]
397432
398433 if self .spectral_space :
399434 u_hat = u .copy ()
400435 else :
401436 u_hat = self .transform (u )
402437
403- DzT_hat [ iT ] = (self .Dz @ u_hat [iT ].flatten ()).reshape (DzT_hat [iT ].shape )
438+ DzT_hat = (self .Dz @ u_hat [iT ].flatten ()).reshape (u_hat [iT ].shape )
404439
405440 # compute vT with dealiasing
406441 padding = (self .dealiasing , self .dealiasing )
407442 u_pad = self .itransform (u_hat , padding = padding ).real
408443 _me = self .xp .zeros_like (u_pad )
409444 _me [0 ] = u_pad [iv ] * u_pad [iT ]
410- vT_hat = self .transform (_me , padding = padding )
445+ vT_hat = self .transform (_me , padding = padding )[ 0 ]
411446
412- nusselt_hat = ( vT_hat [ 0 ] - DzT_hat [ iT ]) / self . nx
413- nusselt_no_v_hat = ( - DzT_hat [ iT ]) / self .nx
447+ if not hasattr ( self , '_zInt' ):
448+ self ._zInt = zAxis . get_integration_matrix ()
414449
415- integral_z = self .xp .sum (nusselt_hat * self .spectral .axes [1 ].get_BC (kind = 'integral' ), axis = - 1 ).real
416- integral_V = (
417- integral_z [0 ] * self .axes [0 ].L
418- ) # only the first Fourier mode has non-zero integral with periodic BCs
419- Nusselt_V = self .comm .bcast (integral_V / self .spectral .V , root = 0 )
450+ nusselt_hat = (vT_hat / self .kappa - DzT_hat ) * self .axes [- 1 ].L
420451
421- Nusselt_t = self .comm .bcast (
422- self .xp .sum (nusselt_hat * self .spectral .axes [1 ].get_BC (kind = 'Dirichlet' , x = 1 ), axis = - 1 ).real [0 ], root = 0
423- )
424- Nusselt_b = self .comm .bcast (
425- self .xp .sum (nusselt_hat * self .spectral .axes [1 ].get_BC (kind = 'Dirichlet' , x = - 1 ), axis = - 1 ).real [0 ], root = 0
426- )
427- Nusselt_no_v_t = self .comm .bcast (
428- self .xp .sum (nusselt_no_v_hat * self .spectral .axes [1 ].get_BC (kind = 'Dirichlet' , x = 1 ), axis = - 1 ).real [0 ], root = 0
429- )
430- Nusselt_no_v_b = self .comm .bcast (
431- self .xp .sum (nusselt_no_v_hat * self .spectral .axes [1 ].get_BC (kind = 'Dirichlet' , x = - 1 ), axis = - 1 ).real [0 ],
432- root = 0 ,
433- )
452+ # get coefficients for evaluation on the boundary
453+ top = zAxis .get_BC (kind = 'Dirichlet' , x = 1 )
454+ bot = zAxis .get_BC (kind = 'Dirichlet' , x = - 1 )
455+
456+ integral_V = 0
457+ if self .comm .rank == 0 :
458+
459+ integral_z = (self ._zInt @ nusselt_hat [0 ]).real
460+ integral_z [0 ] = zAxis .get_integration_constant (integral_z , axis = - 1 )
461+ integral_V = ((top - bot ) * integral_z ).sum () * self .axes [0 ].L / self .nx
462+
463+ Nusselt_V = self .comm .bcast (integral_V / self .spectral .V , root = 0 )
464+ Nusselt_t = self .comm .bcast (self .xp .sum (nusselt_hat .real [0 ] * top , axis = - 1 ) / self .nx , root = 0 )
465+ Nusselt_b = self .comm .bcast (self .xp .sum (nusselt_hat .real [0 ] * bot , axis = - 1 ) / self .nx , root = 0 )
434466
435467 return {
436468 'V' : Nusselt_V ,
437469 't' : Nusselt_t ,
438470 'b' : Nusselt_b ,
439- 't_no_v' : Nusselt_no_v_t ,
440- 'b_no_v' : Nusselt_no_v_b ,
441471 }
442472
443473 def compute_viscous_dissipation (self , u ):
@@ -509,8 +539,8 @@ def compute_max_step_size(P, u):
509539 grid_spacing_x = P .X [1 , 0 ] - P .X [0 , 0 ]
510540
511541 cell_wallz = P .xp .zeros (P .nz + 1 )
512- cell_wallz [0 ] = 1
513- cell_wallz [- 1 ] = - 1
542+ cell_wallz [0 ] = P . Lz
543+ cell_wallz [- 1 ] = 0
514544 cell_wallz [1 :- 1 ] = (P .Z [0 , :- 1 ] + P .Z [0 , 1 :]) / 2
515545 grid_spacing_z = cell_wallz [:- 1 ] - cell_wallz [1 :]
516546
0 commit comments