def unsplit_fluxes(my_data, rp, dt, scalar_name): """ Construct the fluxes through the interfaces for the linear advection equation: .. math:: a_t + u a_x + v a_y = 0 We use a second-order (piecewise linear) unsplit Godunov method (following Colella 1990). In the pure advection case, there is no Riemann problem we need to solve -- we just simply do upwinding. So there is only one 'state' at each interface, and the zone the information comes from depends on the sign of the velocity. Our convection is that the fluxes are going to be defined on the left edge of the computational zones:: | | | | | | | | -+------+------+------+------+------+------+-- | i-1 | i | i+1 | a_l,i a_r,i a_l,i+1 a_r,i and a_l,i+1 are computed using the information in zone i,j. Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation dt : float The timestep we are advancing through. scalar_name : str The name of the variable contained in my_data that we are advecting Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ myg = my_data.grid a = my_data.get_var(scalar_name) u = my_data.get_var("x-velocity") v = my_data.get_var("y-velocity") cx = u * dt / myg.dx cy = v * dt / myg.dy # -------------------------------------------------------------------------- # monotonized central differences # -------------------------------------------------------------------------- limiter = rp.get_param("advection.limiter") ldelta_ax = reconstruction.limit(a, myg, 1, limiter) ldelta_ay = reconstruction.limit(a, myg, 2, limiter) # upwind in x-direction a_x = myg.scratch_array() shift_x = my_data.get_var("x-shift").astype(int) for index, vel in np.ndenumerate(u.v(buf=1)): if vel < 0: a_x.v(buf=1)[index] = a.ip(shift_x.v(buf=1)[index], buf=1)[index] \ - 0.5*(1.0 + cx.v(buf=1)[index]) \ * ldelta_ax.ip(shift_x.v(buf=1)[index], buf=1)[index] else: a_x.v(buf=1)[index] = a.ip(shift_x.v(buf=1)[index], buf=1)[index] \ + 0.5*(1.0 - cx.v(buf=1)[index]) \ * ldelta_ax.ip(shift_x.v(buf=1)[index], buf=1)[index] # upwind in y-direction a_y = myg.scratch_array() shift_y = my_data.get_var("y-shift").astype(int) for index, vel in np.ndenumerate(v.v(buf=1)): if vel < 0: a_y.v(buf=1)[index] = a.jp(shift_y.v(buf=1)[index], buf=1)[index] \ - 0.5*(1.0 + cy.v(buf=1)[index]) \ * ldelta_ay.jp(shift_y.v(buf=1)[index], buf=1)[index] else: a_y.v(buf=1)[index] = a.jp(shift_y.v(buf=1)[index], buf=1)[index] \ + 0.5*(1.0 - cy.v(buf=1)[index]) \ * ldelta_ay.jp(shift_y.v(buf=1)[index], buf=1)[index] # compute the transverse flux differences. The flux is just (u a) # HOTF F_xt = u * a_x F_yt = v * a_y F_x = myg.scratch_array() F_y = myg.scratch_array() # the zone where we grab the transverse flux derivative from # depends on the sign of the advective velocity dtdx2 = 0.5 * dt / myg.dx dtdy2 = 0.5 * dt / myg.dy for index, vel in np.ndenumerate(u.v(buf=1)): F_x.v(buf=1)[index] = vel * ( a_x.v(buf=1)[index] - dtdy2 * (F_yt.ip_jp(shift_x.v(buf=1)[index], 1, buf=1)[index] - F_yt.ip(shift_x.v(buf=1)[index], buf=1)[index])) for index, vel in np.ndenumerate(v.v(buf=1)): F_y.v(buf=1)[index] = vel * ( a_y.v(buf=1)[index] - dtdx2 * (F_xt.ip_jp(1, shift_y.v(buf=1)[index], buf=1)[index] - F_xt.jp(shift_y.v(buf=1)[index], buf=1)[index])) return F_x, F_y
def fluxes(my_data, rp, ivars, solid, tc): """ unsplitFluxes returns the fluxes through the x and y interfaces by doing an unsplit reconstruction of the interface values and then solving the Riemann problem through all the interfaces at once currently we assume a gamma-law EOS Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation vars : Variables object The Variables object that tells us which indices refer to which variables tc : TimerCollection object The timers we are using to profile Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ tm_flux = tc.timer("unsplitFluxes") tm_flux.begin() myg = my_data.grid gamma = rp.get_param("eos.gamma") # ========================================================================= # compute the primitive variables # ========================================================================= # Q = (rho, u, v, p) q = comp.cons_to_prim(my_data.data, gamma, ivars, myg) # ========================================================================= # compute the flattening coefficients # ========================================================================= # there is a single flattening coefficient (xi) for all directions use_flattening = rp.get_param("compressible.use_flattening") if use_flattening: xi_x = reconstruction.flatten(myg, q, 1, ivars, rp) xi_y = reconstruction.flatten(myg, q, 2, ivars, rp) xi = reconstruction.flatten_multid(myg, q, xi_x, xi_y, ivars) else: xi = 1.0 # monotonized central differences in x-direction tm_limit = tc.timer("limiting") tm_limit.begin() limiter = rp.get_param("compressible.limiter") ldx = myg.scratch_array(nvar=ivars.nvar) ldy = myg.scratch_array(nvar=ivars.nvar) for n in range(ivars.nvar): ldx[:, :, n] = xi * reconstruction.limit(q[:, :, n], myg, 1, limiter) ldy[:, :, n] = xi * reconstruction.limit(q[:, :, n], myg, 2, limiter) tm_limit.end() # if we are doing a well-balanced scheme, then redo the pressure # note: we only have gravity in the y direction, so we will only # modify the y pressure slope well_balanced = rp.get_param("compressible.well_balanced") grav = rp.get_param("compressible.grav") if well_balanced: ldy[:, :, ivars.ip] = reconstruction.well_balance(q, myg, limiter, ivars, grav) # ========================================================================= # x-direction # ========================================================================= # left and right primitive variable states tm_states = tc.timer("interfaceStates") tm_states.begin() V_l = myg.scratch_array(ivars.nvar) V_r = myg.scratch_array(ivars.nvar) for n in range(ivars.nvar): V_l.ip(1, n=n, buf=2)[:, :] = q.v(n=n, buf=2) + 0.5 * ldx.v(n=n, buf=2) V_r.v(n=n, buf=2)[:, :] = q.v(n=n, buf=2) - 0.5 * ldx.v(n=n, buf=2) tm_states.end() # transform interface states back into conserved variables U_xl = comp.prim_to_cons(V_l, gamma, ivars, myg) U_xr = comp.prim_to_cons(V_r, gamma, ivars, myg) # ========================================================================= # y-direction # ========================================================================= # left and right primitive variable states tm_states.begin() for n in range(ivars.nvar): if well_balanced and n == ivars.ip: # we want to do p0 + p1 on the interfaces. We found the # limited slope for p1 (it's average is 0). So now we # need p0 on the interface too V_l.jp(1, n=n, buf=2)[:, :] = q.v(n=ivars.ip, buf=2) + \ 0.5 * myg.dy * q.v(n=ivars.irho, buf=2) * \ grav + 0.5 * ldy.v(n=ivars.ip, buf=2) V_r.v(n=n, buf=2)[:, :] = q.v(n=ivars.ip, buf=2) - \ 0.5 * myg.dy * q.v(n=ivars.irho, buf=2) * \ grav - 0.5 * ldy.v(n=ivars.ip, buf=2) else: V_l.jp(1, n=n, buf=2)[:, :] = q.v(n=n, buf=2) + 0.5 * ldy.v(n=n, buf=2) V_r.v(n=n, buf=2)[:, :] = q.v(n=n, buf=2) - 0.5 * ldy.v(n=n, buf=2) tm_states.end() # transform interface states back into conserved variables U_yl = comp.prim_to_cons(V_l, gamma, ivars, myg) U_yr = comp.prim_to_cons(V_r, gamma, ivars, myg) # ========================================================================= # construct the fluxes normal to the interfaces # ========================================================================= tm_riem = tc.timer("Riemann") tm_riem.begin() riemann = rp.get_param("compressible.riemann") if riemann == "HLLC": riemannFunc = interface.riemann_hllc elif riemann == "CGF": riemannFunc = interface.riemann_cgf else: msg.fail("ERROR: Riemann solver undefined") _fx = riemannFunc(1, myg.ng, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, solid.xl, solid.xr, gamma, U_xl, U_xr) _fy = riemannFunc(2, myg.ng, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, solid.yl, solid.yr, gamma, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() # ========================================================================= # apply artificial viscosity # ========================================================================= cvisc = rp.get_param("compressible.cvisc") _ax, _ay = interface.artificial_viscosity(myg.ng, myg.dx, myg.dy, cvisc, q.v(n=ivars.iu, buf=myg.ng), q.v(n=ivars.iv, buf=myg.ng)) avisco_x = ai.ArrayIndexer(d=_ax, grid=myg) avisco_y = ai.ArrayIndexer(d=_ay, grid=myg) b = (2, 1) for n in range(ivars.nvar): # F_x = F_x + avisco_x * (U(i-1,j) - U(i,j)) var = my_data.get_var_by_index(n) F_x.v(buf=b, n=n)[:, :] += \ avisco_x.v(buf=b) * (var.ip(-1, buf=b) - var.v(buf=b)) # F_y = F_y + avisco_y * (U(i,j-1) - U(i,j)) F_y.v(buf=b, n=n)[:, :] += \ avisco_y.v(buf=b) * (var.jp(-1, buf=b) - var.v(buf=b)) tm_flux.end() return F_x, F_y
def fluxes(my_data, rp, ivars, solid, tc): """ unsplitFluxes returns the fluxes through the x and y interfaces by doing an unsplit reconstruction of the interface values and then solving the Riemann problem through all the interfaces at once currently we assume a gamma-law EOS Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation vars : Variables object The Variables object that tells us which indices refer to which variables tc : TimerCollection object The timers we are using to profile Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ tm_flux = tc.timer("unsplitFluxes") tm_flux.begin() myg = my_data.grid gamma = rp.get_param("eos.gamma") # ========================================================================= # compute the primitive variables # ========================================================================= # Q = (rho, u, v, p) q = comp.cons_to_prim(my_data.data, gamma, ivars, myg) # ========================================================================= # compute the flattening coefficients # ========================================================================= # there is a single flattening coefficient (xi) for all directions use_flattening = rp.get_param("compressible.use_flattening") if use_flattening: xi_x = reconstruction.flatten(myg, q, 1, ivars, rp) xi_y = reconstruction.flatten(myg, q, 2, ivars, rp) xi = reconstruction.flatten_multid(myg, q, xi_x, xi_y, ivars) else: xi = 1.0 # monotonized central differences in x-direction tm_limit = tc.timer("limiting") tm_limit.begin() limiter = rp.get_param("compressible.limiter") ldx = myg.scratch_array(nvar=ivars.nvar) ldy = myg.scratch_array(nvar=ivars.nvar) for n in range(ivars.nvar): ldx[:, :, n] = xi * reconstruction.limit(q[:, :, n], myg, 1, limiter) ldy[:, :, n] = xi * reconstruction.limit(q[:, :, n], myg, 2, limiter) tm_limit.end() # if we are doing a well-balanced scheme, then redo the pressure # note: we only have gravity in the y direction, so we will only # modify the y pressure slope well_balanced = rp.get_param("compressible.well_balanced") grav = rp.get_param("compressible.grav") if well_balanced: ldy[:, :, ivars.ip] = reconstruction.well_balance( q, myg, limiter, ivars, grav) # ========================================================================= # x-direction # ========================================================================= # left and right primitive variable states tm_states = tc.timer("interfaceStates") tm_states.begin() V_l = myg.scratch_array(ivars.nvar) V_r = myg.scratch_array(ivars.nvar) for n in range(ivars.nvar): V_l.ip(1, n=n, buf=2)[:, :] = q.v(n=n, buf=2) + 0.5 * ldx.v(n=n, buf=2) V_r.v(n=n, buf=2)[:, :] = q.v(n=n, buf=2) - 0.5 * ldx.v(n=n, buf=2) tm_states.end() # transform interface states back into conserved variables U_xl = comp.prim_to_cons(V_l, gamma, ivars, myg) U_xr = comp.prim_to_cons(V_r, gamma, ivars, myg) # ========================================================================= # y-direction # ========================================================================= # left and right primitive variable states tm_states.begin() for n in range(ivars.nvar): if well_balanced and n == ivars.ip: # we want to do p0 + p1 on the interfaces. We found the # limited slope for p1 (it's average is 0). So now we # need p0 on the interface too V_l.jp(1, n=n, buf=2)[:, :] = q.v(n=ivars.ip, buf=2) + \ 0.5 * myg.dy * q.v(n=ivars.irho, buf=2) * \ grav + 0.5 * ldy.v(n=ivars.ip, buf=2) V_r.v(n=n, buf=2)[:, :] = q.v(n=ivars.ip, buf=2) - \ 0.5 * myg.dy * q.v(n=ivars.irho, buf=2) * \ grav - 0.5 * ldy.v(n=ivars.ip, buf=2) else: V_l.jp(1, n=n, buf=2)[:, :] = q.v( n=n, buf=2) + 0.5 * ldy.v(n=n, buf=2) V_r.v(n=n, buf=2)[:, :] = q.v(n=n, buf=2) - 0.5 * ldy.v(n=n, buf=2) tm_states.end() # transform interface states back into conserved variables U_yl = comp.prim_to_cons(V_l, gamma, ivars, myg) U_yr = comp.prim_to_cons(V_r, gamma, ivars, myg) # ========================================================================= # construct the fluxes normal to the interfaces # ========================================================================= tm_riem = tc.timer("Riemann") tm_riem.begin() riemann = rp.get_param("compressible.riemann") if riemann == "HLLC": riemannFunc = interface.riemann_hllc elif riemann == "CGF": riemannFunc = interface.riemann_cgf else: msg.fail("ERROR: Riemann solver undefined") _fx = riemannFunc(1, myg.ng, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, solid.xl, solid.xr, gamma, U_xl, U_xr) _fy = riemannFunc(2, myg.ng, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, solid.yl, solid.yr, gamma, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() # ========================================================================= # apply artificial viscosity # ========================================================================= cvisc = rp.get_param("compressible.cvisc") _ax, _ay = interface.artificial_viscosity(myg.ng, myg.dx, myg.dy, cvisc, q.v(n=ivars.iu, buf=myg.ng), q.v(n=ivars.iv, buf=myg.ng)) avisco_x = ai.ArrayIndexer(d=_ax, grid=myg) avisco_y = ai.ArrayIndexer(d=_ay, grid=myg) b = (2, 1) for n in range(ivars.nvar): # F_x = F_x + avisco_x * (U(i-1,j) - U(i,j)) var = my_data.get_var_by_index(n) F_x.v(buf=b, n=n)[:, :] += \ avisco_x.v(buf=b) * (var.ip(-1, buf=b) - var.v(buf=b)) # F_y = F_y + avisco_y * (U(i,j-1) - U(i,j)) F_y.v(buf=b, n=n)[:, :] += \ avisco_y.v(buf=b) * (var.jp(-1, buf=b) - var.v(buf=b)) tm_flux.end() return F_x, F_y
def fluxes(my_data, rp, dt): """ Construct the fluxes through the interfaces for the linear advection equation: .. math:: a_t + u a_x + v a_y = 0 We use a second-order (piecewise linear) Godunov method to construct the interface states, using Runge-Kutta integration. These are one-dimensional predictions to the interface, relying on the coupling in transverse directions through the intermediate stages of the Runge-Kutta integrator. In the pure advection case, there is no Riemann problem we need to solve -- we just simply do upwinding. So there is only one 'state' at each interface, and the zone the information comes from depends on the sign of the velocity. Our convection is that the fluxes are going to be defined on the left edge of the computational zones:: | | | | | | | | -+------+------+------+------+------+------+-- | i-1 | i | i+1 | a_l,i a_r,i a_l,i+1 a_r,i and a_l,i+1 are computed using the information in zone i,j. Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation dt : float The timestep we are advancing through. scalar_name : str The name of the variable contained in my_data that we are advecting Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ myg = my_data.grid a = my_data.get_var("density") # get the advection velocities u = rp.get_param("advection.u") v = rp.get_param("advection.v") # -------------------------------------------------------------------------- # monotonized central differences # -------------------------------------------------------------------------- limiter = rp.get_param("advection.limiter") if limiter < 10: ldelta_ax = reconstruction.limit(a, myg, 1, limiter) ldelta_ay = reconstruction.limit(a, myg, 2, limiter) else: ldelta_ax, ldelta_ay = reconstruction.limit(a, myg, 0, limiter) a_x = myg.scratch_array() # upwind if u < 0: # a_x[i,j] = a[i,j] - 0.5*(1.0 + cx)*ldelta_a[i,j] a_x.v(buf=1)[:, :] = a.v(buf=1) - 0.5 * ldelta_ax.v(buf=1) else: # a_x[i,j] = a[i-1,j] + 0.5*(1.0 - cx)*ldelta_a[i-1,j] a_x.v(buf=1)[:, :] = a.ip(-1, buf=1) + 0.5 * ldelta_ax.ip(-1, buf=1) # y-direction a_y = myg.scratch_array() # upwind if v < 0: # a_y[i,j] = a[i,j] - 0.5*(1.0 + cy)*ldelta_a[i,j] a_y.v(buf=1)[:, :] = a.v(buf=1) - 0.5 * ldelta_ay.v(buf=1) else: # a_y[i,j] = a[i,j-1] + 0.5*(1.0 - cy)*ldelta_a[i,j-1] a_y.v(buf=1)[:, :] = a.jp(-1, buf=1) + 0.5 * ldelta_ay.jp(-1, buf=1) F_x = u * a_x F_y = v * a_y return F_x, F_y
def evolve(self): """ Evolve the incompressible equations through one timestep. """ u = self.cc_data.get_var("x-velocity") v = self.cc_data.get_var("y-velocity") gradp_x = self.cc_data.get_var("gradp_x") gradp_y = self.cc_data.get_var("gradp_y") phi = self.cc_data.get_var("phi") myg = self.cc_data.grid # --------------------------------------------------------------------- # create the limited slopes of u and v (in both directions) # --------------------------------------------------------------------- limiter = self.rp.get_param("incompressible.limiter") ldelta_ux = reconstruction.limit(u, myg, 1, limiter) ldelta_vx = reconstruction.limit(v, myg, 1, limiter) ldelta_uy = reconstruction.limit(u, myg, 2, limiter) ldelta_vy = reconstruction.limit(v, myg, 2, limiter) # --------------------------------------------------------------------- # get the advective velocities # --------------------------------------------------------------------- """ the advective velocities are the normal velocity through each cell interface, and are defined on the cell edges, in a MAC type staggered form n+1/2 v i,j+1/2 +------+------+ | | n+1/2 | | n+1/2 u + U + u i-1/2,j | i,j | i+1/2,j | | +------+------+ n+1/2 v i,j-1/2 """ # this returns u on x-interfaces and v on y-interfaces. These # constitute the MAC grid if self.verbose > 0: print(" making MAC velocities") _um, _vm = incomp_interface.mac_vels(myg.ng, myg.dx, myg.dy, self.dt, u, v, ldelta_ux, ldelta_vx, ldelta_uy, ldelta_vy, gradp_x, gradp_y) u_MAC = ai.ArrayIndexer(d=_um, grid=myg) v_MAC = ai.ArrayIndexer(d=_vm, grid=myg) # --------------------------------------------------------------------- # do a MAC projection ot make the advective velocities divergence # free # --------------------------------------------------------------------- # we will solve L phi = D U^MAC, where phi is cell centered, and # U^MAC is the MAC-type staggered grid of the advective # velocities. if self.verbose > 0: print(" MAC projection") # create the multigrid object mg = MG.CellCenterMG2d(myg.nx, myg.ny, xl_BC_type="periodic", xr_BC_type="periodic", yl_BC_type="periodic", yr_BC_type="periodic", xmin=myg.xmin, xmax=myg.xmax, ymin=myg.ymin, ymax=myg.ymax, verbose=0) # first compute divU divU = mg.soln_grid.scratch_array() # MAC velocities are edge-centered. divU is cell-centered. divU.v()[:, :] = \ (u_MAC.ip(1) - u_MAC.v())/myg.dx + (v_MAC.jp(1) - v_MAC.v())/myg.dy # solve the Poisson problem mg.init_zeros() mg.init_RHS(divU) mg.solve(rtol=1.e-12) # update the normal velocities with the pressure gradient -- these # constitute our advective velocities phi_MAC = self.cc_data.get_var("phi-MAC") solution = mg.get_solution() phi_MAC.v(buf=1)[:, :] = solution.v(buf=1) # we need the MAC velocities on all edges of the computational domain b = (0, 1, 0, 0) u_MAC.v( buf=b)[:, :] -= (phi_MAC.v(buf=b) - phi_MAC.ip(-1, buf=b)) / myg.dx b = (0, 0, 0, 1) v_MAC.v( buf=b)[:, :] -= (phi_MAC.v(buf=b) - phi_MAC.jp(-1, buf=b)) / myg.dy # --------------------------------------------------------------------- # recompute the interface states, using the advective velocity # from above # --------------------------------------------------------------------- if self.verbose > 0: print(" making u, v edge states") _ux, _vx, _uy, _vy = \ incomp_interface.states(myg.ng, myg.dx, myg.dy, self.dt, u, v, ldelta_ux, ldelta_vx, ldelta_uy, ldelta_vy, gradp_x, gradp_y, u_MAC, v_MAC) u_xint = ai.ArrayIndexer(d=_ux, grid=myg) v_xint = ai.ArrayIndexer(d=_vx, grid=myg) u_yint = ai.ArrayIndexer(d=_uy, grid=myg) v_yint = ai.ArrayIndexer(d=_vy, grid=myg) # --------------------------------------------------------------------- # update U to get the provisional velocity field # --------------------------------------------------------------------- if self.verbose > 0: print(" doing provisional update of u, v") # compute (U.grad)U # we want u_MAC U_x + v_MAC U_y advect_x = myg.scratch_array() advect_y = myg.scratch_array() # u u_x + v u_y advect_x.v()[:, :] = \ 0.5*(u_MAC.v() + u_MAC.ip(1))*(u_xint.ip(1) - u_xint.v())/myg.dx + \ 0.5*(v_MAC.v() + v_MAC.jp(1))*(u_yint.jp(1) - u_yint.v())/myg.dy # u v_x + v v_y advect_y.v()[:, :] = \ 0.5*(u_MAC.v() + u_MAC.ip(1))*(v_xint.ip(1) - v_xint.v())/myg.dx + \ 0.5*(v_MAC.v() + v_MAC.jp(1))*(v_yint.jp(1) - v_yint.v())/myg.dy proj_type = self.rp.get_param("incompressible.proj_type") if proj_type == 1: u[:, :] -= (self.dt * advect_x[:, :] + self.dt * gradp_x[:, :]) v[:, :] -= (self.dt * advect_y[:, :] + self.dt * gradp_y[:, :]) elif proj_type == 2: u[:, :] -= self.dt * advect_x[:, :] v[:, :] -= self.dt * advect_y[:, :] self.cc_data.fill_BC("x-velocity") self.cc_data.fill_BC("y-velocity") # --------------------------------------------------------------------- # project the final velocity # --------------------------------------------------------------------- # now we solve L phi = D (U* /dt) if self.verbose > 0: print(" final projection") # create the multigrid object mg = MG.CellCenterMG2d(myg.nx, myg.ny, xl_BC_type="periodic", xr_BC_type="periodic", yl_BC_type="periodic", yr_BC_type="periodic", xmin=myg.xmin, xmax=myg.xmax, ymin=myg.ymin, ymax=myg.ymax, verbose=0) # first compute divU # u/v are cell-centered, divU is cell-centered divU.v()[:, :] = \ 0.5*(u.ip(1) - u.ip(-1))/myg.dx + 0.5*(v.jp(1) - v.jp(-1))/myg.dy mg.init_RHS(divU / self.dt) # use the old phi as our initial guess phiGuess = mg.soln_grid.scratch_array() phiGuess.v(buf=1)[:, :] = phi.v(buf=1) mg.init_solution(phiGuess) # solve mg.solve(rtol=1.e-12) # store the solution phi[:, :] = mg.get_solution(grid=myg) # compute the cell-centered gradient of p and update the velocities # this differs depending on what we projected. gradphi_x, gradphi_y = mg.get_solution_gradient(grid=myg) # u = u - grad_x phi dt u[:, :] -= self.dt * gradphi_x v[:, :] -= self.dt * gradphi_y # store gradp for the next step if proj_type == 1: gradp_x[:, :] += gradphi_x[:, :] gradp_y[:, :] += gradphi_y[:, :] elif proj_type == 2: gradp_x[:, :] = gradphi_x[:, :] gradp_y[:, :] = gradphi_y[:, :] self.cc_data.fill_BC("x-velocity") self.cc_data.fill_BC("y-velocity") if self.particles is not None: self.particles.update_particles(self.dt) # increment the time if not self.in_preevolve: self.cc_data.t += self.dt self.n += 1
def unsplit_fluxes(my_data, my_aux, rp, ivars, solid, tc, dt): """ unsplitFluxes returns the fluxes through the x and y interfaces by doing an unsplit reconstruction of the interface values and then solving the Riemann problem through all the interfaces at once The runtime parameter g is assumed to be the gravitational acceleration in the y-direction Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation vars : Variables object The Variables object that tells us which indices refer to which variables tc : TimerCollection object The timers we are using to profile dt : float The timestep we are advancing through. Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ tm_flux = tc.timer("unsplitFluxes") tm_flux.begin() myg = my_data.grid g = rp.get_param("swe.grav") # ========================================================================= # compute the primitive variables # ========================================================================= # Q = (h, u, v, {X}) q = comp.cons_to_prim(my_data.data, g, ivars, myg) # ========================================================================= # compute the flattening coefficients # ========================================================================= # there is a single flattening coefficient (xi) for all directions use_flattening = rp.get_param("swe.use_flattening") if use_flattening: xi_x = reconstruction.flatten(myg, q, 1, ivars, rp) xi_y = reconstruction.flatten(myg, q, 2, ivars, rp) xi = reconstruction.flatten_multid(myg, q, xi_x, xi_y, ivars) else: xi = 1.0 # monotonized central differences tm_limit = tc.timer("limiting") tm_limit.begin() limiter = rp.get_param("swe.limiter") ldx = myg.scratch_array(nvar=ivars.nvar) ldy = myg.scratch_array(nvar=ivars.nvar) for n in range(ivars.nvar): ldx[:, :, n] = xi*reconstruction.limit(q[:, :, n], myg, 1, limiter) ldy[:, :, n] = xi*reconstruction.limit(q[:, :, n], myg, 2, limiter) tm_limit.end() # ========================================================================= # x-direction # ========================================================================= # left and right primitive variable states tm_states = tc.timer("interfaceStates") tm_states.begin() V_l, V_r = ifc.states(1, myg.ng, myg.dx, dt, ivars.ih, ivars.iu, ivars.iv, ivars.ix, ivars.naux, g, q, ldx) tm_states.end() # transform interface states back into conserved variables U_xl = comp.prim_to_cons(V_l, g, ivars, myg) U_xr = comp.prim_to_cons(V_r, g, ivars, myg) # ========================================================================= # y-direction # ========================================================================= # left and right primitive variable states tm_states.begin() _V_l, _V_r = ifc.states(2, myg.ng, myg.dy, dt, ivars.ih, ivars.iu, ivars.iv, ivars.ix, ivars.naux, g, q, ldy) V_l = ai.ArrayIndexer(d=_V_l, grid=myg) V_r = ai.ArrayIndexer(d=_V_r, grid=myg) tm_states.end() # transform interface states back into conserved variables U_yl = comp.prim_to_cons(V_l, g, ivars, myg) U_yr = comp.prim_to_cons(V_r, g, ivars, myg) # ========================================================================= # compute transverse fluxes # ========================================================================= tm_riem = tc.timer("riemann") tm_riem.begin() riemann = rp.get_param("swe.riemann") if riemann == "HLLC": riemannFunc = ifc.riemann_hllc elif riemann == "Roe": riemannFunc = ifc.riemann_roe else: msg.fail("ERROR: Riemann solver undefined") _fx = riemannFunc(1, myg.ng, ivars.ih, ivars.ixmom, ivars.iymom, ivars.ihx, ivars.naux, solid.xl, solid.xr, g, U_xl, U_xr) _fy = riemannFunc(2, myg.ng, ivars.ih, ivars.ixmom, ivars.iymom, ivars.ihx, ivars.naux, solid.yl, solid.yr, g, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() # ========================================================================= # construct the interface values of U now # ========================================================================= """ finally, we can construct the state perpendicular to the interface by adding the central difference part to the trasverse flux difference. The states that we represent by indices i,j are shown below (1,2,3,4): j+3/2--+----------+----------+----------+ | | | | | | | | j+1 -+ | | | | | | | | | | | 1: U_xl[i,j,:] = U j+1/2--+----------XXXXXXXXXXXX----------+ i-1/2,j,L | X X | | X X | j -+ 1 X 2 X | 2: U_xr[i,j,:] = U | X X | i-1/2,j,R | X 4 X | j-1/2--+----------XXXXXXXXXXXX----------+ | | 3 | | 3: U_yl[i,j,:] = U | | | | i,j-1/2,L j-1 -+ | | | | | | | | | | | 4: U_yr[i,j,:] = U j-3/2--+----------+----------+----------+ i,j-1/2,R | | | | | | | i-1 i i+1 i-3/2 i-1/2 i+1/2 i+3/2 remember that the fluxes are stored on the left edge, so F_x[i,j,:] = F_x i-1/2, j F_y[i,j,:] = F_y i, j-1/2 """ tm_transverse = tc.timer("transverse flux addition") tm_transverse.begin() dtdx = dt/myg.dx dtdy = dt/myg.dy b = (2, 1) for n in range(ivars.nvar): # U_xl[i,j,:] = U_xl[i,j,:] - 0.5*dt/dy * (F_y[i-1,j+1,:] - F_y[i-1,j,:]) U_xl.v(buf=b, n=n)[:, :] += \ - 0.5*dtdy*(F_y.ip_jp(-1, 1, buf=b, n=n) - F_y.ip(-1, buf=b, n=n)) # U_xr[i,j,:] = U_xr[i,j,:] - 0.5*dt/dy * (F_y[i,j+1,:] - F_y[i,j,:]) U_xr.v(buf=b, n=n)[:, :] += \ - 0.5*dtdy*(F_y.jp(1, buf=b, n=n) - F_y.v(buf=b, n=n)) # U_yl[i,j,:] = U_yl[i,j,:] - 0.5*dt/dx * (F_x[i+1,j-1,:] - F_x[i,j-1,:]) U_yl.v(buf=b, n=n)[:, :] += \ - 0.5*dtdx*(F_x.ip_jp(1, -1, buf=b, n=n) - F_x.jp(-1, buf=b, n=n)) # U_yr[i,j,:] = U_yr[i,j,:] - 0.5*dt/dx * (F_x[i+1,j,:] - F_x[i,j,:]) U_yr.v(buf=b, n=n)[:, :] += \ - 0.5*dtdx*(F_x.ip(1, buf=b, n=n) - F_x.v(buf=b, n=n)) tm_transverse.end() # ========================================================================= # construct the fluxes normal to the interfaces # ========================================================================= # up until now, F_x and F_y stored the transverse fluxes, now we # overwrite with the fluxes normal to the interfaces tm_riem.begin() _fx = riemannFunc(1, myg.ng, ivars.ih, ivars.ixmom, ivars.iymom, ivars.ihx, ivars.naux, solid.xl, solid.xr, g, U_xl, U_xr) _fy = riemannFunc(2, myg.ng, ivars.ih, ivars.ixmom, ivars.iymom, ivars.ihx, ivars.naux, solid.yl, solid.yr, g, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() tm_flux.end() return F_x, F_y
def unsplit_fluxes(my_data, my_aux, rp, ivars, solid, tc, dt): """ unsplitFluxes returns the fluxes through the x and y interfaces by doing an unsplit reconstruction of the interface values and then solving the Riemann problem through all the interfaces at once currently we assume a gamma-law EOS The runtime parameter grav is assumed to be the gravitational acceleration in the y-direction Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation vars : Variables object The Variables object that tells us which indices refer to which variables tc : TimerCollection object The timers we are using to profile dt : float The timestep we are advancing through. Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ tm_flux = tc.timer("unsplitFluxes") tm_flux.begin() myg = my_data.grid gamma = rp.get_param("eos.gamma") # ========================================================================= # compute the primitive variables # ========================================================================= # Q = (rho, u, v, p, {X}) dens = my_data.get_var("density") ymom = my_data.get_var("y-momentum") q = comp.cons_to_prim(my_data.data, gamma, ivars, myg) # ========================================================================= # compute the flattening coefficients # ========================================================================= # there is a single flattening coefficient (xi) for all directions use_flattening = rp.get_param("compressible.use_flattening") if use_flattening: xi_x = reconstruction.flatten(myg, q, 1, ivars, rp) xi_y = reconstruction.flatten(myg, q, 2, ivars, rp) xi = reconstruction.flatten_multid(myg, q, xi_x, xi_y, ivars) else: xi = 1.0 # monotonized central differences tm_limit = tc.timer("limiting") tm_limit.begin() limiter = rp.get_param("compressible.limiter") ldx = myg.scratch_array(nvar=ivars.nvar) ldy = myg.scratch_array(nvar=ivars.nvar) for n in range(ivars.nvar): ldx[:, :, n] = xi * reconstruction.limit(q[:, :, n], myg, 1, limiter) ldy[:, :, n] = xi * reconstruction.limit(q[:, :, n], myg, 2, limiter) tm_limit.end() # ========================================================================= # x-direction # ========================================================================= # left and right primitive variable states tm_states = tc.timer("interfaceStates") tm_states.begin() V_l, V_r = ifc.states(1, myg.qx, myg.qy, myg.ng, myg.dx, dt, ivars.irho, ivars.iu, ivars.iv, ivars.ip, ivars.ix, ivars.nvar, ivars.naux, gamma, q, ldx) tm_states.end() # transform interface states back into conserved variables U_xl = comp.prim_to_cons(V_l, gamma, ivars, myg) U_xr = comp.prim_to_cons(V_r, gamma, ivars, myg) # ========================================================================= # y-direction # ========================================================================= # left and right primitive variable states tm_states.begin() _V_l, _V_r = ifc.states(2, myg.qx, myg.qy, myg.ng, myg.dy, dt, ivars.irho, ivars.iu, ivars.iv, ivars.ip, ivars.ix, ivars.nvar, ivars.naux, gamma, q, ldy) V_l = ai.ArrayIndexer(d=_V_l, grid=myg) V_r = ai.ArrayIndexer(d=_V_r, grid=myg) tm_states.end() # transform interface states back into conserved variables U_yl = comp.prim_to_cons(V_l, gamma, ivars, myg) U_yr = comp.prim_to_cons(V_r, gamma, ivars, myg) # ========================================================================= # apply source terms # ========================================================================= grav = rp.get_param("compressible.grav") ymom_src = my_aux.get_var("ymom_src") ymom_src.v()[:, :] = dens.v() * grav my_aux.fill_BC("ymom_src") E_src = my_aux.get_var("E_src") E_src.v()[:, :] = ymom.v() * grav my_aux.fill_BC("E_src") # ymom_xl[i,j] += 0.5*dt*dens[i-1,j]*grav U_xl.v(buf=1, n=ivars.iymom)[:, :] += 0.5 * dt * ymom_src.ip(-1, buf=1) U_xl.v(buf=1, n=ivars.iener)[:, :] += 0.5 * dt * E_src.ip(-1, buf=1) # ymom_xr[i,j] += 0.5*dt*dens[i,j]*grav U_xr.v(buf=1, n=ivars.iymom)[:, :] += 0.5 * dt * ymom_src.v(buf=1) U_xr.v(buf=1, n=ivars.iener)[:, :] += 0.5 * dt * E_src.v(buf=1) # ymom_yl[i,j] += 0.5*dt*dens[i,j-1]*grav U_yl.v(buf=1, n=ivars.iymom)[:, :] += 0.5 * dt * ymom_src.jp(-1, buf=1) U_yl.v(buf=1, n=ivars.iener)[:, :] += 0.5 * dt * E_src.jp(-1, buf=1) # ymom_yr[i,j] += 0.5*dt*dens[i,j]*grav U_yr.v(buf=1, n=ivars.iymom)[:, :] += 0.5 * dt * ymom_src.v(buf=1) U_yr.v(buf=1, n=ivars.iener)[:, :] += 0.5 * dt * E_src.v(buf=1) # ========================================================================= # compute transverse fluxes # ========================================================================= tm_riem = tc.timer("riemann") tm_riem.begin() riemann = rp.get_param("compressible.riemann") if riemann == "HLLC": riemannFunc = ifc.riemann_hllc elif riemann == "CGF": riemannFunc = ifc.riemann_cgf else: msg.fail("ERROR: Riemann solver undefined") _fx = riemannFunc(1, myg.qx, myg.qy, myg.ng, ivars.nvar, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, solid.xl, solid.xr, gamma, U_xl, U_xr) _fy = riemannFunc(2, myg.qx, myg.qy, myg.ng, ivars.nvar, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, solid.yl, solid.yr, gamma, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() # ========================================================================= # construct the interface values of U now # ========================================================================= """ finally, we can construct the state perpendicular to the interface by adding the central difference part to the trasverse flux difference. The states that we represent by indices i,j are shown below (1,2,3,4): j+3/2--+----------+----------+----------+ | | | | | | | | j+1 -+ | | | | | | | | | | | 1: U_xl[i,j,:] = U j+1/2--+----------XXXXXXXXXXXX----------+ i-1/2,j,L | X X | | X X | j -+ 1 X 2 X | 2: U_xr[i,j,:] = U | X X | i-1/2,j,R | X 4 X | j-1/2--+----------XXXXXXXXXXXX----------+ | | 3 | | 3: U_yl[i,j,:] = U | | | | i,j-1/2,L j-1 -+ | | | | | | | | | | | 4: U_yr[i,j,:] = U j-3/2--+----------+----------+----------+ i,j-1/2,R | | | | | | | i-1 i i+1 i-3/2 i-1/2 i+1/2 i+3/2 remember that the fluxes are stored on the left edge, so F_x[i,j,:] = F_x i-1/2, j F_y[i,j,:] = F_y i, j-1/2 """ tm_transverse = tc.timer("transverse flux addition") tm_transverse.begin() dtdx = dt / myg.dx dtdy = dt / myg.dy b = (2, 1) for n in range(ivars.nvar): # U_xl[i,j,:] = U_xl[i,j,:] - 0.5*dt/dy * (F_y[i-1,j+1,:] - F_y[i-1,j,:]) U_xl.v(buf=b, n=n)[:, :] += \ - 0.5*dtdy*(F_y.ip_jp(-1, 1, buf=b, n=n) - F_y.ip(-1, buf=b, n=n)) # U_xr[i,j,:] = U_xr[i,j,:] - 0.5*dt/dy * (F_y[i,j+1,:] - F_y[i,j,:]) U_xr.v(buf=b, n=n)[:, :] += \ - 0.5*dtdy*(F_y.jp(1, buf=b, n=n) - F_y.v(buf=b, n=n)) # U_yl[i,j,:] = U_yl[i,j,:] - 0.5*dt/dx * (F_x[i+1,j-1,:] - F_x[i,j-1,:]) U_yl.v(buf=b, n=n)[:, :] += \ - 0.5*dtdx*(F_x.ip_jp(1, -1, buf=b, n=n) - F_x.jp(-1, buf=b, n=n)) # U_yr[i,j,:] = U_yr[i,j,:] - 0.5*dt/dx * (F_x[i+1,j,:] - F_x[i,j,:]) U_yr.v(buf=b, n=n)[:, :] += \ - 0.5*dtdx*(F_x.ip(1, buf=b, n=n) - F_x.v(buf=b, n=n)) tm_transverse.end() # ========================================================================= # construct the fluxes normal to the interfaces # ========================================================================= # up until now, F_x and F_y stored the transverse fluxes, now we # overwrite with the fluxes normal to the interfaces tm_riem.begin() _fx = riemannFunc(1, myg.qx, myg.qy, myg.ng, ivars.nvar, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, solid.xl, solid.xr, gamma, U_xl, U_xr) _fy = riemannFunc(2, myg.qx, myg.qy, myg.ng, ivars.nvar, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, ivars.irhox, ivars.naux, solid.yl, solid.yr, gamma, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() # ========================================================================= # apply artificial viscosity # ========================================================================= cvisc = rp.get_param("compressible.cvisc") _ax, _ay = ifc.artificial_viscosity(myg.qx, myg.qy, myg.ng, myg.dx, myg.dy, cvisc, q.v(n=ivars.iu, buf=myg.ng), q.v(n=ivars.iv, buf=myg.ng)) avisco_x = ai.ArrayIndexer(d=_ax, grid=myg) avisco_y = ai.ArrayIndexer(d=_ay, grid=myg) b = (2, 1) for n in range(ivars.nvar): # F_x = F_x + avisco_x * (U(i-1,j) - U(i,j)) var = my_data.get_var_by_index(n) F_x.v(buf=b, n=n)[:, :] += \ avisco_x.v(buf=b)*(var.ip(-1, buf=b) - var.v(buf=b)) # F_y = F_y + avisco_y * (U(i,j-1) - U(i,j)) F_y.v(buf=b, n=n)[:, :] += \ avisco_y.v(buf=b)*(var.jp(-1, buf=b) - var.v(buf=b)) tm_flux.end() return F_x, F_y
def unsplit_fluxes(my_data, my_aux, rp, ivars, solid, tc, dt): """ unsplitFluxes returns the fluxes through the x and y interfaces by doing an unsplit reconstruction of the interface values and then solving the Riemann problem through all the interfaces at once currently we assume a gamma-law EOS The runtime parameter grav is assumed to be the gravitational acceleration in the y-direction Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation vars : Variables object The Variables object that tells us which indices refer to which variables tc : TimerCollection object The timers we are using to profile dt : float The timestep we are advancing through. Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ tm_flux = tc.timer("unsplitFluxes") tm_flux.begin() myg = my_data.grid gamma = rp.get_param("eos.gamma") #========================================================================= # compute the primitive variables #========================================================================= # Q = (rho, u, v, p) dens = my_data.get_var("density") xmom = my_data.get_var("x-momentum") ymom = my_data.get_var("y-momentum") ener = my_data.get_var("energy") r, u, v, p, re = my_data.get_var("primitive") smallp = 1.e-10 p = p.clip(smallp) # apply a floor to the pressure #========================================================================= # compute the flattening coefficients #========================================================================= # there is a single flattening coefficient (xi) for all directions use_flattening = rp.get_param("compressible.use_flattening") if use_flattening: delta = rp.get_param("compressible.delta") z0 = rp.get_param("compressible.z0") z1 = rp.get_param("compressible.z1") xi_x = reconstruction_f.flatten(1, p, u, myg.qx, myg.qy, myg.ng, smallp, delta, z0, z1) xi_y = reconstruction_f.flatten(2, p, v, myg.qx, myg.qy, myg.ng, smallp, delta, z0, z1) xi = reconstruction_f.flatten_multid(xi_x, xi_y, p, myg.qx, myg.qy, myg.ng) else: xi = 1.0 # monotonized central differences in x-direction tm_limit = tc.timer("limiting") tm_limit.begin() limiter = rp.get_param("compressible.limiter") ldelta_rx = xi * reconstruction.limit(r, myg, 1, limiter) ldelta_ux = xi * reconstruction.limit(u, myg, 1, limiter) ldelta_vx = xi * reconstruction.limit(v, myg, 1, limiter) ldelta_px = xi * reconstruction.limit(p, myg, 1, limiter) ldelta_rex = xi * reconstruction.limit(re, myg, 1, limiter) # monotonized central differences in y-direction ldelta_ry = xi * reconstruction.limit(r, myg, 2, limiter) ldelta_uy = xi * reconstruction.limit(u, myg, 2, limiter) ldelta_vy = xi * reconstruction.limit(v, myg, 2, limiter) ldelta_py = xi * reconstruction.limit(p, myg, 2, limiter) ldelta_rey = xi * reconstruction.limit(re, myg, 2, limiter) tm_limit.end() gamcl = 1.4 * np.ones((136, 18), order='F') gamcr = gamcl #========================================================================= # x-direction #========================================================================= # left and right primitive variable states tm_states = tc.timer("interfaceStates") tm_states.begin() # r = np.array(r, order = 'F') # u = np.array(u, order = 'F') # v = np.array(v, order = 'F') # p = np.array(p, order = 'F') # re = np.array(re, order = 'F') # ldelta_rx = np.array(ldelta_rx, order = 'F') # ldelta_ux = np.array(ldelta_ux, order = 'F') # ldelta_vx = np.array(ldelta_vx, order = 'F') # ldelta_px = np.array(ldelta_px, order = 'F') # ldelta_rex = np.array(ldelta_rex, order = 'F') # myg.ng = 5 # ivars.nvar = 5 # V_l, V_r = interface_f.states(1, myg.qx, myg.qy, myg.ng, myg.dx, dt, # ivars.nvar, # gamma, # r, u, v, p, re, # ldelta_rx, ldelta_ux, ldelta_vx, ldelta_px, ldelta_rex) # myg.ng = 4 # ivars.nvar = 4 V_l, V_r = interface_f.states(1, myg.qx, myg.qy, myg.ng, myg.dx, dt, ivars.nvar, gamma, r, u, v, p, ldelta_rx, ldelta_ux, ldelta_vx, ldelta_px) # keyboard() tm_states.end() # transform interface states back into conserved variables U_xl = comp.prim_to_cons(V_l, gamma, ivars, myg) U_xr = comp.prim_to_cons(V_r, gamma, ivars, myg) #========================================================================= # y-direction #========================================================================= # left and right primitive variable states tm_states.begin() # V_l, V_r = interface_f.states(2, myg.qx, myg.qy, myg.ng, myg.dy, dt, # ivars.nvar, # gamma, # r, u, v, p, re, # ldelta_ry, ldelta_uy, ldelta_vy, ldelta_py, ldelta_rey) V_l, V_r = interface_f.states(2, myg.qx, myg.qy, myg.ng, myg.dx, dt, ivars.nvar, gamma, r, u, v, p, ldelta_rx, ldelta_ux, ldelta_vx, ldelta_px) tm_states.end() # transform interface states back into conserved variables U_yl = comp.prim_to_cons(V_l, gamma, ivars, myg) U_yr = comp.prim_to_cons(V_r, gamma, ivars, myg) #myg.ng = 4 #========================================================================= # apply source terms #========================================================================= grav = rp.get_param("compressible.grav") ymom_src = my_aux.get_var("ymom_src") ymom_src.v()[:, :] = dens.v() * grav my_aux.fill_BC("ymom_src") E_src = my_aux.get_var("E_src") E_src.v()[:, :] = ymom.v() * grav my_aux.fill_BC("E_src") # ymom_xl[i,j] += 0.5*dt*dens[i-1,j]*grav U_xl.v(buf=1, n=ivars.iymom)[:, :] += 0.5 * dt * ymom_src.ip(-1, buf=1) U_xl.v(buf=1, n=ivars.iener)[:, :] += 0.5 * dt * E_src.ip(-1, buf=1) # ymom_xr[i,j] += 0.5*dt*dens[i,j]*grav U_xr.v(buf=1, n=ivars.iymom)[:, :] += 0.5 * dt * ymom_src.v(buf=1) U_xr.v(buf=1, n=ivars.iener)[:, :] += 0.5 * dt * E_src.v(buf=1) # ymom_yl[i,j] += 0.5*dt*dens[i,j-1]*grav U_yl.v(buf=1, n=ivars.iymom)[:, :] += 0.5 * dt * ymom_src.jp(-1, buf=1) U_yl.v(buf=1, n=ivars.iener)[:, :] += 0.5 * dt * E_src.jp(-1, buf=1) # ymom_yr[i,j] += 0.5*dt*dens[i,j]*grav U_yr.v(buf=1, n=ivars.iymom)[:, :] += 0.5 * dt * ymom_src.v(buf=1) U_yr.v(buf=1, n=ivars.iener)[:, :] += 0.5 * dt * E_src.v(buf=1) #========================================================================= # compute transverse fluxes #========================================================================= tm_riem = tc.timer("riemann") tm_riem.begin() riemann = rp.get_param("compressible.riemann") riemann = "CGF" if riemann == "HLLC": riemannFunc = interface_f.riemann_hllc elif riemann == "CGF": riemannFunc = interface_f.riemann_cgf else: msg.fail("ERROR: Riemann solver undefined") #myg.ng = 5 _fx = riemannFunc(1, myg.qx, myg.qy, myg.ng, ivars.nvar, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, solid.xl, solid.xr, gamma, U_xl, U_xr) _fy = riemannFunc(2, myg.qx, myg.qy, myg.ng, ivars.nvar, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, solid.yl, solid.yr, gamma, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() #========================================================================= # construct the interface values of U now #========================================================================= """ finally, we can construct the state perpendicular to the interface by adding the central difference part to the trasverse flux difference. The states that we represent by indices i,j are shown below (1,2,3,4): j+3/2--+----------+----------+----------+ | | | | | | | | j+1 -+ | | | | | | | | | | | 1: U_xl[i,j,:] = U j+1/2--+----------XXXXXXXXXXXX----------+ i-1/2,j,L | X X | | X X | j -+ 1 X 2 X | 2: U_xr[i,j,:] = U | X X | i-1/2,j,R | X 4 X | j-1/2--+----------XXXXXXXXXXXX----------+ | | 3 | | 3: U_yl[i,j,:] = U | | | | i,j-1/2,L j-1 -+ | | | | | | | | | | | 4: U_yr[i,j,:] = U j-3/2--+----------+----------+----------+ i,j-1/2,R | | | | | | | i-1 i i+1 i-3/2 i-1/2 i+1/2 i+3/2 remember that the fluxes are stored on the left edge, so F_x[i,j,:] = F_x i-1/2, j F_y[i,j,:] = F_y i, j-1/2 """ tm_transverse = tc.timer("transverse flux addition") tm_transverse.begin() dtdx = dt / myg.dx dtdy = dt / myg.dy b = (2, 1) for n in range(ivars.nvar): # U_xl[i,j,:] = U_xl[i,j,:] - 0.5*dt/dy * (F_y[i-1,j+1,:] - F_y[i-1,j,:]) U_xl.v(buf=b, n=n)[:,:] += \ - 0.5*dtdy*(F_y.ip_jp(-1, 1, buf=b, n=n) - F_y.ip(-1, buf=b, n=n)) # U_xr[i,j,:] = U_xr[i,j,:] - 0.5*dt/dy * (F_y[i,j+1,:] - F_y[i,j,:]) U_xr.v(buf=b, n=n)[:,:] += \ - 0.5*dtdy*(F_y.jp(1, buf=b, n=n) - F_y.v(buf=b, n=n)) # U_yl[i,j,:] = U_yl[i,j,:] - 0.5*dt/dx * (F_x[i+1,j-1,:] - F_x[i,j-1,:]) U_yl.v(buf=b, n=n)[:,:] += \ - 0.5*dtdx*(F_x.ip_jp(1, -1, buf=b, n=n) - F_x.jp(-1, buf=b, n=n)) # U_yr[i,j,:] = U_yr[i,j,:] - 0.5*dt/dx * (F_x[i+1,j,:] - F_x[i,j,:]) U_yr.v(buf=b, n=n)[:,:] += \ - 0.5*dtdx*(F_x.ip(1, buf=b, n=n) - F_x.v(buf=b, n=n)) tm_transverse.end() #========================================================================= # construct the fluxes normal to the interfaces #========================================================================= # up until now, F_x and F_y stored the transverse fluxes, now we # overwrite with the fluxes normal to the interfaces tm_riem.begin() _fx = riemannFunc(1, myg.qx, myg.qy, myg.ng, ivars.nvar, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, solid.xl, solid.xr, gamma, U_xl, U_xr) _fy = riemannFunc(2, myg.qx, myg.qy, myg.ng, ivars.nvar, ivars.idens, ivars.ixmom, ivars.iymom, ivars.iener, solid.yl, solid.yr, gamma, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() #========================================================================= # apply artificial viscosity #========================================================================= cvisc = rp.get_param("compressible.cvisc") # myg.ng = 4 # ivars.nvar = 4 _ax, _ay = interface_f.artificial_viscosity(myg.qx, myg.qy, myg.ng, myg.dx, myg.dy, cvisc, u, v) avisco_x = ai.ArrayIndexer(d=_ax, grid=myg) avisco_y = ai.ArrayIndexer(d=_ay, grid=myg) b = (2, 1) for n in range(ivars.nvar): # F_x = F_x + avisco_x * (U(i-1,j) - U(i,j)) var = my_data.get_var_by_index(n) F_x.v(buf=b, n=n)[:,:] += \ avisco_x.v(buf=b)*(var.ip(-1, buf=b) - var.v(buf=b)) # F_y = F_y + avisco_y * (U(i,j-1) - U(i,j)) F_y.v(buf=b, n=n)[:,:] += \ avisco_y.v(buf=b)*(var.jp(-1, buf=b) - var.v(buf=b)) tm_flux.end() return F_x, F_y
def unsplit_fluxes(my_data, rp, dt, scalar_name): r""" Construct the fluxes through the interfaces for the linear advection equation: .. math:: a_t + u a_x + v a_y = 0 We use a second-order (piecewise linear) unsplit Godunov method (following Colella 1990). In the pure advection case, there is no Riemann problem we need to solve -- we just simply do upwinding. So there is only one 'state' at each interface, and the zone the information comes from depends on the sign of the velocity. Our convection is that the fluxes are going to be defined on the left edge of the computational zones:: | | | | | | | | -+------+------+------+------+------+------+-- | i-1 | i | i+1 | a_l,i a_r,i a_l,i+1 a_r,i and a_l,i+1 are computed using the information in zone i,j. Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation dt : float The timestep we are advancing through. scalar_name : str The name of the variable contained in my_data that we are advecting Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ myg = my_data.grid a = my_data.get_var(scalar_name) # get the advection velocities u = rp.get_param("advection.u") v = rp.get_param("advection.v") cx = u*dt/myg.dx cy = v*dt/myg.dy # -------------------------------------------------------------------------- # monotonized central differences # -------------------------------------------------------------------------- limiter = rp.get_param("advection.limiter") ldelta_ax = reconstruction.limit(a, myg, 1, limiter) ldelta_ay = reconstruction.limit(a, myg, 2, limiter) a_x = myg.scratch_array() # upwind if u < 0: # a_x[i,j] = a[i,j] - 0.5*(1.0 + cx)*ldelta_a[i,j] a_x.v(buf=1)[:, :] = a.v(buf=1) - 0.5*(1.0 + cx)*ldelta_ax.v(buf=1) else: # a_x[i,j] = a[i-1,j] + 0.5*(1.0 - cx)*ldelta_a[i-1,j] a_x.v(buf=1)[:, :] = a.ip(-1, buf=1) + 0.5*(1.0 - cx)*ldelta_ax.ip(-1, buf=1) # y-direction a_y = myg.scratch_array() # upwind if v < 0: # a_y[i,j] = a[i,j] - 0.5*(1.0 + cy)*ldelta_a[i,j] a_y.v(buf=1)[:, :] = a.v(buf=1) - 0.5*(1.0 + cy)*ldelta_ay.v(buf=1) else: # a_y[i,j] = a[i,j-1] + 0.5*(1.0 - cy)*ldelta_a[i,j-1] a_y.v(buf=1)[:, :] = a.jp(-1, buf=1) + 0.5*(1.0 - cy)*ldelta_ay.jp(-1, buf=1) # compute the transverse flux differences. The flux is just (u a) # HOTF F_xt = u*a_x F_yt = v*a_y F_x = myg.scratch_array() F_y = myg.scratch_array() # the zone where we grab the transverse flux derivative from # depends on the sign of the advective velocity if u <= 0: mx = 0 else: mx = -1 if v <= 0: my = 0 else: my = -1 dtdx2 = 0.5*dt/myg.dx dtdy2 = 0.5*dt/myg.dy F_x.v(buf=1)[:, :] = u*(a_x.v(buf=1) - dtdy2*(F_yt.ip_jp(mx, 1, buf=1) - F_yt.ip(mx, buf=1))) F_y.v(buf=1)[:, :] = v*(a_y.v(buf=1) - dtdx2*(F_xt.ip_jp(1, my, buf=1) - F_xt.jp(my, buf=1))) return F_x, F_y
def unsplit_fluxes(my_data, rp, dt, scalar_name): """ Construct the fluxes through the interfaces for the linear advection equation: .. math:: a_t + u a_x + v a_y = 0 We use a second-order (piecewise linear) unsplit Godunov method (following Colella 1990). In the pure advection case, there is no Riemann problem we need to solve -- we just simply do upwinding. So there is only one 'state' at each interface, and the zone the information comes from depends on the sign of the velocity. Our convection is that the fluxes are going to be defined on the left edge of the computational zones:: | | | | | | | | -+------+------+------+------+------+------+-- | i-1 | i | i+1 | a_l,i a_r,i a_l,i+1 a_r,i and a_l,i+1 are computed using the information in zone i,j. Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation dt : float The timestep we are advancing through. scalar_name : str The name of the variable contained in my_data that we are advecting Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ myg = my_data.grid a = my_data.get_var(scalar_name) u = my_data.get_var("x-velocity") v = my_data.get_var("y-velocity") cx = u * dt / myg.dx cy = v * dt / myg.dy # -------------------------------------------------------------------------- # monotonized central differences # -------------------------------------------------------------------------- limiter = rp.get_param("advection.limiter") ldelta_ax = reconstruction.limit(a, myg, 1, limiter) ldelta_ay = reconstruction.limit(a, myg, 2, limiter) # upwind in x-direction a_x = myg.scratch_array() shift_x = my_data.get_var("x-shift").astype(int) for index, vel in np.ndenumerate(u.v(buf=1)): if vel < 0: a_x.v(buf=1)[index] = a.ip(shift_x.v(buf=1)[index], buf=1)[index] \ - 0.5*(1.0 + cx.v(buf=1)[index]) \ * ldelta_ax.ip(shift_x.v(buf=1)[index], buf=1)[index] else: a_x.v(buf=1)[index] = a.ip(shift_x.v(buf=1)[index], buf=1)[index] \ + 0.5*(1.0 - cx.v(buf=1)[index]) \ * ldelta_ax.ip(shift_x.v(buf=1)[index], buf=1)[index] # upwind in y-direction a_y = myg.scratch_array() shift_y = my_data.get_var("y-shift").astype(int) for index, vel in np.ndenumerate(v.v(buf=1)): if vel < 0: a_y.v(buf=1)[index] = a.jp(shift_y.v(buf=1)[index], buf=1)[index] \ - 0.5*(1.0 + cy.v(buf=1)[index]) \ * ldelta_ay.jp(shift_y.v(buf=1)[index], buf=1)[index] else: a_y.v(buf=1)[index] = a.jp(shift_y.v(buf=1)[index], buf=1)[index] \ + 0.5*(1.0 - cy.v(buf=1)[index]) \ * ldelta_ay.jp(shift_y.v(buf=1)[index], buf=1)[index] # compute the transverse flux differences. The flux is just (u a) # HOTF F_xt = u * a_x F_yt = v * a_y F_x = myg.scratch_array() F_y = myg.scratch_array() # the zone where we grab the transverse flux derivative from # depends on the sign of the advective velocity dtdx2 = 0.5 * dt / myg.dx dtdy2 = 0.5 * dt / myg.dy for index, vel in np.ndenumerate(u.v(buf=1)): F_x.v(buf=1)[index] = vel * (a_x.v(buf=1)[index] - dtdy2 * (F_yt.ip_jp(shift_x.v(buf=1)[index], 1, buf=1)[index] - F_yt.ip(shift_x.v(buf=1)[index], buf=1)[index])) for index, vel in np.ndenumerate(v.v(buf=1)): F_y.v(buf=1)[index] = vel * (a_y.v(buf=1)[index] - dtdx2 * (F_xt.ip_jp(1, shift_y.v(buf=1)[index], buf=1)[index] - F_xt.jp(shift_y.v(buf=1)[index], buf=1)[index])) return F_x, F_y
def unsplit_fluxes(my_data, rp, dt, scalar_name): """ Construct the fluxes through the interfaces for the linear advection equation: .. math:: a_t + u a_x + v a_y = 0 We use a second-order (piecewise linear) unsplit Godunov method (following Colella 1990). In the pure advection case, there is no Riemann problem we need to solve -- we just simply do upwinding. So there is only one 'state' at each interface, and the zone the information comes from depends on the sign of the velocity. Our convection is that the fluxes are going to be defined on the left edge of the computational zones:: | | | | | | | | -+------+------+------+------+------+------+-- | i-1 | i | i+1 | a_l,i a_r,i a_l,i+1 a_r,i and a_l,i+1 are computed using the information in zone i,j. Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation dt : float The timestep we are advancing through. scalar_name : str The name of the variable contained in my_data that we are advecting Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ myg = my_data.grid a = my_data.get_var(scalar_name) # get the advection velocities u = rp.get_param("advection.u") v = rp.get_param("advection.v") cx = u*dt/myg.dx cy = v*dt/myg.dy #-------------------------------------------------------------------------- # monotonized central differences #-------------------------------------------------------------------------- limiter = rp.get_param("advection.limiter") ldelta_ax = reconstruction.limit(a, myg, 1, limiter) ldelta_ay = reconstruction.limit(a, myg, 2, limiter) a_x = myg.scratch_array() # upwind if u < 0: # a_x[i,j] = a[i,j] - 0.5*(1.0 + cx)*ldelta_a[i,j] a_x.v(buf=1)[:, :] = a.v(buf=1) - 0.5*(1.0 + cx)*ldelta_ax.v(buf=1) else: # a_x[i,j] = a[i-1,j] + 0.5*(1.0 - cx)*ldelta_a[i-1,j] a_x.v(buf=1)[:, :] = a.ip(-1, buf=1) + 0.5*(1.0 - cx)*ldelta_ax.ip(-1, buf=1) # y-direction a_y = myg.scratch_array() # upwind if v < 0: # a_y[i,j] = a[i,j] - 0.5*(1.0 + cy)*ldelta_a[i,j] a_y.v(buf=1)[:, :] = a.v(buf=1) - 0.5*(1.0 + cy)*ldelta_ay.v(buf=1) else: # a_y[i,j] = a[i,j-1] + 0.5*(1.0 - cy)*ldelta_a[i,j-1] a_y.v(buf=1)[:, :] = a.jp(-1, buf=1) + 0.5*(1.0 - cy)*ldelta_ay.jp(-1, buf=1) # compute the transverse flux differences. The flux is just (u a) # HOTF F_xt = u*a_x F_yt = v*a_y F_x = myg.scratch_array() F_y = myg.scratch_array() # the zone where we grab the transverse flux derivative from # depends on the sign of the advective velocity if u <= 0: mx = 0 else: mx = -1 if v <= 0: my = 0 else: my = -1 dtdx2 = 0.5*dt/myg.dx dtdy2 = 0.5*dt/myg.dy F_x.v(buf=1)[:, :] = u*(a_x.v(buf=1) - dtdy2*(F_yt.ip_jp(mx, 1, buf=1) - F_yt.ip(mx, buf=1))) F_y.v(buf=1)[:, :] = v*(a_y.v(buf=1) - dtdx2*(F_xt.ip_jp(1, my, buf=1) - F_xt.jp(my, buf=1))) return F_x, F_y
def evolve(self): """ Evolve the incompressible equations through one timestep. """ u = self.cc_data.get_var("x-velocity") v = self.cc_data.get_var("y-velocity") gradp_x = self.cc_data.get_var("gradp_x") gradp_y = self.cc_data.get_var("gradp_y") phi = self.cc_data.get_var("phi") myg = self.cc_data.grid # --------------------------------------------------------------------- # create the limited slopes of u and v (in both directions) # --------------------------------------------------------------------- limiter = self.rp.get_param("incompressible.limiter") ldelta_ux = reconstruction.limit(u, myg, 1, limiter) ldelta_vx = reconstruction.limit(v, myg, 1, limiter) ldelta_uy = reconstruction.limit(u, myg, 2, limiter) ldelta_vy = reconstruction.limit(v, myg, 2, limiter) # --------------------------------------------------------------------- # get the advective velocities # --------------------------------------------------------------------- """ the advective velocities are the normal velocity through each cell interface, and are defined on the cell edges, in a MAC type staggered form n+1/2 v i,j+1/2 +------+------+ | | n+1/2 | | n+1/2 u + U + u i-1/2,j | i,j | i+1/2,j | | +------+------+ n+1/2 v i,j-1/2 """ # this returns u on x-interfaces and v on y-interfaces. These # constitute the MAC grid if self.verbose > 0: print(" making MAC velocities") _um, _vm = incomp_interface.mac_vels(myg.ng, myg.dx, myg.dy, self.dt, u, v, ldelta_ux, ldelta_vx, ldelta_uy, ldelta_vy, gradp_x, gradp_y) u_MAC = ai.ArrayIndexer(d=_um, grid=myg) v_MAC = ai.ArrayIndexer(d=_vm, grid=myg) # --------------------------------------------------------------------- # do a MAC projection ot make the advective velocities divergence # free # --------------------------------------------------------------------- # we will solve L phi = D U^MAC, where phi is cell centered, and # U^MAC is the MAC-type staggered grid of the advective # velocities. if self.verbose > 0: print(" MAC projection") # create the multigrid object mg = MG.CellCenterMG2d(myg.nx, myg.ny, xl_BC_type="periodic", xr_BC_type="periodic", yl_BC_type="periodic", yr_BC_type="periodic", xmin=myg.xmin, xmax=myg.xmax, ymin=myg.ymin, ymax=myg.ymax, verbose=0) # first compute divU divU = mg.soln_grid.scratch_array() # MAC velocities are edge-centered. divU is cell-centered. divU.v()[:, :] = \ (u_MAC.ip(1) - u_MAC.v())/myg.dx + (v_MAC.jp(1) - v_MAC.v())/myg.dy # solve the Poisson problem mg.init_zeros() mg.init_RHS(divU) mg.solve(rtol=1.e-12) # update the normal velocities with the pressure gradient -- these # constitute our advective velocities phi_MAC = self.cc_data.get_var("phi-MAC") solution = mg.get_solution() phi_MAC.v(buf=1)[:, :] = solution.v(buf=1) # we need the MAC velocities on all edges of the computational domain b = (0, 1, 0, 0) u_MAC.v(buf=b)[:, :] -= (phi_MAC.v(buf=b) - phi_MAC.ip(-1, buf=b))/myg.dx b = (0, 0, 0, 1) v_MAC.v(buf=b)[:, :] -= (phi_MAC.v(buf=b) - phi_MAC.jp(-1, buf=b))/myg.dy # --------------------------------------------------------------------- # recompute the interface states, using the advective velocity # from above # --------------------------------------------------------------------- if self.verbose > 0: print(" making u, v edge states") _ux, _vx, _uy, _vy = \ incomp_interface.states(myg.ng, myg.dx, myg.dy, self.dt, u, v, ldelta_ux, ldelta_vx, ldelta_uy, ldelta_vy, gradp_x, gradp_y, u_MAC, v_MAC) u_xint = ai.ArrayIndexer(d=_ux, grid=myg) v_xint = ai.ArrayIndexer(d=_vx, grid=myg) u_yint = ai.ArrayIndexer(d=_uy, grid=myg) v_yint = ai.ArrayIndexer(d=_vy, grid=myg) # --------------------------------------------------------------------- # update U to get the provisional velocity field # --------------------------------------------------------------------- if self.verbose > 0: print(" doing provisional update of u, v") # compute (U.grad)U # we want u_MAC U_x + v_MAC U_y advect_x = myg.scratch_array() advect_y = myg.scratch_array() # u u_x + v u_y advect_x.v()[:, :] = \ 0.5*(u_MAC.v() + u_MAC.ip(1))*(u_xint.ip(1) - u_xint.v())/myg.dx + \ 0.5*(v_MAC.v() + v_MAC.jp(1))*(u_yint.jp(1) - u_yint.v())/myg.dy # u v_x + v v_y advect_y.v()[:, :] = \ 0.5*(u_MAC.v() + u_MAC.ip(1))*(v_xint.ip(1) - v_xint.v())/myg.dx + \ 0.5*(v_MAC.v() + v_MAC.jp(1))*(v_yint.jp(1) - v_yint.v())/myg.dy proj_type = self.rp.get_param("incompressible.proj_type") if proj_type == 1: u[:, :] -= (self.dt*advect_x[:, :] + self.dt*gradp_x[:, :]) v[:, :] -= (self.dt*advect_y[:, :] + self.dt*gradp_y[:, :]) elif proj_type == 2: u[:, :] -= self.dt*advect_x[:, :] v[:, :] -= self.dt*advect_y[:, :] self.cc_data.fill_BC("x-velocity") self.cc_data.fill_BC("y-velocity") # --------------------------------------------------------------------- # project the final velocity # --------------------------------------------------------------------- # now we solve L phi = D (U* /dt) if self.verbose > 0: print(" final projection") # create the multigrid object mg = MG.CellCenterMG2d(myg.nx, myg.ny, xl_BC_type="periodic", xr_BC_type="periodic", yl_BC_type="periodic", yr_BC_type="periodic", xmin=myg.xmin, xmax=myg.xmax, ymin=myg.ymin, ymax=myg.ymax, verbose=0) # first compute divU # u/v are cell-centered, divU is cell-centered divU.v()[:, :] = \ 0.5*(u.ip(1) - u.ip(-1))/myg.dx + 0.5*(v.jp(1) - v.jp(-1))/myg.dy mg.init_RHS(divU/self.dt) # use the old phi as our initial guess phiGuess = mg.soln_grid.scratch_array() phiGuess.v(buf=1)[:, :] = phi.v(buf=1) mg.init_solution(phiGuess) # solve mg.solve(rtol=1.e-12) # store the solution phi[:, :] = mg.get_solution(grid=myg) # compute the cell-centered gradient of p and update the velocities # this differs depending on what we projected. gradphi_x, gradphi_y = mg.get_solution_gradient(grid=myg) # u = u - grad_x phi dt u[:, :] -= self.dt*gradphi_x v[:, :] -= self.dt*gradphi_y # store gradp for the next step if proj_type == 1: gradp_x[:, :] += gradphi_x[:, :] gradp_y[:, :] += gradphi_y[:, :] elif proj_type == 2: gradp_x[:, :] = gradphi_x[:, :] gradp_y[:, :] = gradphi_y[:, :] self.cc_data.fill_BC("x-velocity") self.cc_data.fill_BC("y-velocity") if self.particles is not None: self.particles.update_particles(self.dt) # increment the time if not self.in_preevolve: self.cc_data.t += self.dt self.n += 1
def evolve(self): """ Evolve the low Mach system through one timestep. """ rho = self.cc_data.get_var("density") u = self.cc_data.get_var("x-velocity") v = self.cc_data.get_var("y-velocity") gradp_x = self.cc_data.get_var("gradp_x") gradp_y = self.cc_data.get_var("gradp_y") # note: the base state quantities do not have valid ghost cells beta0 = self.base["beta0"] beta0_edges = self.base["beta0-edges"] rho0 = self.base["rho0"] phi = self.cc_data.get_var("phi") myg = self.cc_data.grid #--------------------------------------------------------------------- # create the limited slopes of rho, u and v (in both directions) #--------------------------------------------------------------------- limiter = self.rp.get_param("lm-atmosphere.limiter") ldelta_rx = reconstruction.limit(rho, myg, 1, limiter) ldelta_ux = reconstruction.limit(u, myg, 1, limiter) ldelta_vx = reconstruction.limit(v, myg, 1, limiter) ldelta_ry = reconstruction.limit(rho, myg, 2, limiter) ldelta_uy = reconstruction.limit(u, myg, 2, limiter) ldelta_vy = reconstruction.limit(v, myg, 2, limiter) #--------------------------------------------------------------------- # get the advective velocities #--------------------------------------------------------------------- """ the advective velocities are the normal velocity through each cell interface, and are defined on the cell edges, in a MAC type staggered form n+1/2 v i,j+1/2 +------+------+ | | n+1/2 | | n+1/2 u + U + u i-1/2,j | i,j | i+1/2,j | | +------+------+ n+1/2 v i,j-1/2 """ # this returns u on x-interfaces and v on y-interfaces. These # constitute the MAC grid if self.verbose > 0: print(" making MAC velocities") # create the coefficient to the grad (pi/beta) term coeff = self.aux_data.get_var("coeff") coeff.v()[:, :] = 1.0 / rho.v() coeff.v()[:, :] = coeff.v() * beta0.v2d() self.aux_data.fill_BC("coeff") # create the source term source = self.aux_data.get_var("source_y") g = self.rp.get_param("lm-atmosphere.grav") rhoprime = self.make_prime(rho, rho0) source.v()[:, :] = rhoprime.v() * g / rho.v() self.aux_data.fill_BC("source_y") _um, _vm = lm_interface_f.mac_vels(myg.qx, myg.qy, myg.ng, myg.dx, myg.dy, self.dt, u, v, ldelta_ux, ldelta_vx, ldelta_uy, ldelta_vy, coeff * gradp_x, coeff * gradp_y, source) u_MAC = ai.ArrayIndexer(d=_um, grid=myg) v_MAC = ai.ArrayIndexer(d=_vm, grid=myg) #--------------------------------------------------------------------- # do a MAC projection to make the advective velocities divergence # free #--------------------------------------------------------------------- # we will solve D (beta_0^2/rho) G phi = D (beta_0 U^MAC), where # phi is cell centered, and U^MAC is the MAC-type staggered # grid of the advective velocities. if self.verbose > 0: print(" MAC projection") # create the coefficient array: beta0**2/rho # MZ!!!! probably don't need the buf here coeff.v(buf=1)[:, :] = 1.0 / rho.v(buf=1) coeff.v(buf=1)[:, :] = coeff.v(buf=1) * beta0.v2d(buf=1)**2 # create the multigrid object mg = vcMG.VarCoeffCCMG2d(myg.nx, myg.ny, xl_BC_type=self.cc_data.BCs["phi-MAC"].xlb, xr_BC_type=self.cc_data.BCs["phi-MAC"].xrb, yl_BC_type=self.cc_data.BCs["phi-MAC"].ylb, yr_BC_type=self.cc_data.BCs["phi-MAC"].yrb, xmin=myg.xmin, xmax=myg.xmax, ymin=myg.ymin, ymax=myg.ymax, coeffs=coeff, coeffs_bc=self.cc_data.BCs["density"], verbose=0) # first compute div{beta_0 U} div_beta_U = mg.soln_grid.scratch_array() # MAC velocities are edge-centered. div{beta_0 U} is cell-centered. div_beta_U.v()[:,:] = \ beta0.v2d()*(u_MAC.ip(1) - u_MAC.v())/myg.dx + \ (beta0_edges.v2dp(1)*v_MAC.jp(1) - beta0_edges.v2d()*v_MAC.v())/myg.dy # solve the Poisson problem mg.init_RHS(div_beta_U) mg.solve(rtol=1.e-12) # update the normal velocities with the pressure gradient -- these # constitute our advective velocities. Note that what we actually # solved for here is phi/beta_0 phi_MAC = self.cc_data.get_var("phi-MAC") phi_MAC[:, :] = mg.get_solution(grid=myg) coeff = self.aux_data.get_var("coeff") coeff.v()[:, :] = 1.0 / rho.v() coeff.v()[:, :] = coeff.v() * beta0.v2d() self.aux_data.fill_BC("coeff") coeff_x = myg.scratch_array() b = (3, 1, 0, 0) # this seems more than we need coeff_x.v(buf=b)[:, :] = 0.5 * (coeff.ip(-1, buf=b) + coeff.v(buf=b)) coeff_y = myg.scratch_array() b = (0, 0, 3, 1) coeff_y.v(buf=b)[:, :] = 0.5 * (coeff.jp(-1, buf=b) + coeff.v(buf=b)) # we need the MAC velocities on all edges of the computational domain # here we do U = U - (beta_0/rho) grad (phi/beta_0) b = (0, 1, 0, 0) u_MAC.v(buf=b)[:,:] -= \ coeff_x.v(buf=b)*(phi_MAC.v(buf=b) - phi_MAC.ip(-1, buf=b))/myg.dx b = (0, 0, 0, 1) v_MAC.v(buf=b)[:,:] -= \ coeff_y.v(buf=b)*(phi_MAC.v(buf=b) - phi_MAC.jp(-1, buf=b))/myg.dy #--------------------------------------------------------------------- # predict rho to the edges and do its conservative update #--------------------------------------------------------------------- _rx, _ry = lm_interface_f.rho_states(myg.qx, myg.qy, myg.ng, myg.dx, myg.dy, self.dt, rho, u_MAC, v_MAC, ldelta_rx, ldelta_ry) rho_xint = ai.ArrayIndexer(d=_rx, grid=myg) rho_yint = ai.ArrayIndexer(d=_ry, grid=myg) rho_old = rho.copy() rho.v()[:, :] -= self.dt * ( # (rho u)_x (rho_xint.ip(1) * u_MAC.ip(1) - rho_xint.v() * u_MAC.v()) / myg.dx + # (rho v)_y (rho_yint.jp(1) * v_MAC.jp(1) - rho_yint.v() * v_MAC.v()) / myg.dy) self.cc_data.fill_BC("density") # update eint as a diagnostic eint = self.cc_data.get_var("eint") gamma = self.rp.get_param("eos.gamma") eint.v()[:, :] = self.base["p0"].v2d() / (gamma - 1.0) / rho.v() #--------------------------------------------------------------------- # recompute the interface states, using the advective velocity # from above #--------------------------------------------------------------------- if self.verbose > 0: print(" making u, v edge states") coeff = self.aux_data.get_var("coeff") coeff.v()[:, :] = 2.0 / (rho.v() + rho_old.v()) coeff.v()[:, :] = coeff.v() * beta0.v2d() self.aux_data.fill_BC("coeff") _ux, _vx, _uy, _vy = \ lm_interface_f.states(myg.qx, myg.qy, myg.ng, myg.dx, myg.dy, self.dt, u, v, ldelta_ux, ldelta_vx, ldelta_uy, ldelta_vy, coeff*gradp_x, coeff*gradp_y, source, u_MAC, v_MAC) u_xint = ai.ArrayIndexer(d=_ux, grid=myg) v_xint = ai.ArrayIndexer(d=_vx, grid=myg) u_yint = ai.ArrayIndexer(d=_uy, grid=myg) v_yint = ai.ArrayIndexer(d=_vy, grid=myg) #--------------------------------------------------------------------- # update U to get the provisional velocity field #--------------------------------------------------------------------- if self.verbose > 0: print(" doing provisional update of u, v") # compute (U.grad)U # we want u_MAC U_x + v_MAC U_y advect_x = myg.scratch_array() advect_y = myg.scratch_array() advect_x.v()[:,:] = \ 0.5*(u_MAC.v() + u_MAC.ip(1))*(u_xint.ip(1) - u_xint.v())/myg.dx +\ 0.5*(v_MAC.v() + v_MAC.jp(1))*(u_yint.jp(1) - u_yint.v())/myg.dy advect_y.v()[:,:] = \ 0.5*(u_MAC.v() + u_MAC.ip(1))*(v_xint.ip(1) - v_xint.v())/myg.dx +\ 0.5*(v_MAC.v() + v_MAC.jp(1))*(v_yint.jp(1) - v_yint.v())/myg.dy proj_type = self.rp.get_param("lm-atmosphere.proj_type") if proj_type == 1: u.v()[:, :] -= (self.dt * advect_x.v() + self.dt * gradp_x.v()) v.v()[:, :] -= (self.dt * advect_y.v() + self.dt * gradp_y.v()) elif proj_type == 2: u.v()[:, :] -= self.dt * advect_x.v() v.v()[:, :] -= self.dt * advect_y.v() # add the gravitational source rho_half = 0.5 * (rho + rho_old) rhoprime = self.make_prime(rho_half, rho0) source[:, :] = (rhoprime * g / rho_half) self.aux_data.fill_BC("source_y") v[:, :] += self.dt * source self.cc_data.fill_BC("x-velocity") self.cc_data.fill_BC("y-velocity") if self.verbose > 0: print("min/max rho = {}, {}".format(self.cc_data.min("density"), self.cc_data.max("density"))) print( "min/max u = {}, {}".format(self.cc_data.min("x-velocity"), self.cc_data.max("x-velocity"))) print( "min/max v = {}, {}".format(self.cc_data.min("y-velocity"), self.cc_data.max("y-velocity"))) #--------------------------------------------------------------------- # project the final velocity #--------------------------------------------------------------------- # now we solve L phi = D (U* /dt) if self.verbose > 0: print(" final projection") # create the coefficient array: beta0**2/rho coeff = 1.0 / rho coeff.v()[:, :] = coeff.v() * beta0.v2d()**2 # create the multigrid object mg = vcMG.VarCoeffCCMG2d(myg.nx, myg.ny, xl_BC_type=self.cc_data.BCs["phi"].xlb, xr_BC_type=self.cc_data.BCs["phi"].xrb, yl_BC_type=self.cc_data.BCs["phi"].ylb, yr_BC_type=self.cc_data.BCs["phi"].yrb, xmin=myg.xmin, xmax=myg.xmax, ymin=myg.ymin, ymax=myg.ymax, coeffs=coeff, coeffs_bc=self.cc_data.BCs["density"], verbose=0) # first compute div{beta_0 U} # u/v are cell-centered, divU is cell-centered div_beta_U.v()[:,:] = \ 0.5*beta0.v2d()*(u.ip(1) - u.ip(-1))/myg.dx + \ 0.5*(beta0.v2dp(1)*v.jp(1) - beta0.v2dp(-1)*v.jp(-1))/myg.dy mg.init_RHS(div_beta_U / self.dt) # use the old phi as our initial guess phiGuess = mg.soln_grid.scratch_array() phiGuess.v(buf=1)[:, :] = phi.v(buf=1) mg.init_solution(phiGuess) # solve mg.solve(rtol=1.e-12) # store the solution in our self.cc_data object -- include a single # ghostcell phi[:, :] = mg.get_solution(grid=myg) # get the cell-centered gradient of p and update the velocities # this differs depending on what we projected. gradphi_x, gradphi_y = mg.get_solution_gradient(grid=myg) # U = U - (beta_0/rho) grad (phi/beta_0) coeff = 1.0 / rho coeff.v()[:, :] = coeff.v() * beta0.v2d() u.v()[:, :] -= self.dt * coeff.v() * gradphi_x.v() v.v()[:, :] -= self.dt * coeff.v() * gradphi_y.v() # store gradp for the next step if proj_type == 1: gradp_x.v()[:, :] += gradphi_x.v() gradp_y.v()[:, :] += gradphi_y.v() elif proj_type == 2: gradp_x.v()[:, :] = gradphi_x.v() gradp_y.v()[:, :] = gradphi_y.v() self.cc_data.fill_BC("x-velocity") self.cc_data.fill_BC("y-velocity") self.cc_data.fill_BC("gradp_x") self.cc_data.fill_BC("gradp_y") # increment the time if not self.in_preevolve: self.cc_data.t += self.dt self.n += 1
def fluxes(my_data, rp, vars, solid, tc): """ unsplitFluxes returns the fluxes through the x and y interfaces by doing an unsplit reconstruction of the interface values and then solving the Riemann problem through all the interfaces at once currently we assume a gamma-law EOS Parameters ---------- my_data : CellCenterData2d object The data object containing the grid and advective scalar that we are advecting. rp : RuntimeParameters object The runtime parameters for the simulation vars : Variables object The Variables object that tells us which indices refer to which variables tc : TimerCollection object The timers we are using to profile Returns ------- out : ndarray, ndarray The fluxes on the x- and y-interfaces """ tm_flux = tc.timer("unsplitFluxes") tm_flux.begin() myg = my_data.grid gamma = rp.get_param("eos.gamma") #========================================================================= # compute the primitive variables #========================================================================= # Q = (rho, u, v, p) dens = my_data.get_var("density") xmom = my_data.get_var("x-momentum") ymom = my_data.get_var("y-momentum") ener = my_data.get_var("energy") r, u, v, p = my_data.get_var("primitive") smallp = 1.e-10 p = p.clip(smallp) # apply a floor to the pressure #========================================================================= # compute the flattening coefficients #========================================================================= # there is a single flattening coefficient (xi) for all directions use_flattening = rp.get_param("compressible.use_flattening") if use_flattening: delta = rp.get_param("compressible.delta") z0 = rp.get_param("compressible.z0") z1 = rp.get_param("compressible.z1") xi_x = reconstruction_f.flatten(1, p, u, myg.qx, myg.qy, myg.ng, smallp, delta, z0, z1) xi_y = reconstruction_f.flatten(2, p, v, myg.qx, myg.qy, myg.ng, smallp, delta, z0, z1) xi = reconstruction_f.flatten_multid(xi_x, xi_y, p, myg.qx, myg.qy, myg.ng) else: xi = 1.0 # monotonized central differences in x-direction tm_limit = tc.timer("limiting") tm_limit.begin() limiter = rp.get_param("compressible.limiter") ldelta_rx = xi * reconstruction.limit(r, myg, 1, limiter) ldelta_ux = xi * reconstruction.limit(u, myg, 1, limiter) ldelta_vx = xi * reconstruction.limit(v, myg, 1, limiter) ldelta_px = xi * reconstruction.limit(p, myg, 1, limiter) # monotonized central differences in y-direction ldelta_ry = xi * reconstruction.limit(r, myg, 2, limiter) ldelta_uy = xi * reconstruction.limit(u, myg, 2, limiter) ldelta_vy = xi * reconstruction.limit(v, myg, 2, limiter) ldelta_py = xi * reconstruction.limit(p, myg, 2, limiter) tm_limit.end() #========================================================================= # x-direction #========================================================================= # left and right primitive variable states tm_states = tc.timer("interfaceStates") tm_states.begin() V_l = myg.scratch_array(vars.nvar) V_r = myg.scratch_array(vars.nvar) V_l.ip(1, n=vars.irho, buf=2)[:, :] = r.v(buf=2) + 0.5 * ldelta_rx.v(buf=2) V_r.v(n=vars.irho, buf=2)[:, :] = r.v(buf=2) - 0.5 * ldelta_rx.v(buf=2) V_l.ip(1, n=vars.iu, buf=2)[:, :] = u.v(buf=2) + 0.5 * ldelta_ux.v(buf=2) V_r.v(n=vars.iu, buf=2)[:, :] = u.v(buf=2) - 0.5 * ldelta_ux.v(buf=2) V_l.ip(1, n=vars.iv, buf=2)[:, :] = v.v(buf=2) + 0.5 * ldelta_vx.v(buf=2) V_r.v(n=vars.iv, buf=2)[:, :] = v.v(buf=2) - 0.5 * ldelta_vx.v(buf=2) V_l.ip(1, n=vars.ip, buf=2)[:, :] = p.v(buf=2) + 0.5 * ldelta_px.v(buf=2) V_r.v(n=vars.ip, buf=2)[:, :] = p.v(buf=2) - 0.5 * ldelta_px.v(buf=2) tm_states.end() # transform interface states back into conserved variables U_xl = myg.scratch_array(vars.nvar) U_xr = myg.scratch_array(vars.nvar) U_xl[:, :, vars.idens] = V_l[:, :, vars.irho] U_xl[:, :, vars.ixmom] = V_l[:, :, vars.irho] * V_l[:, :, vars.iu] U_xl[:, :, vars.iymom] = V_l[:, :, vars.irho] * V_l[:, :, vars.iv] U_xl[:,:,vars.iener] = eos.rhoe(gamma, V_l[:,:,vars.ip]) + \ 0.5*V_l[:,:,vars.irho]*(V_l[:,:,vars.iu]**2 + V_l[:,:,vars.iv]**2) U_xr[:, :, vars.idens] = V_r[:, :, vars.irho] U_xr[:, :, vars.ixmom] = V_r[:, :, vars.irho] * V_r[:, :, vars.iu] U_xr[:, :, vars.iymom] = V_r[:, :, vars.irho] * V_r[:, :, vars.iv] U_xr[:,:,vars.iener] = eos.rhoe(gamma, V_r[:,:,vars.ip]) + \ 0.5*V_r[:,:,vars.irho]*(V_r[:,:,vars.iu]**2 + V_r[:,:,vars.iv]**2) #========================================================================= # y-direction #========================================================================= # left and right primitive variable states tm_states.begin() V_l.jp(1, n=vars.irho, buf=2)[:, :] = r.v(buf=2) + 0.5 * ldelta_ry.v(buf=2) V_r.v(n=vars.irho, buf=2)[:, :] = r.v(buf=2) - 0.5 * ldelta_ry.v(buf=2) V_l.jp(1, n=vars.iu, buf=2)[:, :] = u.v(buf=2) + 0.5 * ldelta_uy.v(buf=2) V_r.v(n=vars.iu, buf=2)[:, :] = u.v(buf=2) - 0.5 * ldelta_uy.v(buf=2) V_l.jp(1, n=vars.iv, buf=2)[:, :] = v.v(buf=2) + 0.5 * ldelta_vy.v(buf=2) V_r.v(n=vars.iv, buf=2)[:, :] = v.v(buf=2) - 0.5 * ldelta_vy.v(buf=2) V_l.jp(1, n=vars.ip, buf=2)[:, :] = p.v(buf=2) + 0.5 * ldelta_py.v(buf=2) V_r.v(n=vars.ip, buf=2)[:, :] = p.v(buf=2) - 0.5 * ldelta_py.v(buf=2) tm_states.end() # transform interface states back into conserved variables U_yl = myg.scratch_array(vars.nvar) U_yr = myg.scratch_array(vars.nvar) U_yl[:, :, vars.idens] = V_l[:, :, vars.irho] U_yl[:, :, vars.ixmom] = V_l[:, :, vars.irho] * V_l[:, :, vars.iu] U_yl[:, :, vars.iymom] = V_l[:, :, vars.irho] * V_l[:, :, vars.iv] U_yl[:,:,vars.iener] = eos.rhoe(gamma, V_l[:,:,vars.ip]) + \ 0.5*V_l[:,:,vars.irho]*(V_l[:,:,vars.iu]**2 + V_l[:,:,vars.iv]**2) U_yr[:, :, vars.idens] = V_r[:, :, vars.irho] U_yr[:, :, vars.ixmom] = V_r[:, :, vars.irho] * V_r[:, :, vars.iu] U_yr[:, :, vars.iymom] = V_r[:, :, vars.irho] * V_r[:, :, vars.iv] U_yr[:,:,vars.iener] = eos.rhoe(gamma, V_r[:,:,vars.ip]) + \ 0.5*V_r[:,:,vars.irho]*(V_r[:,:,vars.iu]**2 + V_r[:,:,vars.iv]**2) #========================================================================= # construct the fluxes normal to the interfaces #========================================================================= tm_riem = tc.timer("Riemann") tm_riem.begin() riemann = rp.get_param("compressible.riemann") if riemann == "HLLC": riemannFunc = interface_f.riemann_hllc elif riemann == "CGF": riemannFunc = interface_f.riemann_cgf else: msg.fail("ERROR: Riemann solver undefined") _fx = riemannFunc(1, myg.qx, myg.qy, myg.ng, vars.nvar, vars.idens, vars.ixmom, vars.iymom, vars.iener, solid.xl, solid.xr, gamma, U_xl, U_xr) _fy = riemannFunc(2, myg.qx, myg.qy, myg.ng, vars.nvar, vars.idens, vars.ixmom, vars.iymom, vars.iener, solid.yl, solid.yr, gamma, U_yl, U_yr) F_x = ai.ArrayIndexer(d=_fx, grid=myg) F_y = ai.ArrayIndexer(d=_fy, grid=myg) tm_riem.end() #========================================================================= # apply artificial viscosity #========================================================================= cvisc = rp.get_param("compressible.cvisc") _ax, _ay = interface_f.artificial_viscosity(myg.qx, myg.qy, myg.ng, myg.dx, myg.dy, cvisc, u, v) avisco_x = ai.ArrayIndexer(d=_ax, grid=myg) avisco_y = ai.ArrayIndexer(d=_ay, grid=myg) b = (2, 1) # F_x = F_x + avisco_x * (U(i-1,j) - U(i,j)) F_x.v(buf=b, n=vars.idens)[:,:] += \ avisco_x.v(buf=b)*(dens.ip(-1, buf=b) - dens.v(buf=b)) F_x.v(buf=b, n=vars.ixmom)[:,:] += \ avisco_x.v(buf=b)*(xmom.ip(-1, buf=b) - xmom.v(buf=b)) F_x.v(buf=b, n=vars.iymom)[:,:] += \ avisco_x.v(buf=b)*(ymom.ip(-1, buf=b) - ymom.v(buf=b)) F_x.v(buf=b, n=vars.iener)[:,:] += \ avisco_x.v(buf=b)*(ener.ip(-1, buf=b) - ener.v(buf=b)) # F_y = F_y + avisco_y * (U(i,j-1) - U(i,j)) F_y.v(buf=b, n=vars.idens)[:,:] += \ avisco_y.v(buf=b)*(dens.jp(-1, buf=b) - dens.v(buf=b)) F_y.v(buf=b, n=vars.ixmom)[:,:] += \ avisco_y.v(buf=b)*(xmom.jp(-1, buf=b) - xmom.v(buf=b)) F_y.v(buf=b, n=vars.iymom)[:,:] += \ avisco_y.v(buf=b)*(ymom.jp(-1, buf=b) - ymom.v(buf=b)) F_y.v(buf=b, n=vars.iener)[:,:] += \ avisco_y.v(buf=b)*(ener.jp(-1, buf=b) - ener.v(buf=b)) tm_flux.end() return F_x, F_y
def evolve(self): """ Evolve the low Mach system through one timestep. """ rho = self.cc_data.get_var("density") u = self.cc_data.get_var("x-velocity") v = self.cc_data.get_var("y-velocity") gradp_x = self.cc_data.get_var("gradp_x") gradp_y = self.cc_data.get_var("gradp_y") # note: the base state quantities do not have valid ghost cells beta0 = self.base["beta0"] beta0_edges = self.base["beta0-edges"] rho0 = self.base["rho0"] phi = self.cc_data.get_var("phi") myg = self.cc_data.grid # --------------------------------------------------------------------- # create the limited slopes of rho, u and v (in both directions) # --------------------------------------------------------------------- limiter = self.rp.get_param("lm-atmosphere.limiter") ldelta_rx = reconstruction.limit(rho, myg, 1, limiter) ldelta_ux = reconstruction.limit(u, myg, 1, limiter) ldelta_vx = reconstruction.limit(v, myg, 1, limiter) ldelta_ry = reconstruction.limit(rho, myg, 2, limiter) ldelta_uy = reconstruction.limit(u, myg, 2, limiter) ldelta_vy = reconstruction.limit(v, myg, 2, limiter) # --------------------------------------------------------------------- # get the advective velocities # --------------------------------------------------------------------- """ the advective velocities are the normal velocity through each cell interface, and are defined on the cell edges, in a MAC type staggered form n+1/2 v i,j+1/2 +------+------+ | | n+1/2 | | n+1/2 u + U + u i-1/2,j | i,j | i+1/2,j | | +------+------+ n+1/2 v i,j-1/2 """ # this returns u on x-interfaces and v on y-interfaces. These # constitute the MAC grid if self.verbose > 0: print(" making MAC velocities") # create the coefficient to the grad (pi/beta) term coeff = self.aux_data.get_var("coeff") coeff.v()[:, :] = 1.0/rho.v() coeff.v()[:, :] = coeff.v()*beta0.v2d() self.aux_data.fill_BC("coeff") # create the source term source = self.aux_data.get_var("source_y") g = self.rp.get_param("lm-atmosphere.grav") rhoprime = self.make_prime(rho, rho0) source.v()[:, :] = rhoprime.v()*g/rho.v() self.aux_data.fill_BC("source_y") _um, _vm = lm_interface.mac_vels(myg.ng, myg.dx, myg.dy, self.dt, u, v, ldelta_ux, ldelta_vx, ldelta_uy, ldelta_vy, coeff*gradp_x, coeff*gradp_y, source) u_MAC = ai.ArrayIndexer(d=_um, grid=myg) v_MAC = ai.ArrayIndexer(d=_vm, grid=myg) # --------------------------------------------------------------------- # do a MAC projection to make the advective velocities divergence # free # --------------------------------------------------------------------- # we will solve D (beta_0^2/rho) G phi = D (beta_0 U^MAC), where # phi is cell centered, and U^MAC is the MAC-type staggered # grid of the advective velocities. if self.verbose > 0: print(" MAC projection") # create the coefficient array: beta0**2/rho # MZ!!!! probably don't need the buf here coeff.v(buf=1)[:, :] = 1.0/rho.v(buf=1) coeff.v(buf=1)[:, :] = coeff.v(buf=1)*beta0.v2d(buf=1)**2 # create the multigrid object mg = vcMG.VarCoeffCCMG2d(myg.nx, myg.ny, xl_BC_type=self.cc_data.BCs["phi-MAC"].xlb, xr_BC_type=self.cc_data.BCs["phi-MAC"].xrb, yl_BC_type=self.cc_data.BCs["phi-MAC"].ylb, yr_BC_type=self.cc_data.BCs["phi-MAC"].yrb, xmin=myg.xmin, xmax=myg.xmax, ymin=myg.ymin, ymax=myg.ymax, coeffs=coeff, coeffs_bc=self.cc_data.BCs["density"], verbose=0) # first compute div{beta_0 U} div_beta_U = mg.soln_grid.scratch_array() # MAC velocities are edge-centered. div{beta_0 U} is cell-centered. div_beta_U.v()[:, :] = \ beta0.v2d()*(u_MAC.ip(1) - u_MAC.v())/myg.dx + \ (beta0_edges.v2dp(1)*v_MAC.jp(1) - beta0_edges.v2d()*v_MAC.v())/myg.dy # solve the Poisson problem mg.init_RHS(div_beta_U) mg.solve(rtol=1.e-12) # update the normal velocities with the pressure gradient -- these # constitute our advective velocities. Note that what we actually # solved for here is phi/beta_0 phi_MAC = self.cc_data.get_var("phi-MAC") phi_MAC[:, :] = mg.get_solution(grid=myg) coeff = self.aux_data.get_var("coeff") coeff.v()[:, :] = 1.0/rho.v() coeff.v()[:, :] = coeff.v()*beta0.v2d() self.aux_data.fill_BC("coeff") coeff_x = myg.scratch_array() b = (3, 1, 0, 0) # this seems more than we need coeff_x.v(buf=b)[:, :] = 0.5*(coeff.ip(-1, buf=b) + coeff.v(buf=b)) coeff_y = myg.scratch_array() b = (0, 0, 3, 1) coeff_y.v(buf=b)[:, :] = 0.5*(coeff.jp(-1, buf=b) + coeff.v(buf=b)) # we need the MAC velocities on all edges of the computational domain # here we do U = U - (beta_0/rho) grad (phi/beta_0) b = (0, 1, 0, 0) u_MAC.v(buf=b)[:, :] -= \ coeff_x.v(buf=b)*(phi_MAC.v(buf=b) - phi_MAC.ip(-1, buf=b))/myg.dx b = (0, 0, 0, 1) v_MAC.v(buf=b)[:, :] -= \ coeff_y.v(buf=b)*(phi_MAC.v(buf=b) - phi_MAC.jp(-1, buf=b))/myg.dy # --------------------------------------------------------------------- # predict rho to the edges and do its conservative update # --------------------------------------------------------------------- _rx, _ry = lm_interface.rho_states(myg.ng, myg.dx, myg.dy, self.dt, rho, u_MAC, v_MAC, ldelta_rx, ldelta_ry) rho_xint = ai.ArrayIndexer(d=_rx, grid=myg) rho_yint = ai.ArrayIndexer(d=_ry, grid=myg) rho_old = rho.copy() rho.v()[:, :] -= self.dt*( # (rho u)_x (rho_xint.ip(1)*u_MAC.ip(1) - rho_xint.v()*u_MAC.v())/myg.dx + # (rho v)_y (rho_yint.jp(1)*v_MAC.jp(1) - rho_yint.v()*v_MAC.v())/myg.dy) self.cc_data.fill_BC("density") # update eint as a diagnostic eint = self.cc_data.get_var("eint") gamma = self.rp.get_param("eos.gamma") eint.v()[:, :] = self.base["p0"].v2d()/(gamma - 1.0)/rho.v() # --------------------------------------------------------------------- # recompute the interface states, using the advective velocity # from above # --------------------------------------------------------------------- if self.verbose > 0: print(" making u, v edge states") coeff = self.aux_data.get_var("coeff") coeff.v()[:, :] = 2.0/(rho.v() + rho_old.v()) coeff.v()[:, :] = coeff.v()*beta0.v2d() self.aux_data.fill_BC("coeff") _ux, _vx, _uy, _vy = \ lm_interface.states(myg.ng, myg.dx, myg.dy, self.dt, u, v, ldelta_ux, ldelta_vx, ldelta_uy, ldelta_vy, coeff*gradp_x, coeff*gradp_y, source, u_MAC, v_MAC) u_xint = ai.ArrayIndexer(d=_ux, grid=myg) v_xint = ai.ArrayIndexer(d=_vx, grid=myg) u_yint = ai.ArrayIndexer(d=_uy, grid=myg) v_yint = ai.ArrayIndexer(d=_vy, grid=myg) # --------------------------------------------------------------------- # update U to get the provisional velocity field # --------------------------------------------------------------------- if self.verbose > 0: print(" doing provisional update of u, v") # compute (U.grad)U # we want u_MAC U_x + v_MAC U_y advect_x = myg.scratch_array() advect_y = myg.scratch_array() advect_x.v()[:, :] = \ 0.5*(u_MAC.v() + u_MAC.ip(1))*(u_xint.ip(1) - u_xint.v())/myg.dx +\ 0.5*(v_MAC.v() + v_MAC.jp(1))*(u_yint.jp(1) - u_yint.v())/myg.dy advect_y.v()[:, :] = \ 0.5*(u_MAC.v() + u_MAC.ip(1))*(v_xint.ip(1) - v_xint.v())/myg.dx +\ 0.5*(v_MAC.v() + v_MAC.jp(1))*(v_yint.jp(1) - v_yint.v())/myg.dy proj_type = self.rp.get_param("lm-atmosphere.proj_type") if proj_type == 1: u.v()[:, :] -= (self.dt*advect_x.v() + self.dt*gradp_x.v()) v.v()[:, :] -= (self.dt*advect_y.v() + self.dt*gradp_y.v()) elif proj_type == 2: u.v()[:, :] -= self.dt*advect_x.v() v.v()[:, :] -= self.dt*advect_y.v() # add the gravitational source rho_half = 0.5*(rho + rho_old) rhoprime = self.make_prime(rho_half, rho0) source[:, :] = (rhoprime*g/rho_half) self.aux_data.fill_BC("source_y") v[:, :] += self.dt*source self.cc_data.fill_BC("x-velocity") self.cc_data.fill_BC("y-velocity") if self.verbose > 0: print("min/max rho = {}, {}".format(self.cc_data.min("density"), self.cc_data.max("density"))) print("min/max u = {}, {}".format(self.cc_data.min("x-velocity"), self.cc_data.max("x-velocity"))) print("min/max v = {}, {}".format(self.cc_data.min("y-velocity"), self.cc_data.max("y-velocity"))) # --------------------------------------------------------------------- # project the final velocity # --------------------------------------------------------------------- # now we solve L phi = D (U* /dt) if self.verbose > 0: print(" final projection") # create the coefficient array: beta0**2/rho coeff = 1.0/rho coeff.v()[:, :] = coeff.v()*beta0.v2d()**2 # create the multigrid object mg = vcMG.VarCoeffCCMG2d(myg.nx, myg.ny, xl_BC_type=self.cc_data.BCs["phi"].xlb, xr_BC_type=self.cc_data.BCs["phi"].xrb, yl_BC_type=self.cc_data.BCs["phi"].ylb, yr_BC_type=self.cc_data.BCs["phi"].yrb, xmin=myg.xmin, xmax=myg.xmax, ymin=myg.ymin, ymax=myg.ymax, coeffs=coeff, coeffs_bc=self.cc_data.BCs["density"], verbose=0) # first compute div{beta_0 U} # u/v are cell-centered, divU is cell-centered div_beta_U.v()[:, :] = \ 0.5*beta0.v2d()*(u.ip(1) - u.ip(-1))/myg.dx + \ 0.5*(beta0.v2dp(1)*v.jp(1) - beta0.v2dp(-1)*v.jp(-1))/myg.dy mg.init_RHS(div_beta_U/self.dt) # use the old phi as our initial guess phiGuess = mg.soln_grid.scratch_array() phiGuess.v(buf=1)[:, :] = phi.v(buf=1) mg.init_solution(phiGuess) # solve mg.solve(rtol=1.e-12) # store the solution in our self.cc_data object -- include a single # ghostcell phi[:, :] = mg.get_solution(grid=myg) # get the cell-centered gradient of p and update the velocities # this differs depending on what we projected. gradphi_x, gradphi_y = mg.get_solution_gradient(grid=myg) # U = U - (beta_0/rho) grad (phi/beta_0) coeff = 1.0/rho coeff.v()[:, :] = coeff.v()*beta0.v2d() u.v()[:, :] -= self.dt*coeff.v()*gradphi_x.v() v.v()[:, :] -= self.dt*coeff.v()*gradphi_y.v() # store gradp for the next step if proj_type == 1: gradp_x.v()[:, :] += gradphi_x.v() gradp_y.v()[:, :] += gradphi_y.v() elif proj_type == 2: gradp_x.v()[:, :] = gradphi_x.v() gradp_y.v()[:, :] = gradphi_y.v() self.cc_data.fill_BC("x-velocity") self.cc_data.fill_BC("y-velocity") self.cc_data.fill_BC("gradp_x") self.cc_data.fill_BC("gradp_y") # increment the time if not self.in_preevolve: self.cc_data.t += self.dt self.n += 1