@@ -141,6 +141,10 @@ def __init__(
141141 U01 = self .get_basis_change_matrix (p_in = 0 , p_out = 1 )
142142 U12 = self .get_basis_change_matrix (p_in = 1 , p_out = 2 )
143143 U02 = self .get_basis_change_matrix (p_in = 0 , p_out = 2 )
144+ self .eliminate_zeros (S1 )
145+ self .eliminate_zeros (S2 )
146+ self .eliminate_zeros (Dz )
147+ self .eliminate_zeros (Dzz )
144148
145149 self .Dx = Dx
146150 self .Dxx = Dxx
@@ -158,13 +162,12 @@ def __init__(
158162
159163 # construct operators
160164 _D = U02 @ (Dxx + Dyy ) + Dzz
161- L_lhs = {
162- 'p' : {'u' : U01 @ Dx , 'v' : U01 @ Dy , 'w' : Dz }, # divergence free constraint
163- 'u' : {'p' : U02 @ Dx , 'u' : - self .nu * _D },
164- 'v' : {'p' : U02 @ Dy , 'v' : - self .nu * _D },
165- 'w' : {'p' : U12 @ Dz , 'w' : - self .nu * _D , 'T' : - U02 @ Id },
166- 'T' : {'T' : - self .kappa * _D },
167- }
165+ L_lhs = {}
166+ L_lhs ['p' ] = {'u' : U01 @ Dx , 'v' : U01 @ Dy , 'w' : Dz } # divergence free constraint
167+ L_lhs ['u' ] = {'p' : U02 @ Dx , 'u' : - self .nu * _D }
168+ L_lhs ['v' ] = {'p' : U02 @ Dy , 'v' : - self .nu * _D }
169+ L_lhs ['w' ] = {'p' : U12 @ Dz , 'w' : - self .nu * _D , 'T' : - U02 @ Id }
170+ L_lhs ['T' ] = {'T' : - self .kappa * _D }
168171 self .setup_L (L_lhs )
169172
170173 # mass matrix
@@ -308,16 +311,30 @@ def compute_Nusselt_numbers(self, u):
308311 u: The solution you want to compute the Nusselt numbers of
309312
310313 Returns:
311- dict: Nusselt number averaged over the entire volume and horizontally averaged at the top and bottom.
314+ dict: Nusselt number averaged over the entire volume and horizontally averaged at the top and bottom as well
315+ as computed from thermal and kinetic dissipation.
312316 """
313- iw , iT = self .index (['w' , 'T' ])
317+ iu , iv , iw , iT = self .index (['u' , 'v' , 'w' , 'T' ])
314318 zAxis = self .spectral .axes [- 1 ]
315319
316320 if self .spectral_space :
317321 u_hat = u .copy ()
318322 else :
319323 u_hat = self .transform (u )
320324
325+ # evaluate derivatives in x, y, and z for u, v, w, and T
326+ derivatives = []
327+ derivative_indices = [iu , iv , iw , iT ]
328+ u_hat_flat = [u_hat [i ].flatten () for i in derivative_indices ]
329+ _D_u_hat = self .u_init_forward
330+ for D in [self .Dx , self .Dy , self .Dz ]:
331+ _D_u_hat *= 0
332+ for i in derivative_indices :
333+ self .xp .copyto (_D_u_hat [i ], (D @ u_hat_flat [i ]).reshape (_D_u_hat [i ].shape ))
334+ derivatives .append (
335+ self .itransform (_D_u_hat ).real
336+ ) # derivatives[0] contains x derivatives, [2] is y and [3] is z
337+
321338 DzT_hat = (self .Dz @ u_hat [iT ].flatten ()).reshape (u_hat [iT ].shape )
322339
323340 # compute wT with dealiasing
@@ -327,31 +344,56 @@ def compute_Nusselt_numbers(self, u):
327344 _me [0 ] = u_pad [iw ] * u_pad [iT ]
328345 wT_hat = self .transform (_me , padding = padding )[0 ]
329346
347+ # compute Nusselt number
330348 nusselt_hat = (wT_hat / self .kappa - DzT_hat ) * self .axes [- 1 ].L
331349
332- if not hasattr (self , '_zInt' ):
333- self ._zInt = zAxis .get_integration_matrix ()
350+ # compute thermal dissipation
351+ thermal_dissipation = self .u_init_physical
352+ thermal_dissipation [0 , ...] = (
353+ self .kappa * (derivatives [0 ][iT ].real + derivatives [1 ][iT ].real + derivatives [2 ][iT ].real ) ** 2
354+ )
355+ thermal_dissipation_hat = self .transform (thermal_dissipation )[0 ]
356+
357+ # compute kinetic energy dissipation
358+ kinetic_energy_dissipation = self .u_init_physical
359+ for i in [iu , iv , iw ]:
360+ kinetic_energy_dissipation [0 , ...] += (
361+ self .nu * (derivatives [0 ][i ].real + derivatives [1 ][i ].real + derivatives [2 ][i ].real ) ** 2
362+ )
363+ kinetic_energy_dissipation_hat = self .transform (kinetic_energy_dissipation )[0 ]
334364
335- # get coefficients for evaluation on the boundary
365+ # get coefficients for evaluation on the boundary and vertical integration
366+ zInt = zAxis .get_integration_weights ()
336367 top = zAxis .get_BC (kind = 'Dirichlet' , x = 1 )
337368 bot = zAxis .get_BC (kind = 'Dirichlet' , x = - 1 )
338369
370+ # compute volume averages
339371 integral_V = 0
372+ integral_V_th = 0
373+ integral_V_kin = 0
340374 if self .comm .rank == 0 :
341375
342- integral_z = (self ._zInt @ nusselt_hat [0 , 0 ]).real
343- integral_z [0 ] = zAxis .get_integration_constant (integral_z , axis = - 1 )
344- integral_V = ((top - bot ) * integral_z ).sum () * self .axes [0 ].L * self .axes [1 ].L / self .nx / self .ny
376+ integral_V = (zInt @ nusselt_hat [0 , 0 ]).real * self .axes [0 ].L * self .axes [1 ].L / self .nx / self .ny
377+ integral_V_th = (
378+ (zInt @ thermal_dissipation_hat [0 , 0 ]).real * self .axes [0 ].L * self .axes [1 ].L / self .nx / self .ny
379+ )
380+ integral_V_kin = (
381+ (zInt @ kinetic_energy_dissipation_hat [0 , 0 ]).real * self .axes [0 ].L * self .axes [1 ].L / self .nx / self .ny
382+ )
345383
384+ # communicate result across all tasks
346385 Nusselt_V = self .comm .bcast (integral_V / self .spectral .V , root = 0 )
347-
348386 Nusselt_t = self .comm .bcast (self .xp .sum (nusselt_hat .real [0 , 0 , :] * top , axis = - 1 ) / self .nx / self .ny , root = 0 )
349387 Nusselt_b = self .comm .bcast (self .xp .sum (nusselt_hat .real [0 , 0 ] * bot / self .nx / self .ny , axis = - 1 ), root = 0 )
388+ Nusselt_thermal = self .comm .bcast (1 / self .kappa * integral_V_th / self .spectral .V , root = 0 )
389+ Nusselt_kinetic = self .comm .bcast (1 + 1 / self .kappa * integral_V_kin / self .spectral .V , root = 0 )
350390
351391 return {
352392 'V' : Nusselt_V ,
353393 't' : Nusselt_t ,
354394 'b' : Nusselt_b ,
395+ 'thermal' : Nusselt_thermal ,
396+ 'kinetic' : Nusselt_kinetic ,
355397 }
356398
357399 def get_frequency_spectrum (self , u ):
0 commit comments