def cfl_max(uvw, dt, dxyz): #-------------------------------------------------------------------------- """ Docstring. """ # Unpack received tuples u, v, w = uvw dx, dy, dz = dxyz # Take velocity's position d = u.pos # Mesh is cell-centered if d == C: cfl = dt * max(abs(u.val/dx).max(), \ abs(v.val/dy).max(), \ abs(w.val/dz).max()) # Mesh is staggered else: cfl = dt * max(abs(u.val/avg(X, dx)).max(), \ abs(v.val/avg(Y, dy)).max(), \ abs(w.val/avg(Z, dz)).max()) return cfl
def corr_uvw(uvw, p, rho, dt, dxyz, obst): # -------------------------------------------------------------------------- """ Docstring. """ # Unpack received tuples dx, dy, dz = dxyz # Compute pressure correction gradients p_x = dif(X, p.val) / avg(X, dx) p_y = dif(Y, p.val) / avg(Y, dy) p_z = dif(Z, p.val) / avg(Z, dz) # Set to zero in obst if obst.any() != 0: p_x = obst_zero_val(X, p_x, obst) p_y = obst_zero_val(Y, p_y, obst) p_z = obst_zero_val(Z, p_z, obst) # Pad with boundary values by expanding from interior # (This is done only for collocated formulation) if uvw[X].pos == C: p_x = avg(X, cat(X, (p_x[:1, :, :], p_x, p_x[-1:, :, :]))) p_y = avg(Y, cat(Y, (p_y[:, :1, :], p_y, p_y[:, -1:, :]))) p_z = avg(Z, cat(Z, (p_z[:, :, :1], p_z, p_z[:, :, -1:]))) # Correct the velocities uvw[X].val[:] = uvw[X].val[:] - dt / avg(uvw[X].pos, rho) * p_x uvw[Y].val[:] = uvw[Y].val[:] - dt / avg(uvw[Y].pos, rho) * p_y uvw[Z].val[:] = uvw[Z].val[:] - dt / avg(uvw[Z].pos, rho) * p_z return # end of function
def par(mean_val, x_nodes): """ A function to generate a parabolic profile over a set of cell centers, useful for specifying parabolic inlet velocity profiles. It expects nodal coordinates as input, but sends values at cell centers back. Input coordinates: |-----|-----|-----|-----|-----|-----|-----| Output values: o-----o-----o-----o-----o-----o-----o Input parameters mean_val - mean value the parabola will have x_nodes - nodal coordinatels ------------------------------------------------------------------------- """ # It is known that maximum of a parabola is 3/2 of its mean value max_val = mean_val * 3 / 2 # Normalized x coordinates (from -1 to +1) xn = copy(x_nodes) xn -= xn.min() xn /= (xn.max() - xn.min()) xn *= 2 xn -= 1 xc = avg(xn) yc = (1.0 - xc * xc) * max_val return yc # end of function
def plot_isolines(phi, uvw, xyzn, d): """ Docstring. """ # Unpack tuples u, v, w = uvw xn, yn, zn = xyzn # Cell coordinates xc = avg(xn) yc = avg(yn) zc = avg(zn) # Collocated velocity components if u.pos == C: uc = u.val vc = v.val wc = w.val else: uc = avg(X, cat(X, (u.bnd[W].val[:1, :, :], \ u.val, \ u.bnd[E].val[:1, :, :]))) vc = avg(Y, cat(Y, (v.bnd[S].val[:, :1, :], \ v.val, \ v.bnd[N].val[:, :1, :]))) wc = avg(Z, cat(Z, (w.bnd[B].val[:, :, :1], \ w.val, \ w.bnd[T].val[:, :, :1]))) # Pick coordinates for plotting (xp, yp) and values for plotting if d == Y: jp = floor(yc.size / 2) xp, yp = meshgrid(xc, zc) zp = transpose(phi[:, jp, :], (1, 0)) up = transpose(uc[:, jp, :], (1, 0)) vp = transpose(wc[:, jp, :], (1, 0)) if d == Z: kp = floor(zc.size / 2) xp, yp = meshgrid(xc, yc) zp = transpose(phi[:, :, kp], (1, 0)) up = transpose(uc[:, :, kp], (1, 0)) vp = transpose(vc[:, :, kp], (1, 0)) # Set levels and normalize the colors levels = linspace(zp.min(), zp.max(), 11) norm = cm.colors.Normalize(vmax=zp.max(), vmin=zp.min()) plt.figure() plt.gca(aspect='equal') plt.contour(xp, yp, zp, levels, cmap=plt.cm.rainbow, norm=norm) plt.quiver(xp, yp, up, vp) plt.axis([min(xn), max(xn), min(yn), max(yn)]) plt.show() return # end of function
def adj_o_bnds(uvw, dxyz, dt): """ Update velocities in boundary cells :param uvw: velocities u, v, w :param dxyz: deltas of x, y, z coordinates :param dt: time step :return: """ # pylint:disable=invalid-name,too-many-locals,too-many-statements # Unpack tuples u, v, w = uvw dx, dy, dz = dxyz # Local variables used in this function area_in = 0.0 # area of the inlet area_out = 0.0 # area of the outlet vol_in = 0.0 # inlet volume flux; positive for inflow vol_out_1 = 0.0 # outlet volume flux; positive for outflow vol_out_2 = 0.0 # outlet volume flux; positive for outflow verbatim = False sx = dy * dz sy = dx * dz sz = dx * dy sx = sx[:1, :, :] sy = sy[:, :1, :] sz = sz[:, :, :1] # ----------------------------------------------------------------- # Compute the volume flowing in (v_in), volume flowing out (v_out) # as well as inlet and outlet areas (a_in, a_out) # ----------------------------------------------------------------- # Inlets: these arrays will hold values true (1) in cells # with inlet boundary conditions, and false (0) otherwise if_w_in = (u.bnd[W].typ[:1, :, :] == DIRICHLET) & (u.bnd[W].val[:1, :, :] > +TINY) if_e_in = (u.bnd[E].typ[:1, :, :] == DIRICHLET) & (u.bnd[E].val[:1, :, :] < -TINY) if_s_in = (v.bnd[S].typ[:, :1, :] == DIRICHLET) & (v.bnd[S].val[:, :1, :] > +TINY) if_n_in = (v.bnd[N].typ[:, :1, :] == DIRICHLET) & (v.bnd[N].val[:, :1, :] < -TINY) if_b_in = (w.bnd[B].typ[:, :, :1] == DIRICHLET) & (w.bnd[B].val[:, :, :1] > +TINY) if_t_in = (w.bnd[T].typ[:, :, :1] == DIRICHLET) & (w.bnd[T].val[:, :, :1] < -TINY) # Using the arrays defined above, compute inlet surface area area_in += (if_w_in * sx).sum() area_in += (if_e_in * sx).sum() area_in += (if_s_in * sy).sum() area_in += (if_n_in * sy).sum() area_in += (if_b_in * sz).sum() area_in += (if_t_in * sz).sum() # If there is no inlet, nothing to do here any longer if area_in < TINY: return u, v, w # one end of function # Using the arrays defined above, compute inlet volume flux vol_in += (if_w_in * u.bnd[W].val[:1, :, :] * sx).sum() vol_in -= (if_e_in * u.bnd[E].val[:1, :, :] * sx).sum() vol_in += (if_s_in * v.bnd[S].val[:, :1, :] * sy).sum() vol_in -= (if_n_in * v.bnd[N].val[:, :1, :] * sy).sum() vol_in += (if_b_in * w.bnd[B].val[:, :, :1] * sz).sum() vol_in -= (if_t_in * w.bnd[T].val[:, :, :1] * sz).sum() # Outlets: these arrays will hold values true (1) in cells ... # with outlet boundary conditions, and false (0) otherwise if_w_out_u = (u.bnd[W].typ[:1, :, :] == OUTLET) if_w_out_v = avg(v.pos, if_w_out_u * 1) > 0.5 if_w_out_w = avg(w.pos, if_w_out_u * 1) > 0.5 if_e_out_u = (u.bnd[E].typ[:1, :, :] == OUTLET) if_e_out_v = avg(v.pos, if_e_out_u * 1) > 0.5 if_e_out_w = avg(w.pos, if_e_out_u * 1) > 0.5 if_s_out_v = (v.bnd[S].typ[:, :1, :] == OUTLET) if_s_out_u = avg(u.pos, if_s_out_v * 1) > 0.5 if_s_out_w = avg(w.pos, if_s_out_v * 1) > 0.5 if_n_out_v = (v.bnd[N].typ[:, :1, :] == OUTLET) if_n_out_u = avg(u.pos, if_n_out_v * 1) > 0.5 if_n_out_w = avg(w.pos, if_n_out_v * 1) > 0.5 if_b_out_w = (w.bnd[B].typ[:, :, :1] == OUTLET) if_b_out_u = avg(u.pos, if_b_out_w * 1) > 0.5 if_b_out_v = avg(v.pos, if_b_out_w * 1) > 0.5 if_t_out_w = (w.bnd[T].typ[:, :, :1] == OUTLET) if_t_out_u = avg(u.pos, if_t_out_w * 1) > 0.5 if_t_out_v = avg(v.pos, if_t_out_w * 1) > 0.5 # Using the arrays defined above, compute outlet surface area area_out += (if_w_out_u * sx).sum() area_out += (if_e_out_u * sx).sum() area_out += (if_s_out_v * sy).sum() area_out += (if_n_out_v * sy).sum() area_out += (if_b_out_w * sz).sum() area_out += (if_t_out_w * sz).sum() # Using the arrays defined above, compute outlet volume flux vol_out_1 -= (if_w_out_u * u.bnd[W].val[:1, :, :] * sx).sum() vol_out_1 += (if_e_out_u * u.bnd[E].val[:1, :, :] * sx).sum() vol_out_1 -= (if_s_out_v * v.bnd[S].val[:, :1, :] * sy).sum() vol_out_1 += (if_n_out_v * v.bnd[N].val[:, :1, :] * sy).sum() vol_out_1 -= (if_b_out_w * w.bnd[B].val[:, :, :1] * sz).sum() vol_out_1 += (if_t_out_w * w.bnd[T].val[:, :, :1] * sz).sum() # -------------------------------- # Check and calculate corrections # -------------------------------- if area_in == 0: ub_in = 0 else: ub_in = vol_in / area_in if area_out == 0: ub_out = 0 else: ub_out = vol_out_1 / area_out # --------------------------------------------- # If nothing comes out, make a bulk correction # --------------------------------------------- if ub_out < TINY: u_bulk_corr = ub_in * area_in / area_out u.bnd[W].val[:1, :, :] = (u.bnd[W].val[:1, :, :] * lnot(if_w_out_u) - u_bulk_corr * if_w_out_u) u.bnd[E].val[:1, :, :] = (u.bnd[E].val[:1, :, :] * lnot(if_e_out_u) + u_bulk_corr * if_e_out_u) v.bnd[S].val[:, :1, :] = (v.bnd[S].val[:, :1, :] * lnot(if_s_out_v) - u_bulk_corr * if_s_out_v) v.bnd[N].val[:, :1, :] = (v.bnd[N].val[:, :1, :] * lnot(if_n_out_v) + u_bulk_corr * if_n_out_v) w.bnd[B].val[:, :, :1] = (w.bnd[B].val[:, :, :1] * lnot(if_b_out_w) - u_bulk_corr * if_b_out_w) w.bnd[T].val[:, :, :1] = (w.bnd[T].val[:, :, :1] * lnot(if_t_out_w) + u_bulk_corr * if_t_out_w) # ------------------------------------------------------------- # Correction outflow by applying convective boundary condition # ------------------------------------------------------------- else: du_dx_w = (u.val[:1, :, :] - u.bnd[W].val[:1, :, :]) / dx[:1, :, :] dv_dx_w = ((v.val[:1, :, :] - v.bnd[W].val[:1, :, :]) / avg(v.pos, dx[:1, :, :])) dw_dx_w = ((w.val[:1, :, :] - w.bnd[W].val[:1, :, :]) / avg(w.pos, dx[:1, :, :])) du_dx_e = (u.val[-1:, :, :] - u.bnd[E].val[:1, :, :]) / dx[-1:, :, :] dv_dx_e = ((v.val[-1:, :, :] - v.bnd[E].val[:1, :, :]) / avg(v.pos, dx[-1:, :, :])) dw_dx_e = ((w.val[-1:, :, :] - w.bnd[E].val[:1, :, :]) / avg(w.pos, dx[-1:, :, :])) du_dy_s = ((u.val[:, :1, :] - u.bnd[S].val[:, :1, :]) / avg(u.pos, dy[:, :1, :])) dv_dy_s = (v.val[:, :1, :] - v.bnd[S].val[:, :1, :]) / dy[:, :1, :] dw_dy_s = ((w.val[:, :1, :] - w.bnd[S].val[:, :1, :]) / avg(w.pos, dy[:, :1, :])) du_dy_n = ((u.val[:, -1:, :] - u.bnd[N].val[:, :1, :]) / avg(u.pos, dy[:, -1:, :])) dv_dy_n = (v.val[:, -1:, :] - v.bnd[N].val[:, :1, :]) / dy[:, -1:, :] dw_dy_n = ((w.val[:, -1:, :] - w.bnd[N].val[:, :1, :]) / avg(w.pos, dy[:, -1:, :])) du_dz_b = ((u.val[:, :, :1] - u.bnd[B].val[:, :, :1]) / avg(u.pos, dz[:, :, :1])) dv_dz_b = ((v.val[:, :, :1] - v.bnd[B].val[:, :, :1]) / avg(v.pos, dz[:, :, :1])) dw_dz_b = (w.val[:, :, :1] - w.bnd[B].val[:, :, :1]) / dz[:, :, :1] du_dz_t = (((u.val[:, :, -1:] - u.bnd[T].val[:, :, :1]) / avg(u.pos, dz[:, :, -1:]))) dv_dz_t = ((v.val[:, :, -1:] - v.bnd[T].val[:, :, :1]) / avg(v.pos, dz[:, :, -1:])) dw_dz_t = (w.val[:, :, -1:] - w.bnd[T].val[:, :, :1]) / dz[:, :, -1:] u_bnd_w_corr = (u.bnd[W].val[:1, :, :] + ub_out * dt * du_dx_w) v_bnd_w_corr = (v.bnd[W].val[:1, :, :] + ub_out * dt * dv_dx_w) w_bnd_w_corr = (w.bnd[W].val[:1, :, :] + ub_out * dt * dw_dx_w) u.bnd[W].val[:1, :, :] = (u.bnd[W].val[:1, :, :] * lnot(if_w_out_u) + u_bnd_w_corr * if_w_out_u) v.bnd[W].val[:1, :, :] = (v.bnd[W].val[:1, :, :] * lnot(if_w_out_v) + v_bnd_w_corr * if_w_out_v) w.bnd[W].val[:1, :, :] = (w.bnd[W].val[:1, :, :] * lnot(if_w_out_w) + w_bnd_w_corr * if_w_out_w) u_bnd_e_corr = (u.bnd[E].val[:1, :, :] + ub_out * dt * du_dx_e) v_bnd_e_corr = (v.bnd[E].val[:1, :, :] + ub_out * dt * dv_dx_e) w_bnd_e_corr = (w.bnd[E].val[:1, :, :] + ub_out * dt * dw_dx_e) u.bnd[E].val[:1, :, :] = (u.bnd[E].val[:1, :, :] * lnot(if_e_out_u) + u_bnd_e_corr * if_e_out_u) v.bnd[E].val[:1, :, :] = (v.bnd[E].val[:1, :, :] * lnot(if_e_out_v) + v_bnd_e_corr * if_e_out_v) w.bnd[E].val[:1, :, :] = (w.bnd[E].val[:1, :, :] * lnot(if_e_out_w) + w_bnd_e_corr * if_e_out_w) u_bnd_s_corr = (u.bnd[S].val[:, :1, :] + ub_out * dt * du_dy_s) v_bnd_s_corr = (v.bnd[S].val[:, :1, :] + ub_out * dt * dv_dy_s) w_bnd_s_corr = (w.bnd[S].val[:, :1, :] + ub_out * dt * dw_dy_s) u.bnd[S].val[:, :1, :] = (u.bnd[S].val[:, :1, :] * lnot(if_s_out_u) + u_bnd_s_corr * if_s_out_u) v.bnd[S].val[:, :1, :] = (v.bnd[S].val[:, :1, :] * lnot(if_s_out_v) + v_bnd_s_corr * if_s_out_v) w.bnd[S].val[:, :1, :] = (w.bnd[S].val[:, :1, :] * lnot(if_s_out_w) + w_bnd_s_corr * if_s_out_w) u_bnd_n_corr = (u.bnd[N].val[:, :1, :] + ub_out * dt * du_dy_n) v_bnd_n_corr = (v.bnd[N].val[:, :1, :] + ub_out * dt * dv_dy_n) w_bnd_n_corr = (w.bnd[N].val[:, :1, :] + ub_out * dt * dw_dy_n) u.bnd[N].val[:, :1, :] = (u.bnd[N].val[:, :1, :] * lnot(if_n_out_u) + u_bnd_n_corr * if_n_out_u) v.bnd[N].val[:, :1, :] = (v.bnd[N].val[:, :1, :] * lnot(if_n_out_v) + v_bnd_n_corr * if_n_out_v) w.bnd[N].val[:, :1, :] = (w.bnd[N].val[:, :1, :] * lnot(if_n_out_w) + w_bnd_n_corr * if_n_out_w) u_bnd_b_corr = (u.bnd[B].val[:, :, :1] + ub_out * dt * du_dz_b) v_bnd_b_corr = (v.bnd[B].val[:, :, :1] + ub_out * dt * dv_dz_b) w_bnd_b_corr = (w.bnd[B].val[:, :, :1] + ub_out * dt * dw_dz_b) u.bnd[B].val[:, :, :1] = (u.bnd[B].val[:, :, :1] * lnot(if_b_out_u) + u_bnd_b_corr * if_b_out_u) v.bnd[B].val[:, :, :1] = (v.bnd[B].val[:, :, :1] * lnot(if_b_out_v) + v_bnd_b_corr * if_b_out_v) w.bnd[B].val[:, :, :1] = (w.bnd[B].val[:, :, :1] * lnot(if_b_out_w) + w_bnd_b_corr * if_b_out_w) u_bnd_t_corr = (u.bnd[T].val[:, :, :1] + ub_out * dt * du_dz_t) v_bnd_t_corr = (v.bnd[T].val[:, :, :1] + ub_out * dt * dv_dz_t) w_bnd_t_corr = (w.bnd[T].val[:, :, :1] + ub_out * dt * dw_dz_t) u.bnd[T].val[:, :, :1] = (u.bnd[T].val[:, :, :1] * lnot(if_t_out_u) + u_bnd_t_corr * if_t_out_u) v.bnd[T].val[:, :, :1] = (v.bnd[T].val[:, :, :1] * lnot(if_t_out_v) + v_bnd_t_corr * if_t_out_v) w.bnd[T].val[:, :, :1] = (w.bnd[T].val[:, :, :1] * lnot(if_t_out_w) + w_bnd_t_corr * if_t_out_w) if verbatim: print('+----------------------------+') print('| ub_in = %12.5e |' % ub_in) print('| a_in = %12.5e |' % area_in) print('| v_in = %12.5e |' % vol_in) print('| ub_out = %12.5e |' % ub_out) print('| a_out = %12.5e |' % area_out) print('| v_out_1 = %12.5e |' % vol_out_1) # --------------------------------------------- # Scaling correction to whatever you did above # (bulk correction or convective outflow) # --------------------------------------------- vol_out_2 = 0.0 vol_out_2 -= (if_w_out_u * u.bnd[W].val[:1, :, :] * sx).sum() vol_out_2 += (if_e_out_u * u.bnd[E].val[:1, :, :] * sx).sum() vol_out_2 -= (if_s_out_v * v.bnd[S].val[:, :1, :] * sy).sum() vol_out_2 += (if_n_out_v * v.bnd[N].val[:, :1, :] * sy).sum() vol_out_2 -= (if_b_out_w * w.bnd[B].val[:, :, :1] * sz).sum() vol_out_2 += (if_t_out_w * w.bnd[T].val[:, :, :1] * sz).sum() if vol_out_2 > TINY: factor = vol_in / vol_out_2 else: factor = 1.0 if verbatim: print('+----------------------------+') print('| v_out_2 = %12.5e |' % vol_out_2) print('| factor = %12.5e |' % factor) print('+----------------------------+') # ------------------------------------- # Correction to satisfy volume balance # ------------------------------------- u.bnd[W].val[:1, :, :] = (u.bnd[W].val[:1, :, :] * lnot(if_w_out_u) + u.bnd[W].val[:1, :, :] * if_w_out_u * factor) u.bnd[E].val[:1, :, :] = (u.bnd[E].val[:1, :, :] * lnot(if_e_out_u) + u.bnd[E].val[:1, :, :] * if_e_out_u * factor) v.bnd[S].val[:, :1, :] = (v.bnd[S].val[:, :1, :] * lnot(if_s_out_v) + v.bnd[S].val[:, :1, :] * if_s_out_v * factor) v.bnd[N].val[:, :1, :] = (v.bnd[N].val[:, :1, :] * lnot(if_n_out_v) + v.bnd[N].val[:, :1, :] * if_n_out_v * factor) w.bnd[B].val[:, :, :1] = (w.bnd[B].val[:, :, :1] * lnot(if_b_out_w) + w.bnd[B].val[:, :, :1] * if_b_out_w * factor) w.bnd[T].val[:, :, :1] = (w.bnd[T].val[:, :, :1] * lnot(if_t_out_w) + w.bnd[T].val[:, :, :1] * if_t_out_w * factor)
def calc_uvw(uvw, uvwf, rho, mu, p_tot, e_f, dt, dxyz, obst): """ Calculate uvw TODO: Write useful docstring :param uvw: :param uvwf: :param rho: :param mu: :param p_tot: :param e_f: :param dt: :param dxyz: :param obst: :return: """ # pylint: disable=invalid-name, too-many-locals, too-many-statements # Unpack tuples u, v, w = uvw uf, vf, wf = uvwf dx, dy, dz = dxyz e_x, e_y, e_z = e_f # Fetch resolutions ru = u.val.shape rv = v.val.shape rw = w.val.shape # Pre-compute geometrical quantities dv = dx * dy * dz d = u.pos # Create linear systems A_u, b_u = create_matrix(u, rho / dt, mu, dxyz, obst, 'd') A_v, b_v = create_matrix(v, rho / dt, mu, dxyz, obst, 'd') A_w, b_w = create_matrix(w, rho / dt, mu, dxyz, obst, 'd') # Advection terms for momentum c_u = advection(rho, u, uvwf, dxyz, dt, 'superbee') c_v = advection(rho, v, uvwf, dxyz, dt, 'superbee') c_w = advection(rho, w, uvwf, dxyz, dt, 'superbee') # Innertial term for momentum (this works for collocated and staggered) i_u = u.old * avg(u.pos, rho) * avg(u.pos, dv) / dt i_v = v.old * avg(v.pos, rho) * avg(v.pos, dv) / dt i_w = w.old * avg(w.pos, rho) * avg(w.pos, dv) / dt # Compute staggered pressure gradients p_tot_x = dif(X, p_tot) / avg(X, dx) p_tot_y = dif(Y, p_tot) / avg(Y, dy) p_tot_z = dif(Z, p_tot) / avg(Z, dz) # Make pressure gradients cell-centered if d == C: p_tot_x = avg(X, cat(X, (p_tot_x[:1, :, :], p_tot_x, p_tot_x[-1:, :, :]))) p_tot_y = avg(Y, cat(Y, (p_tot_y[:, :1, :], p_tot_y, p_tot_y[:, -1:, :]))) p_tot_z = avg(Z, cat(Z, (p_tot_z[:, :, :1], p_tot_z, p_tot_z[:, :, -1:]))) # Total pressure gradients (this works for collocated and staggered) p_st_u = p_tot_x * avg(u.pos, dv) p_st_v = p_tot_y * avg(v.pos, dv) p_st_w = p_tot_z * avg(w.pos, dv) # Full force terms for momentum equations (collocated and staggered) f_u = b_u - c_u + i_u - p_st_u + e_x * avg(u.pos, dv) f_v = b_v - c_v + i_v - p_st_v + e_y * avg(v.pos, dv) f_w = b_w - c_w + i_w - p_st_w + e_z * avg(w.pos, dv) # Take care of obsts in the domian if obst.any() != 0: f_u = obst_zero_val(u.pos, f_u, obst) f_v = obst_zero_val(v.pos, f_v, obst) f_w = obst_zero_val(w.pos, f_w, obst) # Solve for velocities res0 = bicgstab(A_u, reshape(f_u, prod(ru)), tol=TOL) res1 = bicgstab(A_v, reshape(f_v, prod(rv)), tol=TOL) res2 = bicgstab(A_w, reshape(f_w, prod(rw)), tol=TOL) u.val[:] = reshape(res0[0], ru) v.val[:] = reshape(res1[0], rv) w.val[:] = reshape(res2[0], rw) # Update velocities in boundary cells adj_o_bnds((u, v, w), (dx, dy, dz), dt) # Update face velocities (also substract cell-centered pressure gradients # and add staggered pressure gradients) if d == C: uf.val[:] = (avg(X, u.val + dt / rho * (p_tot_x)) - dt / avg(X, rho) * (dif(X, p_tot) / avg(X, dx))) vf.val[:] = (avg(Y, v.val + dt / rho * (p_tot_y)) - dt / avg(Y, rho) * (dif(Y, p_tot) / avg(Y, dy))) wf.val[:] = (avg(Z, w.val + dt / rho * (p_tot_z)) - dt / avg(Z, rho) * (dif(Z, p_tot) / avg(Z, dz))) for j in (W, E): uf.bnd[j].val[:] = u.bnd[j].val[:] vf.bnd[j].val[:] = avg(Y, v.bnd[j].val[:]) wf.bnd[j].val[:] = avg(Z, w.bnd[j].val[:]) for j in (S, N): uf.bnd[j].val[:] = avg(X, u.bnd[j].val[:]) vf.bnd[j].val[:] = v.bnd[j].val[:] wf.bnd[j].val[:] = avg(Z, w.bnd[j].val[:]) for j in (B, T): uf.bnd[j].val[:] = avg(X, u.bnd[j].val[:]) vf.bnd[j].val[:] = avg(Y, v.bnd[j].val[:]) wf.bnd[j].val[:] = w.bnd[j].val[:] else: uf.val[:] = u.val[:] vf.val[:] = v.val[:] wf.val[:] = w.val[:] for j in (W, E, S, N, B, T): uf.bnd[j].val[:] = u.bnd[j].val[:] vf.bnd[j].val[:] = v.bnd[j].val[:] wf.bnd[j].val[:] = w.bnd[j].val[:] if obst.any() != 0: uf.val[:] = obst_zero_val(X, uf.val, obst) vf.val[:] = obst_zero_val(Y, vf.val, obst) wf.val[:] = obst_zero_val(Z, wf.val, obst) return # end of function
def advection(rho, phi, uvwf, dxyz, dt, lim_name): """ Docstring. """ res = phi.val.shape nx, ny, nz = res # Unpack tuples uf, vf, wf = uvwf dx, dy, dz = dxyz d = phi.pos # Pre-compute geometrical quantities sx = dy * dz sy = dx * dz sz = dx * dy # ------------------------------------------------- # Specific for cell-centered transported variable # ------------------------------------------------- if d == C: # Facial values of physical properties including boundary cells rho_x_fac = cat( X, (rho[:1, :, :], avg(X, rho), rho[-1:, :, :])) # nxp,ny, nz rho_y_fac = cat( Y, (rho[:, :1, :], avg(Y, rho), rho[:, -1:, :])) # nx, nyp,nz rho_z_fac = cat( Z, (rho[:, :, :1], avg(Z, rho), rho[:, :, -1:])) # nx, ny, nzp # Facial values of areas including boundary cells a_x_fac = cat(X, (sx[:1, :, :], avg(X, sx), sx[-1:, :, :])) a_y_fac = cat(Y, (sy[:, :1, :], avg(Y, sy), sy[:, -1:, :])) a_z_fac = cat(Z, (sz[:, :, :1], avg(Z, sz), sz[:, :, -1:])) del_x = avg(X, dx) del_y = avg(Y, dy) del_z = avg(Z, dz) # Facial values of velocities without boundary values u_fac = uf.val # nxm,ny, nz v_fac = vf.val # nx, nym,nz w_fac = wf.val # nx, ny, nzm # Boundary velocity values u_bnd_W = uf.bnd[W].val u_bnd_E = uf.bnd[E].val v_bnd_S = vf.bnd[S].val v_bnd_N = vf.bnd[N].val w_bnd_B = wf.bnd[B].val w_bnd_T = wf.bnd[T].val # ------------------------------------------------------------ # Specific for transported variable staggered in x direction # ------------------------------------------------------------ if d == X: # Facial values of physical properties including boundary cells rho_x_fac = rho # nx, ny, nz rho_nod_y = avg(X, avg(Y, rho)) # nxm,nym,nz rho_y_fac = cat(Y, (rho_nod_y[:, :1, :], \ rho_nod_y[:, :, :], \ rho_nod_y[:, -1:, :])) # nxm,nyp,nz rho_nod_z = avg(X, avg(Z, rho)) # nxm,ny,nzm rho_z_fac = cat(Z, (rho_nod_z[:, :, :1], \ rho_nod_z[:, :, :], \ rho_nod_z[:, :, -1:])) # nxm,ny,nzp # Facial values of areas including boundary cells a_x_fac = sx a_y_fac = cat(Y, ( \ avg(X, sy[:, :1, :]),\ avg(X, avg(Y, sy)), \ avg(X, sy[:, -1:, :]))) a_z_fac = cat(Z, ( \ avg(X, sz[:, :, :1]), \ avg(X, avg(Z, sz)), \ avg(X, sz[:, :, -1:]))) del_x = dx[1:-1, :, :] del_y = avg(X, avg(Y, dy)) del_z = avg(X, avg(Z, dz)) # Facial values of velocities without boundary values u_fac = avg(X, uf.val) # nxmm,ny, nz v_fac = avg(X, vf.val) # nxm, nym,nz w_fac = avg(X, wf.val) # nxm, ny, nzm # Boundary velocity values u_bnd_W = uf.bnd[W].val u_bnd_E = uf.bnd[E].val v_bnd_S = avg(X, vf.bnd[S].val) v_bnd_N = avg(X, vf.bnd[N].val) w_bnd_B = avg(X, wf.bnd[B].val) w_bnd_T = avg(X, wf.bnd[T].val) # ------------------------------------------------------------ # Specific for transported variable staggered in y direction # ------------------------------------------------------------ if d == Y: # Facial values of physical properties including boundary cells rho_nod_x = avg(Y, avg(X, rho)) # nxm,nym,nz rho_x_fac = cat(X, (rho_nod_x[:1, :, :], \ rho_nod_x[:, :, :], \ rho_nod_x[-1:, :, :])) # nxp,nym,nz rho_y_fac = rho # nx, ny, nz rho_nod_z = avg(Y, avg(Z, rho)) # nx, nym,nzm rho_z_fac = cat(Z, (rho_nod_z[:, :, :1], \ rho_nod_z[:, :, :], \ rho_nod_z[:, :, -1:])) # nx, nym,nzp # Facial values of areas including boundary cells a_x_fac = cat(X, ( \ avg(Y, sx[:1, :, :]), \ avg(Y, avg(X, sx)), \ avg(Y, sx[-1:, :, :]))) a_y_fac = sy a_z_fac = cat(Z, ( \ avg(Y, sz[:, :, :1]), \ avg(Y, avg(Z, sz)), \ avg(Y, sz[:, :, -1:]))) del_x = avg(Y, avg(X, dx)) del_y = dy[:, 1:-1, :] del_z = avg(Y, avg(Z, dz)) # Facial values of velocities without boundary values u_fac = avg(Y, uf.val) # nxm,nym, nz v_fac = avg(Y, vf.val) # nx, nymm,nz w_fac = avg(Y, wf.val) # nx, nym, nzm # Facial values of velocities with boundary values u_bnd_W = avg(Y, uf.bnd[W].val) u_bnd_E = avg(Y, uf.bnd[E].val) v_bnd_S = vf.bnd[S].val v_bnd_N = vf.bnd[N].val w_bnd_B = avg(Y, wf.bnd[B].val) w_bnd_T = avg(Y, wf.bnd[T].val) # ------------------------------------------------------------ # Specific for transported variable staggered in z direction # ------------------------------------------------------------ if d == Z: # Facial values of physical properties including boundary cells rho_nod_x = avg(Z, avg(X, rho)) # nxm,ny, nzm rho_x_fac = cat(X, (rho_nod_x[ :1, :, :], \ rho_nod_x[ :, :, :], \ rho_nod_x[-1:, :, :])) # nxp,ny, nzm rho_nod_y = avg(Z, avg(Y, rho)) # nx, nym,nzm rho_y_fac = cat(Y, (rho_nod_y[:, :1, :], \ rho_nod_y[:, :, :], \ rho_nod_y[:, -1:, :])) # nx, nyp,nzm rho_z_fac = rho # nx, ny, nz # Facial values of areas including boundary cells a_x_fac = cat(X, ( \ avg(Z, sx[:1, :, :]), \ avg(Z, avg(X, sx)), \ avg(Z, sx[-1:, :, :]))) a_y_fac = cat(Y, ( \ avg(Z, sy[:, :1, :]), \ avg(Z, avg(Y, sy)), \ avg(Z, sy[:, -1:, :]))) a_z_fac = sz del_x = avg(Z, avg(X, dx)) del_y = avg(Z, avg(Y, dy)) del_z = dz[:, :, 1:-1] # Facial values of velocities without boundary values u_fac = avg(Z, uf.val) # nxm,ny, nzm v_fac = avg(Z, vf.val) # nx, nym, nzm w_fac = avg(Z, wf.val) # nx, ny, nzmm # Facial values of velocities with boundary values u_bnd_W = avg(Z, uf.bnd[W].val) u_bnd_E = avg(Z, uf.bnd[E].val) v_bnd_S = avg(Z, vf.bnd[S].val) v_bnd_N = avg(Z, vf.bnd[N].val) w_bnd_B = wf.bnd[B].val w_bnd_T = wf.bnd[T].val # ------------------------------ # Common part of the algorithm # ------------------------------ # ------------------------------------------------------------ # # |-o-|-o-|-o-|-o-|-o-|-o-|-o-|-o-|-o-|-o-| # 1 2 3 4 5 6 7 8 9 10 phi # x---x---x---x---x---x---x---x---x # 1 2 3 4 5 6 7 8 9 d_x initial # 0---x---x---x---x---x---x---x---x---x---0 # 1 2 3 4 5 6 7 8 9 10 11 d_x padded # # ------------------------------------------------------------ # Compute consecutive differences (and avoid division by zero) d_x = dif(X, phi.val) # nxm, ny, nz d_x[(d_x > -TINY) & (d_x <= 0.0)] = -TINY d_x[(d_x >= 0.0) & (d_x < +TINY)] = +TINY d_x = cat(X, (d_x[:1, :, :], d_x, d_x[-1:, :, :])) # nxp, ny, nz d_y = dif(Y, phi.val) # nx, nym, nz d_y[(d_y > -TINY) & (d_y <= 0.0)] = -TINY d_y[(d_y >= 0.0) & (d_y < +TINY)] = +TINY d_y = cat(Y, (d_y[:, :1, :], d_y, d_y[:, -1:, :])) # nx, nyp, nz d_z = dif(Z, phi.val) # nx, ny, nzm d_z[(d_z > -TINY) & (d_z <= 0.0)] = -TINY d_z[(d_z >= 0.0) & (d_z < +TINY)] = +TINY d_z = cat(Z, (d_z[:, :, :1], d_z, d_z[:, :, -1:])) # nx, ny, nzp # Ratio of consecutive gradients for positive and negative flow r_x_we = d_x[1:-1, :, :] / d_x[0:-2, :, :] # nxm,ny, nz r_x_ew = d_x[2:, :, :] / d_x[1:-1, :, :] # nxm,ny, nz r_y_sn = d_y[:, 1:-1, :] / d_y[:, 0:-2, :] # nx, nym,nz r_y_ns = d_y[:, 2:, :] / d_y[:, 1:-1, :] # nx, nym,nz r_z_bt = d_z[:, :, 1:-1] / d_z[:, :, 0:-2] # nx, ny, nzm r_z_tb = d_z[:, :, 2:] / d_z[:, :, 1:-1] # nx, ny, nzm flow_we = u_fac >= 0 flow_ew = lnot(flow_we) flow_sn = v_fac >= 0 flow_ns = lnot(flow_sn) flow_bt = w_fac >= 0 flow_tb = lnot(flow_bt) r_x = r_x_we * flow_we + r_x_ew * flow_ew r_y = r_y_sn * flow_sn + r_y_ns * flow_ns r_z = r_z_bt * flow_bt + r_z_tb * flow_tb # Apply a limiter if lim_name == 'upwind': psi_x = r_x * 0.0 psi_y = r_y * 0.0 psi_z = r_z * 0.0 elif lim_name == 'minmod': psi_x = mx(zeros(r_x.shape), mn(r_x, ones(r_x.shape))) psi_y = mx(zeros(r_y.shape), mn(r_y, ones(r_y.shape))) psi_z = mx(zeros(r_z.shape), mn(r_z, ones(r_z.shape))) elif lim_name == 'superbee': psi_x = mx(zeros(r_x.shape), mn(2. * r_x, ones(r_x.shape)), mn(r_x, 2.)) psi_y = mx(zeros(r_y.shape), mn(2. * r_y, ones(r_y.shape)), mn(r_y, 2.)) psi_z = mx(zeros(r_z.shape), mn(2. * r_z, ones(r_z.shape)), mn(r_z, 2.)) elif lim_name == 'koren': psi_x = mx(zeros(r_x.shape), mn(2.*r_x, (2.+r_x)/3., \ 2.*ones(r_x.shape))) psi_y = mx(zeros(r_y.shape), mn(2.*r_y, (2.+r_y)/3., \ 2.*ones(r_y.shape))) psi_z = mx(zeros(r_z.shape), mn(2.*r_z, (2.+r_z)/3., \ 2.*ones(r_z.shape))) flux_fac_lim_x = phi.val[0:-1, :, :] * u_fac * flow_we \ + phi.val[1:, :, :] * u_fac * flow_ew \ + 0.5 * abs(u_fac) * (1 - abs(u_fac) * dt / del_x) \ * (psi_x[:, :, :] * d_x[0:nx-1, :, :] * flow_we \ + psi_x[:, :, :] * d_x[1:nx, :, :] * flow_ew) flux_fac_lim_y = phi.val[:, 0:-1, :] * v_fac * flow_sn \ + phi.val[:, 1:, :] * v_fac * flow_ns \ + 0.5 * abs(v_fac) * (1 - abs(v_fac) * dt / del_y) \ * (psi_y[:, :, :] * d_y[:, 0:ny-1, :] * flow_sn \ + psi_y[:, :, :] * d_y[:, 1:ny, :] * flow_ns) flux_fac_lim_z = phi.val[:, :, 0:-1] * w_fac * flow_bt \ + phi.val[:, :, 1: ] * w_fac * flow_tb \ + 0.5 * abs(w_fac) * (1 - abs(w_fac) * dt / del_z) \ * (psi_z[:, :, :] * d_z[:, :, 0:nz-1] * flow_bt \ + psi_z[:, :, :] * d_z[:, :, 1:nz] * flow_tb) # Pad with boundary values flux_fac_lim_x = cat(X, (phi.bnd[W].val * u_bnd_W, \ flux_fac_lim_x, \ phi.bnd[E].val * u_bnd_E)) flux_fac_lim_y = cat(Y, (phi.bnd[S].val * v_bnd_S, \ flux_fac_lim_y, \ phi.bnd[N].val * v_bnd_N)) flux_fac_lim_z = cat(Z, (phi.bnd[B].val * w_bnd_B, \ flux_fac_lim_z, \ phi.bnd[T].val * w_bnd_T)) # Multiply with face areas flux_fac_lim_x = rho_x_fac * flux_fac_lim_x * a_x_fac flux_fac_lim_y = rho_y_fac * flux_fac_lim_y * a_y_fac flux_fac_lim_z = rho_z_fac * flux_fac_lim_z * a_z_fac # Sum contributions from all directions up c = dif(X, flux_fac_lim_x) + \ dif(Y, flux_fac_lim_y) + \ dif(Z, flux_fac_lim_z) return c # end of function
def main(show_plot=True): """ Docstring. """ # ========================================================================= # # Define problem # # ========================================================================= # Node coordinates xn = nodes(0, 10, 300) yn = nodes(0, 1, 40, 1 / 500, 1 / 500) zn = nodes(0, 3, 3) # Cell coordinates xc = avg(xn) yc = avg(yn) zc = avg(zn) # Cell dimensions nx, ny, nz, dx, dy, dz, rc, ru, rv, rw = cartesian_grid(xn, yn, zn) # Set physical properties rho = zeros(rc) mu = zeros(rc) kappa = zeros(rc) cap = zeros(rc) rho[:, :, :] = 1. mu[:, :, :] = 0.1 kappa[:, :, :] = 0.15 cap[:, :, :] = 1.0 # Time-stepping parameters dt = 0.003 # time step ndt = 1500 # number of time steps # Create unknowns; names, positions and sizes uf = create_unknown('face-u-vel', X, ru, DIRICHLET) vf = create_unknown('face-v-vel', Y, rv, DIRICHLET) wf = create_unknown('face-w-vel', Z, rw, DIRICHLET) t = create_unknown('temperature', C, rc, NEUMANN) p = create_unknown('pressure', C, rc, NEUMANN) # Specify boundary conditions uf.bnd[W].typ[:1, :, :] = DIRICHLET for k in range(0, nz): uf.bnd[W].val[:1, :, k] = par(1.0, yn) uf.bnd[E].typ[:1, :, :] = OUTLET uf.bnd[E].val[:1, :, :] = 1.0 for j in (B, T): uf.bnd[j].typ[:] = NEUMANN vf.bnd[j].typ[:] = NEUMANN wf.bnd[j].typ[:] = NEUMANN t.bnd[W].typ[:1, :, :] = DIRICHLET for k in range(0, nz): t.bnd[W].val[:1, :, k] = 1.0 - yc t.bnd[S].typ[:, :1, :] = DIRICHLET t.bnd[S].val[:, :1, :] = +1.0 t.bnd[N].typ[:, :1, :] = DIRICHLET t.bnd[N].val[:, :1, :] = 0.0 adj_n_bnds(t) adj_n_bnds(p) # Specify initial conditions uf.val[:, :, :] = 1.0 t.val[:, :, :] = 0 obst = zeros(rc) # ========================================================================= # # Solution algorithm # # ========================================================================= # ----------- # # Time loop # # ----------- for ts in range(1, ndt + 1): print_time_step(ts) # ------------------ # Store old values # ------------------ t.old[:] = t.val[:] uf.old[:] = uf.val[:] vf.old[:] = vf.val[:] wf.old[:] = wf.val[:] # ------------------------ # Temperature (enthalpy) # ------------------------ calc_t(t, (uf, vf, wf), (rho * cap), kappa, dt, (dx, dy, dz), obst) # ----------------------- # Momentum conservation # ----------------------- ef = zeros(ru), 150.0 * avg(Y, t.val), zeros(rw) calc_uvw((uf, vf, wf), (uf, vf, wf), rho, mu, \ zeros(rc), ef, dt, (dx, dy, dz), obst) # ---------- # Pressure # ---------- calc_p(p, (uf, vf, wf), rho, dt, (dx, dy, dz), obst) # --------------------- # Velocity correction # --------------------- corr_uvw((uf, vf, wf), p, rho, dt, (dx, dy, dz), obst) # Compute volume balance for checking err = vol_balance((uf, vf, wf), (dx, dy, dz), obst) print('Maximum volume error after correction: %12.5e' % abs(err).max()) # Check the CFL number too cfl = cfl_max((uf, vf, wf), dt, (dx, dy, dz)) print('Maximum CFL number: %12.5e' % cfl) # ===================================================================== # # Visualisation # # ===================================================================== if show_plot: if ts % 150 == 0: plot_isolines(t.val, (uf, vf, wf), (xn, yn, zn), Z) plot_isolines(p.val, (uf, vf, wf), (xn, yn, zn), Z)
def main(show_plot=True): """ inlet example :param show_plot: show plot or not :return: None """ #========================================================================== # # Define problem # #========================================================================== planes = ('XY', 'XZ', 'YZ') tests = array([11, 12, 13, 14, \ 21, 22, 23, 24, \ 31, 32, 33, 34, \ 41, 42, 43, 44]) TEST = tests[floor(random() * 16)] PLANE = planes[floor(random() * 3)] # Node coordinates xn = nodes(0, 1, 160) yn = nodes(0, 1, 160) zn = nodes(0, 0.025, 4) # Cell coordinates xc = avg(xn) yc = avg(yn) zc = avg(zn) # Cell dimensions nx, ny, nz, \ dx, dy, dz, \ rc, ru, rv, rw = cartesian_grid(xn, yn, zn) # Set physical properties rho, mu, cap, kappa = properties_for_air(rc) # Time-stepping parameters dt = 0.15 # time step ndt = 200 # number of time steps # Create unknowns names, positions and sizes uf = create_unknown('face-u-vel', X, ru, DIRICHLET) vf = create_unknown('face-v-vel', Y, rv, DIRICHLET) wf = create_unknown('face-w-vel', Z, rw, DIRICHLET) p = create_unknown('pressure', C, rc, NEUMANN) print(TEST) # Specify boundary conditions if TEST == 11: for k in range(0, nz): uf.bnd[W].val[0, ny // 4:3 * ny // 4, k] = +par(0.01, yn[ny // 4:3 * ny // 4 + 1]) uf.bnd[E].typ[0, ny // 4:3 * ny // 4, k] = OUTLET elif TEST == 12: # vertical mirror from 11 for k in range(0, nz): uf.bnd[E].val[0, ny // 4:3 * ny // 4, k] = -par(0.01, yn[ny // 4:3 * ny // 4 + 1]) uf.bnd[W].typ[0, ny // 4:3 * ny // 4, k] = OUTLET elif TEST == 13: # rotate 11 for k in range(0, nz): vf.bnd[S].val[nx // 4:3 * nx // 4, 0, k] = +par(0.01, xn[nx // 4:3 * nx // 4 + 1]) vf.bnd[N].typ[nx // 4:3 * nx // 4, 0, k] = OUTLET elif TEST == 14: # horizontal mirror 13 for k in range(0, nz): vf.bnd[N].val[nx // 4:3 * nx // 4, 0, k] = -par(0.01, xn[nx // 4:3 * nx // 4 + 1]) vf.bnd[S].typ[nx // 4:3 * nx // 4, 0, k] = OUTLET elif TEST == 21: # 2 exits for k in range(0, nz): uf.bnd[W].val[0, ny // 4:3 * ny // 4, k] = +par(0.01, yn[ny // 4:3 * ny // 4 + 1]) uf.bnd[E].typ[0, 0:ny // 4, k] = OUTLET uf.bnd[E].typ[0, 3 * ny // 4:ny, k] = OUTLET elif TEST == 22: # vertical mirror 21 for k in range(0, nz): uf.bnd[E].val[0, ny // 4:3 * ny // 4, k] = -par(0.01, yn[ny // 4:3 * ny // 4 + 1]) uf.bnd[W].typ[0, 0:ny // 4, k] = OUTLET uf.bnd[W].typ[0, 3 * ny // 4:ny, k] = OUTLET elif TEST == 23: # rotated 21 for k in range(0, nz): vf.bnd[S].val[nx // 4:3 * nx // 4, 0, k] = +par(0.01, xn[nx // 4:3 * nx // 4 + 1]) vf.bnd[N].typ[:nx // 4, 0, k] = OUTLET vf.bnd[N].typ[3 * nx // 4:nx, 0, k] = OUTLET elif TEST == 24: # horizontal mirror of 23 for k in range(0, nz): vf.bnd[N].val[nx // 4:3 * nx // 4, 0, k] = -par(0.01, xn[nx // 4:3 * nx // 4 + 1]) vf.bnd[S].typ[:nx // 4, 0, k] = OUTLET vf.bnd[S].typ[3 * nx // 4:nx, 0, k] = OUTLET elif TEST == 31: # inlet and outlet at the same face for k in range(0, nz): uf.bnd[W].val[0, 3 * ny // 4:ny, k] = +par(0.01, yn[3 * ny // 4:ny + 1]) uf.bnd[W].typ[0, :ny // 4, k] = OUTLET elif TEST == 32: # vertical mirror of 31 for k in range(0, nz): uf.bnd[E].val[0, 3 * ny // 4:ny, k] = -par(0.01, yn[3 * ny // 4:ny + 1]) uf.bnd[E].typ[0, 0:ny // 4, k] = OUTLET elif TEST == 33: # rotated 31 for k in range(0, nz): vf.bnd[S].val[3 * nx // 4:nx, 0, k] = +par(0.01, xn[3 * nx // 4:nx + 1]) vf.bnd[S].typ[:nx // 4, 0, k] = OUTLET elif TEST == 34: # horizontal mirror of 33 for k in range(0, nz): vf.bnd[N].val[3 * nx // 4:nx, 0, k] = -par(0.01, xn[3 * nx // 4:nx + 1]) vf.bnd[N].typ[:nx // 4, 0, k] = OUTLET elif TEST == 41: # inlet and outlet at the same face, one more outlet for k in range(0, nz): uf.bnd[W].val[0, 3 * ny // 4:ny, k] = +par(0.01, yn[3 * ny // 4:ny + 1]) uf.bnd[W].typ[0, :ny // 8, k] = OUTLET uf.bnd[E].typ[0, :ny // 8, k] = OUTLET elif TEST == 42: # vertical mirror of 41 for k in range(0, nz): uf.bnd[E].val[0, 3 * ny // 4:ny, k] = -par(0.01, yn[3 * ny // 4:ny + 1]) uf.bnd[E].typ[0, :ny // 8, k] = OUTLET uf.bnd[W].typ[0, :ny // 8, k] = OUTLET elif TEST == 43: # rotated 41 for k in range(0, nz): vf.bnd[S].val[3 * nx // 4:nx, 0, k] = +par(0.01, xn[3 * nx // 4:nx + 1]) vf.bnd[S].typ[0:nx // 8, 0, k] = OUTLET vf.bnd[N].typ[0:nx // 8, 0, k] = OUTLET elif TEST == 44: # horizontal mirror of 43 for k in range(0, nz): vf.bnd[N].val[3 * ny // 4:nx, 0, k] = -par(0.01, xn[3 * nx // 4:nx + 1]) vf.bnd[N].typ[:nx // 8, 0, k] = OUTLET vf.bnd[S].typ[:nx // 8, 0, k] = OUTLET for j in (B, T): uf.bnd[j].typ[:] = NEUMANN vf.bnd[j].typ[:] = NEUMANN wf.bnd[j].typ[:] = NEUMANN # Create a cylindrical obstacle in the middle just for kicks obst = zeros(rc) for k in range(0, nz): for j in range(0, ny): for i in range(0, nx): dist = sqrt((j - ny / 2 + 1)**2 + (i - nx / 2 + 1)**2) if dist < ny / 4: obst[i, j, k] = 1 # ========================================================================= # # Solution algorithm # # ========================================================================= # ----------- # # Time loop # # ----------- for ts in range(1, ndt + 1): print_time_step(ts) # ------------------ # Store old values # ------------------ uf.old[:] = uf.val[:] vf.old[:] = vf.val[:] wf.old[:] = wf.val[:] # ----------------------- # Momentum conservation # ----------------------- ef = zeros(ru), zeros(rv), zeros(rw) calc_uvw((uf, vf, wf), (uf, vf, wf), rho, mu, zeros(rc), ef, dt, (dx, dy, dz), obst) # ---------- # Pressure # ---------- calc_p(p, (uf, vf, wf), rho, dt, (dx, dy, dz), obst) # --------------------- # Velocity correction # --------------------- corr_uvw((uf, vf, wf), p, rho, dt, (dx, dy, dz), obst) # Compute volume balance for checking err = vol_balance((uf, vf, wf), (dx, dy, dz), obst) print('Maximum volume error after correction: %12.5e' % abs(err).max()) # Check the CFL number too cfl = cfl_max((uf, vf, wf), dt, (dx, dy, dz)) print('Maximum CFL number: %12.5e' % cfl) # ========================================================================= # # Visualisation # # ========================================================================= if show_plot: if ts % 10 == 0: plot_isolines(p.val, (uf, vf, wf), (xn, yn, zn), Z)
def main(show_plot=True): """ Docstring. """ # ========================================================================= # # Define problem # # ========================================================================= xn = nodes(0, 1, 64, 1.0 / 256, 1.0 / 256) yn = nodes(0, 1, 64, 1.0 / 256, 1.0 / 256) zn = nodes(0, 0.1, 5) # Cell dimensions nx, ny, nz, dx, dy, dz, rc, ru, rv, rw = cartesian_grid(xn, yn, zn) # Set physical properties grashof = 1.4105E+06 prandtl = 0.7058 rho = zeros(rc) mu = zeros(rc) kappa = zeros(rc) cap = zeros(rc) rho[:, :, :] = 1.0 mu[:, :, :] = 1.0 / sqrt(grashof) kappa[:, :, :] = 1.0 / (prandtl * sqrt(grashof)) cap[:, :, :] = 1.0 # Time-stepping parameters dt = 0.02 # time step ndt = 1000 # number of time steps # Create unknowns; names, positions and sizes uf = create_unknown('face-u-vel', X, ru, DIRICHLET) vf = create_unknown('face-v-vel', Y, rv, DIRICHLET) wf = create_unknown('face-w-vel', Z, rw, DIRICHLET) t = create_unknown('temperature', C, rc, NEUMANN) p = create_unknown('pressure', C, rc, NEUMANN) p_tot = zeros(rc) # This is a new test t.bnd[W].typ[:] = DIRICHLET t.bnd[W].val[:] = -0.5 t.bnd[E].typ[:] = DIRICHLET t.bnd[E].val[:] = +0.5 for j in (B, T): uf.bnd[j].typ[:] = NEUMANN vf.bnd[j].typ[:] = NEUMANN wf.bnd[j].typ[:] = NEUMANN obst = zeros(rc) # ========================================================================= # # Solution algorithm # # ========================================================================= # ----------- # # Time loop # # ----------- for ts in range(1, ndt + 1): print_time_step(ts) # ------------------ # Store old values # ------------------ t.old[:] = t.val[:] uf.old[:] = uf.val[:] vf.old[:] = vf.val[:] wf.old[:] = wf.val[:] # ------------------------ # Temperature (enthalpy) # ------------------------ calc_t(t, (uf, vf, wf), (rho * cap), kappa, dt, (dx, dy, dz), obst) # ----------------------- # Momentum conservation # ----------------------- ef = zeros(ru), avg(Y, t.val), zeros(rw) calc_uvw((uf, vf, wf), (uf, vf, wf), rho, mu, \ p_tot, ef, dt, (dx, dy, dz), obst) # ---------- # Pressure # ---------- calc_p(p, (uf, vf, wf), rho, dt, (dx, dy, dz), obst) p_tot = p_tot + p.val # --------------------- # Velocity correction # --------------------- corr_uvw((uf, vf, wf), p, rho, dt, (dx, dy, dz), obst) # Compute volume balance for checking err = vol_balance((uf, vf, wf), (dx, dy, dz), obst) print('Maximum volume error after correction: %12.5e' % abs(err).max()) # Check the CFL number too cfl = cfl_max((uf, vf, wf), dt, (dx, dy, dz)) print('Maximum CFL number: %12.5e' % cfl) # ===================================================================== # # Visualisation # # ===================================================================== if show_plot: if ts % 20 == 0: plot_isolines(t.val, (uf, vf, wf), (xn, yn, zn), Z)
def main(show_plot=True): """Example """ #========================================================================== # # Define problem # #========================================================================== # Node coordinates xn = nodes(0, 1, 256) yn = nodes(0, 0.125, 32) zn = nodes(0, 0.125, 4) # Cell coordinates xc = avg(xn) yc = avg(yn) zc = avg(zn) # Cell dimensions nx, ny, nz, \ dx, dy, dz, \ rc, ru, rv, rw = cartesian_grid(xn, yn, zn) # Set physical properties rho, mu, cap, kappa = properties_for_air(rc) # Time-stepping parameters dt = 0.002 # time step ndt = 5000 # number of time steps # Create unknowns; names, positions and sizes uf = create_unknown('face-u-vel', X, ru, DIRICHLET) vf = create_unknown('face-v-vel', Y, rv, DIRICHLET) wf = create_unknown('face-w-vel', Z, rw, DIRICHLET) p = create_unknown('pressure', C, rc, NEUMANN) # Specify boundary conditions uf.bnd[W].typ[:1, :, :] = DIRICHLET for k in range(0, nz): uf.bnd[W].val[:1, :, k] = par(0.1, yn) uf.bnd[E].typ[:1, :, :] = OUTLET for j in (B, T): uf.bnd[j].typ[:] = NEUMANN vf.bnd[j].typ[:] = NEUMANN wf.bnd[j].typ[:] = NEUMANN adj_n_bnds(p) obst = zeros(rc) for j in range(0, 24): for i in range(64 + j, 64 + 24): for k in range(0, nz): obst[i, j, k] = 1 # ========================================================================= # # Solution algorithm # # ========================================================================= # ----------- # # Time loop # # ----------- for ts in range(1, ndt + 1): print_time_step(ts) # ------------------ # Store old values # ------------------ uf.old[:] = uf.val[:] vf.old[:] = vf.val[:] wf.old[:] = wf.val[:] # ----------------------- # Momentum conservation # ----------------------- ef = zeros(ru), zeros(rv), zeros(rw) calc_uvw((uf, vf, wf), (uf, vf, wf), rho, mu, \ zeros(rc), ef, dt, (dx, dy, dz), obst) # ---------- # Pressure # ---------- calc_p(p, (uf, vf, wf), rho, dt, (dx, dy, dz), obst) # --------------------- # Velocity correction # --------------------- corr_uvw((uf, vf, wf), p, rho, dt, (dx, dy, dz), obst) # Compute volume balance for checking err = vol_balance((uf, vf, wf), (dx, dy, dz), obst) print('Maximum volume error after correction: %12.5e' % abs(err).max()) # Check the CFL number too cfl = cfl_max((uf, vf, wf), dt, (dx, dy, dz)) print('Maximum CFL number: %12.5e' % cfl) # ===================================================================== # # Visualisation # # ===================================================================== if show_plot: if ts % 20 == 0: plot_isolines(p.val, (uf, vf, wf), (xn, yn, zn), Z)
def main(show_plot=True): """ Docstring. """ # ========================================================================= # # Define problem # # ========================================================================= # Node coordinates xn = nodes(0, 1.25, 256) yn = nodes(0, 0.125, 32) zn = nodes(0, 0.125, 32) # Cell coordinates xc = avg(xn) yc = avg(yn) zc = avg(zn) # Cell dimensions nx, ny, nz, \ dx, dy, dz, \ rc, ru, rv, rw = cartesian_grid(xn, yn, zn) # Set physical properties rho, mu, cap, kappa = properties_for_air(rc) # Time-stepping parameters dt = 0.005 # time step ndt = 2000 # number of time steps # Create unknowns; names, positions and sizes uc = create_unknown('cell-u-vel', C, rc, DIRICHLET) vc = create_unknown('cell-v-vel', C, rc, DIRICHLET) wc = create_unknown('cell-w-vel', C, rc, DIRICHLET) uf = create_unknown('face-u-vel', X, ru, DIRICHLET) vf = create_unknown('face-v-vel', Y, rv, DIRICHLET) wf = create_unknown('face-w-vel', Z, rw, DIRICHLET) p = create_unknown('pressure', C, rc, NEUMANN) # Specify boundary conditions uc.bnd[W].typ[:1, :, :] = DIRICHLET uc.bnd[W].val[:1, :, :] = 0.1 * outer(par(1.0, yn), par(1.0, zn)) uc.bnd[E].typ[:1, :, :] = OUTLET for j in (B, T): uc.bnd[j].typ[:] = NEUMANN vc.bnd[j].typ[:] = NEUMANN wc.bnd[j].typ[:] = NEUMANN adj_n_bnds(p) # Create obstacles obst = zeros(rc) class key: """ Class Docstring. """ ip = -1 im = -1 jp = -1 jm = -1 kp = -1 km = -1 block = (key(), key(), key(), key()) th = 5 block[0].im = 3 * nx / 16 # i minus block[0].ip = block[0].im + th # i plus block[0].jm = 0 # j minus block[0].jp = 3 * ny / 4 # j plus block[0].km = 0 # k minus block[0].kp = 3 * ny / 4 # k plus block[1].im = 5 * nx / 16 # i minus block[1].ip = block[1].im + th # i plus block[1].jm = ny / 4 # j minus block[1].jp = ny # j plus block[1].km = ny / 4 # k minus block[1].kp = ny # k plus block[2].im = 7 * nx / 16 # i minus block[2].ip = block[2].im + th # i plus block[2].jm = 0 # j minus block[2].jp = 3 * ny / 4 # j plus block[2].km = 0 # k minus block[2].kp = 3 * ny / 4 # k plus block[3].im = 9 * nx / 16 # i minus block[3].ip = block[3].im + th # i plus block[3].jm = ny / 4 # j minus block[3].jp = ny # j plus block[3].km = ny / 4 # k minus block[3].kp = ny # k plus for o in range(0, 4): for i in range(floor(block[o].im), floor(block[o].ip)): for j in range(floor(block[o].jm), floor(block[o].jp)): for k in range(floor(block[o].km), floor(block[o].kp)): obst[i, j, k] = 1 # ========================================================================= # # Solution algorithm # # ========================================================================= # ----------- # # Time loop # # ----------- for ts in range(1, ndt + 1): print_time_step(ts) # ------------------ # Store old values # ------------------ uc.old[:] = uc.val[:] vc.old[:] = vc.val[:] wc.old[:] = wc.val[:] # ----------------------- # Momentum conservation # ----------------------- ef = zeros(rc), zeros(rc), zeros(rc) calc_uvw((uc, vc, wc), (uf, vf, wf), rho, mu, zeros(rc), ef, dt, (dx, dy, dz), obst) # ---------- # Pressure # ---------- calc_p(p, (uf, vf, wf), rho, dt, (dx, dy, dz), obst) # --------------------- # Velocity correction # --------------------- corr_uvw((uc, vc, wc), p, rho, dt, (dx, dy, dz), obst) corr_uvw((uf, vf, wf), p, rho, dt, (dx, dy, dz), obst) # Compute volume balance for checking err = vol_balance((uf, vf, wf), (dx, dy, dz), obst) print('Maximum volume error after correction: %12.5e' % abs(err).max()) # Check the CFL number too cfl = cfl_max((uc, vc, wc), dt, (dx, dy, dz)) print('Maximum CFL number: %12.5e' % cfl) # ===================================================================== # # Visualisation # # ===================================================================== if show_plot: if ts % 20 == 0: plot_isolines(p.val, (uc, vc, wc), (xn, yn, zn), Y) plot_isolines(p.val, (uc, vc, wc), (xn, yn, zn), Z)
def create_matrix(phi, inn, mu, dxyz, obst, obc): """ pos - position of variable (C - central, X - staggered in x direction, Y - staggered in y direction, Z - staggered in z direction) inn - innertial term mu - viscous coefficient dx, dy, dz - cell size in x, y and z directions obc - obstacles's boundary condition, ('n' - Neumann, 'd' - Dirichlet) ------------------------------------------------------------------------- """ # Unpack tuples dx, dy, dz = dxyz res = phi.val.shape # ------------------------------- # Create right hand side vector # ------------------------------- b = zeros(res) # ------------------------------------ # Create default matrix coefficients # ------------------------------------ coefficients = namedtuple('matrix_diagonal', 'W E S N B T P') c = coefficients(zeros(res), zeros(res), zeros(res), \ zeros(res), zeros(res), zeros(res), \ zeros(res)) d = phi.pos # Handle central coefficient due to innertia c.P[:] = avg(d, inn) * avg(d, dx * dy * dz) # Pre-compute geometrical quantities sx = dy * dz sy = dx * dz sz = dx * dy if d != X: c.W[:] = cat(X, ( \ avg(d, mu[:1, :, :]) \ * avg(d, sx[:1, :, :]) / avg(d, (dx[:1, :, :])/2.0), \ avg(d, avg(X, mu)) * avg(d, avg(X, sx)) / avg(d, avg(X, dx)))) c.E[:] = cat(X, ( \ avg(d, avg(X, mu)) * avg(d, avg(X, sx)) / avg(d, avg(X, dx)), \ avg(d, mu[-1:, :, :]) \ * avg(d, sx[-1:, :, :])/ avg(d, (dx[-1:, :, :])/2.0))) if d != Y: c.S[:] = cat(Y, ( \ avg(d, mu[:, :1, :]) \ * avg(d, sy[:, :1, :]) / avg(d, (dy[:, :1, :])/2.0), \ avg(d, avg(Y, mu)) * avg(d, avg(Y, sy)) / avg(d, avg(Y, dy)))) c.N[:] = cat(Y, ( \ avg(d, avg(Y, mu)) * avg(d, avg(Y, sy)) / avg(d, avg(Y, dy)), \ avg(d, mu[:, -1:, :]) \ * avg(d, sy[:, -1:, :]) / avg(d, (dy[:, -1:, :])/2.0))) if d != Z: c.B[:] = cat(Z, ( \ avg(d, mu[:, :, :1]) \ * avg(d, sz[:, :, :1]) / avg(d, (dz[:, :, :1])/2.0), \ avg(d, avg(Z, mu)) * avg(d, avg(Z, sz)) / avg(d, avg(Z, dz)))) c.T[:] = cat(Z, ( \ avg(d, avg(Z, mu)) * avg(d, avg(Z, sz)) / avg(d, avg(Z, dz)), \ avg(d, mu[:, :, -1:]) \ * avg(d, sz[:, :, -1:]) / avg(d, (dz[:, :, -1:])/2.0))) # --------------------------------- # Correct for staggered variables # --------------------------------- if d == X: c.W[:] = mu[0:-1, :, :] * sx[0:-1, :, :] / dx[0:-1, :, :] c.E[:] = mu[1:, :, :] * sx[1:, :, :] / dx[1:, :, :] elif d == Y: c.S[:] = mu[:, 0:-1, :] * sy[:, 0:-1, :] / dy[:, 0:-1, :] c.N[:] = mu[:, 1:, :] * sy[:, 1:, :] / dy[:, 1:, :] elif d == Z: c.B[:] = mu[:, :, 0:-1] * sz[:, :, 0:-1] / dz[:, :, 0:-1] c.T[:] = mu[:, :, 1:] * sz[:, :, 1:] / dz[:, :, 1:] # ----------------------------------------------------------------------- # Zero them (correct them) for vanishing derivative boundary condition. # ----------------------------------------------------------------------- # The values defined here will be false (numerical value 0) # wherever there is or Neumann boundary condition. c.W[:1, :, :] *= (phi.bnd[W].typ[:] == DIRICHLET) c.E[-1:, :, :] *= (phi.bnd[E].typ[:] == DIRICHLET) c.S[:, :1, :] *= (phi.bnd[S].typ[:] == DIRICHLET) c.N[:, -1:, :] *= (phi.bnd[N].typ[:] == DIRICHLET) c.B[:, :, :1] *= (phi.bnd[B].typ[:] == DIRICHLET) c.T[:, :, -1:] *= (phi.bnd[T].typ[:] == DIRICHLET) # -------------------------------------------- # Fill the source terms with boundary values # -------------------------------------------- b[:1, :, :] += c.W[:1, :, :] * phi.bnd[W].val[:1, :, :] b[-1:, :, :] += c.E[-1:, :, :] * phi.bnd[E].val[:1, :, :] b[:, :1, :] += c.S[:, :1, :] * phi.bnd[S].val[:, :1, :] b[:, -1:, :] += c.N[:, -1:, :] * phi.bnd[N].val[:, :1, :] b[:, :, :1] += c.B[:, :, :1] * phi.bnd[B].val[:, :, :1] b[:, :, -1:] += c.T[:, :, -1:] * phi.bnd[T].val[:, :, :1] # --------------------------------------- # Correct system matrices for obstacles # --------------------------------------- if obst.any() != 0: c = obst_mod_matrix(phi, c, obst, obc) # ----------------------------------------------- # Add all neighbours to the central matrix, # and zero the coefficients towards boundaries # ----------------------------------------------- c.P[:] += c.W[:] + c.E[:] + c.S[:] + c.N[:] + c.B[:] + c.T[:] c.W[:1, :, :] = 0.0 c.E[-1:, :, :] = 0.0 c.S[:, :1, :] = 0.0 c.N[:, -1:, :] = 0.0 c.B[:, :, :1] = 0.0 c.T[:, :, -1:] = 0.0 # ---------------------- # Create sparse matrix # ---------------------- nx, ny, nz = res n = nx * ny * nz data = array([reshape(c.P, n), \ reshape(-c.W, n), reshape(-c.E, n), \ reshape(-c.S, n), reshape(-c.N, n), \ reshape(-c.B, n), reshape(-c.T, n)]) diag = array([0, +ny * nz, -ny * nz, +nz, -nz, +1, -1]) A = spdiags(data, diag, n, n) return A, b # end of function