def finalize(self, mskp_model): print('found %i islands' % self.nbisland) mskp = zeros((self.nyl, self.nxl), dtype=int8) work = zeros((self.nyl, self.nxl)) mskr = zeros((self.nyl, self.nxl)) for k in range(self.nbisland): idx = self.data[k]['idx'] psi0 = self.data[k]['psi0'] mskr[:, :] = 1. mskp[:, :] = 0 mskr[idx] = 0. celltocorner(mskr, work) mskp[work == 1] = 1 mskp = 1 - mskp vort = (roll(mskp, -1, axis=1) + roll(mskp, -1, axis=0) + roll(mskp, +1, axis=1) + roll(mskp, +1, axis=0)) z = (vort) * psi0 / (self.dx * self.dy) # *(1-mskp) self.rhsp[vort > 0] = z[vort > 0] self.psi[mskp == 1] = psi0 # print(self.psi[:,10]) print('island are ok')
def invert_vorticity(self, x, flag='full', island=False): """ compute psi from vorticity (or 'whosetspsi' in general) this routine interpolates the vorticity from cell centers to cell corners (where psi is defined) it then solves div*grad psi = omega with psi=0 along the boundary (Dirichlet condition) using a multigrid the non-divergent velocity is computed from psi""" iu = self.varname_list.index('u') iv = self.varname_list.index('v') ip = self.varname_list.index('psi') iw = self.varname_list.index(self.whosetspsi) u = x[iu] v = x[iv] psi = x[ip] fo.celltocorner(x[iw], self.work) #fo.celltocornerbicubic(x[iw], self.work) if island: # correcting RHS for islands self.work[:, :] -= self.rhsp if flag == 'fast': ite, res = self.gmg.twoVcycle(psi, self.work, { 'maxite': 1, 'tol': 1e-6, 'verbose': True }) # ite, res = self.gmg.solve(psi, self.work, # {'maxite': 2, # 'tol': 1e-8, # 'verbose': False}) else: # compute to machine accuracy if self.first_time: verbose = True else: verbose = False if (self.myrank == 0) and verbose: print('-' * 50) print(' Convergence of the vorticity inversion') print(' the residual should decrease by several orders') print(' of magnitude otherwise something is wrong') print('-' * 50) ite, res = self.gmg.solve(psi, self.work, { 'maxite': 4, 'tol': 1e-11, 'verbose': verbose }) if self.geometry == 'perio': # make sure psi has zero mean (to avoid the drift) psim = self.domain_integration(psi) / self.area psi -= psim # don't apply the fill_halo on it # [because fill_halo, as it is, is applying periodic BC] psi = psi * self.mskp if island: # we set psi on the boundary values by adding # self.psi (defined in island module) # before that line, psi=0 along all boundaries psi += self.psi # it should be added only if we invert for the total psi # it should not be added if we compute the increment of psi self.first_time = False # compute (u,v) @ U,V points from psi @ cell corner fo.computeorthogradient(self.msk, psi, self.dx, self.dy, self.nh, u, v) # self.fill_halo(u) # self.fill_halo(v) x[iu] = u x[iv] = v x[ip] = psi
def __init__(self, param, grid): self.list_param = [ 'varname_list', 'tracer_list', 'whosetspsi', 'mpi', 'npx', 'npy', 'nh', 'gravity', 'f0', 'beta', 'Rd', 'qgoperator', 'order', 'Kdiff', 'diffusion', 'enforce_momentum', 'isisland', 'aparab', 'flux_splitting_method', 'hydroepsilon', 'myrank', 'geometry', 'sqgoperator' ] param.copy(self, self.list_param) self.list_grid = [ 'msk', 'nxl', 'nyl', 'dx', 'dy', 'bcarea', 'mpitools', 'msknoslip', 'mskbc', 'domain_integration', 'nh', 'xr0', 'yr0', 'i0', 'j0', 'area' ] grid.copy(self, self.list_grid) self.first_time = True # internal work array for the inversion self.work = zeros((self.nyl, self.nxl)) self.work2 = zeros((self.nyl, self.nxl)) pp = { 'np': param.npx, 'mp': param.npy, 'nh': param.nh, 'n': param.nx // param.npx, 'm': param.ny // param.npy, 'omega': 8. / 9., 'npmpmax': 1, 'verbose': False, 'dx': grid.dx, 'dy': grid.dy, 'n1': 32, 'n0': 4, 'method': 'deep', 'nagglo': 2, 'hydroepsilon': param.hydroepsilon, 'relaxation': param.relaxation } # load the multigrid solver # # WARNING: the multigrid needs the mask at cell corners!!! # not at cell centers mskr = self.msk * 1. # this piece is a bit awkward: to initialize gmg, we need # a mask with a halo properly filled but the fill_halo method # belongs to gmg. We have a circular definition. # the trick: define a dummy gmg first a msk=1 everywhere # then grab the fill_halo method and redefine once again the # multigrid, this time with the proper mask # self.gmg = Gmg(pp,mskr) # borrow the fill_halo from the multigrid # self.fill_halo = self.gmg.grid[0].halo.fill fo.celltocorner(mskr, self.work) # self.fill_halo(self.work) # del self.gmg # del self.fill_halo self.work[self.work < 1.] = 0. self.mskp = self.msk * 0 self.mskp[self.work == 1.] = 1 pp['verbose'] = True if self.myrank == 0: print('-' * 50) print(' Multigrid hierarchy') print('-' * 50) if hasattr(self, 'qgoperator'): pp['qgoperator'] = True pp['Rd'] = self.Rd self.gmg = Gmg(pp, self.work) else: self.gmg = Gmg(pp, self.work) if hasattr(self, 'sqgoperator'): self.fourier = Fourier(param, grid) # borrow the fill_halo from the multigrid self.fill_halo = self.gmg.grid[0].halo.fill grid.fill_halo = self.gmg.grid[0].halo.fill self.blwidth = param.Lx * 0.05 # tentative for a regularized no-slip source term coef = 0. * zeros_like(self.mskp) coef[1:, 1:] = (self.mskp[:-1, 1:] + self.mskp[:-1, :-1] + self.mskp[1:, 1:] + self.mskp[1:, :-1]) # nbpsibc is the number of land psi-points surrounding a fluid cell self.nbpsibc = (4. - coef) * self.msk self.nbpsibc[self.nbpsibc > 0] = 1. self.set_boundary_msk() self.cst = zeros(5, ) # select the proper flux discretization if self.order % 2 == 0: self.fortran_adv = fa.adv_centered self.cst[0] = grid.dx self.cst[1] = grid.dy self.cst[2] = 0.05 self.cst[3] = 0 # umax self.cst[4] = 0 # unused # should be updated at each timestep # self.cst[3]=param.umax else: self.fortran_adv = fa.adv_upwind self.cst[0] = grid.dx self.cst[1] = grid.dy self.cst[2] = 0.05 self.cst[3] = 0 # umax self.cst[4] = self.aparab # should be updated at each timestep # self.cst[3]=param.umax # controls the flux splitting method # 0 = min/max # 1 = parabolic list_fs_method = ['minmax', 'parabolic'] if self.flux_splitting_method in list_fs_method: self.fs_method = list_fs_method.index(self.flux_splitting_method) else: print('Warning: %s does not exist' % self.flux_splitting_method) print('replaced with the default: parabolic') self.fs_method = list_fs_method.index('parabolic') # these coefficients below are used for the thermalwind model coef = 0. * zeros_like(self.msk) coef[1:-1, 1:-1] = self.msk[1:-1, 2:] + self.msk[1:-1, 0:-2] coef[coef < 2] = 0. coef[coef == 2] = 0.5 self.fill_halo(coef) self.coefb = coef * 1. coef = 0. * zeros_like(self.msk) coef[1:-1, 1:-1] = self.msk[2:, 1:-1] + self.msk[0:-2, 1:-1] coef[coef < 2] = 0. coef[coef == 2] = 0.5 self.fill_halo(coef) self.coefV = coef * 1. if type(self.Kdiff) != dict: K = self.Kdiff self.Kdiff = {} for trac in self.tracer_list: self.Kdiff[trac] = K if self.diffusion: print('diffusion coefficients') print(' => ', self.Kdiff)