def corr_uvw(uvw, p, rho, dt, dxyz, obstacle=None, verbose=True): # ----------------------------------------------------------------------------- """ Args: uvw: .... Tuple with three staggered or centered velocity components. (Each component is object of type "Unknown"). p: ...... Unknown holding the pressure correction. rho: .... Three-dimensional array holding density for all cells. dt: ..... Time step dxyz: ... Tuple holding cell dimensions in "x", "y" and "z" directions. Each cell dimension is a three-dimensional array. obstacle: Obstacle, three-dimensional array with zeros and ones. It is zero in fluid, one in solid. Returns: None, but input argument uvw is modified. """ # 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 obstacle is not None: p_x = obst_zero_val(X, p_x, obstacle) p_y = obst_zero_val(Y, p_y, obstacle) p_z = obst_zero_val(Z, p_z, obstacle) # 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 # Compute volume balance for checking if verbose is True: if not uvw[X].pos == C: err = vol_balance(uvw, (dx, dy, dz), obstacle) write.at(__name__) print(" Maximum volume error after correction: %12.5e" % abs(err).max()) return # end of function
def vol_balance(uvwf, dxyz, obst): # ----------------------------------------------------------------------------- """ Args: uvwf: Tuple with three staggered velocity components (where each component is created with "create_unknown" function. dxyz: Tuple holding cell dimensions in "x", "y" and "z" directions. Each cell dimension is a three-dimensional array. obst: Obstacle, three-dimensional matrix with zeros and ones. It is zero in fluid, one in solid. Note: "obst" is an optional parameter. If it is not sent, the source won't be set to zero inside the obstacle. That is important for calculation of pressure, see function "calc_p" as well. """ # Unpack tuples dx, dy, dz = dxyz uf, vf, wf = uvwf # Compute it throughout the domain src = - dif_x(cat_x((uf.bnd[W].val, uf.val, uf.bnd[E].val)))*dy*dz \ - dif_y(cat_y((vf.bnd[S].val, vf.val, vf.bnd[N].val)))*dx*dz \ - dif_z(cat_z((wf.bnd[B].val, wf.val, wf.bnd[T].val)))*dx*dy # Zero it inside obstacles, if obstacle is sent as parameter if obst.any() != 0: src = obst_zero_val(C, src, obst) return src # end of function
def corr_uvw(uvw, p, rho, dt, dxyz, obst): # ----------------------------------------------------------------------------- """ Args: uvw: Tuple with three staggered or centered velocity components (each component is created with "create_unknown" function. p: Unknown holding the pressure correction. rho: Three-dimensional matrix holding density for all cells. dt: Time step dxyz: Tuple holding cell dimensions in "x", "y" and "z" directions. Each cell dimension is a three-dimensional array. obst: Obstacle, three-dimensional matrix with zeros and ones. It is zero in fluid, one in solid. Returns: none, but input argument uvw is modified. """ # 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 calc_p(p, uvwf, rho, dt, dxyz, obstacle=None, verbose=True): # ----------------------------------------------------------------------------- """ Args: p: ...... Object of the type "Unknown", holding the pressure. uvwf: ... Tuple with three staggered velocity components (where each component is an object of type "Unknown". rho: .... Three-dimensional array holding density for all cells. dt: ..... Time step. dxyz: ... Tuple holding cell dimensions in "x", "y" and "z" directions. Each cell dimension is a three-dimensional array. obstacle: Obstacle, three-dimensional array with zeros and ones. It is zero in fluid, one in solid. Returns: None, but input argument p is modified! """ # Fetch the resolution rc = p.val.shape # Create system matrix and right hand side A_p = diffusion(p, zeros(rc), dt / rho, dxyz, obstacle, NEUMANN) b_p = zeros(rc) # Compute the source for the pressure. Important: don't send "obst" # as a parameter here, because you don't want to take it into # account at this stage. After velocity corrections, you should. b_p = vol_balance(uvwf, dxyz, zeros(rc)) if verbose is True: write.at(__name__) print(" Volume error before correction : %12.5e" % abs(b_p).max()) print(" Volume imbalance before correction : %12.5e" % b_p.sum()) # Solve for pressure p.val[:] = bicgstab(A_p, p, b_p, TOL, False) # Anchor it to values around zero (the absolute value of pressure # correction can get really volatile. Although it is in prinicple not # important for incompressible flows, it is ugly for post-processing. p.val[:] = p.val[:] - p.val.mean() # Set to zero in obstacle (it can get strange # values during the iterative solution procedure) if obstacle is not None: p.val[:] = obst_zero_val(p.pos, p.val, obstacle) # Finally adjust the boundary values adj_n_bnds(p) return # end of function
def calc_p(p, uvwf, rho, dt, dxyz, obst): # ----------------------------------------------------------------------------- """ Args: p: Pressure unknown (created with "pyns.create_unknown" function) uvwf: Tuple with three staggered velocity components (where each component is created with "pyns.create_unknown" function. rho: Three-dimensional matrix holding density for all cells. dt: Time step. dxyz: Tuple holding cell dimensions in "x", "y" and "z" directions. Each cell dimension is a three-dimensional matrix. obst: Obstacle, three-dimensional matrix with zeros and ones. It is zero in fluid, one in solid. Returns: none, but input argument p is modified! """ # Fetch the resolution rc = p.val.shape # Create system matrix and right hand side A_p = create_matrix(p, zeros(rc), dt / rho, dxyz, obst, NEUMANN) b_p = zeros(rc) # Compute the source for the pressure. Important: don't send "obst" # as a parameter here, because you don't want to take it into # account at this stage. After velocity corrections, you should. b_p = vol_balance(uvwf, dxyz, zeros(rc)) print('Maximum volume error before correction: %12.5e' % abs(b_p).max()) print('Volume imbalance before correction : %12.5e' % b_p.sum()) # Solve for pressure p.val[:] = bicgstab(A_p, p, b_p, TOL, False) # Anchor it to values around zero (the absolute value of pressure # correction can get really volatile. Although it is in prinicple not # important for incompressible flows, it is ugly for post-processing. p.val[:] = p.val[:] - p.val.mean() # Set to zero in obstacle (it can get strange # values during the iterative solution procedure) if obst.any() != 0: p.val[:] = obst_zero_val(p.pos, p.val, obst) # Finally adjust the boundary values p = adj_n_bnds(p) return # end of function
def calc_uvw(uvw, uvwf, rho, mu, dt, dxyz, obstacle=None, pressure=None, force=None, under_relaxation=1.0, advection_scheme="superbee"): # ----------------------------------------------------------------------------- """ Args: uvw: ............ Tuple with three velocity components, staggered or collocated. (Each component is object of type "Unknown".) uvwf: ........... Tuple with three staggered velocity components. (Each component is object of type "Unknown".) rho: ............ Three-dimensional array holding density for all cells. mu: ............. Three-dimensional array holding dynamic viscosity. pressure: ....... Object "Unknown" holding total pressure. force: .......... Tuple containing three-dimensional matrices holding external forces in each direction. dt: ............. Time step. dxyz: ........... Tuple holding cell dimensions in "x", "y" and "z" directions. Each component (each cell dimension) is a three-dimensional array. obstacle: ....... Obstacle, three-dimensional array with zeros and ones. It is zero in fluid, one in solid. under_relaxation: Under relaxation factor. advection_scheme: Advection scheme. Returns: None, but input argument uvw is modified! """ # Unpack tuples u, v, w = uvw uf, vf, wf = uvwf dx, dy, dz = dxyz # 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 system matrices and right hand sides A_u = diffusion(u, rho / dt, mu, dxyz, obstacle, DIRICHLET) A_v = diffusion(v, rho / dt, mu, dxyz, obstacle, DIRICHLET) A_w = diffusion(w, rho / dt, mu, dxyz, obstacle, DIRICHLET) b_u = zeros(ru) b_v = zeros(rv) b_w = zeros(rw) # Advection terms for momentum c_u = advection(rho, u, uvwf, dxyz, dt, advection_scheme, matrix=A_u) c_v = advection(rho, v, uvwf, dxyz, dt, advection_scheme, matrix=A_v) c_w = advection(rho, w, uvwf, dxyz, dt, advection_scheme, matrix=A_w) # Innertial term for momentum (this works for collocated and staggered) A_u.C += avg(u.pos, rho) * avg(u.pos, dv) / dt A_v.C += avg(v.pos, rho) * avg(v.pos, dv) / dt A_w.C += avg(w.pos, rho) * avg(w.pos, dv) / dt 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 # Full force terms for momentum equations (collocated and staggered) f_u = b_u - c_u + i_u f_v = b_v - c_v + i_v f_w = b_w - c_w + i_w # Compute staggered pressure gradients if pressure is not None: pressure.exchange() p_tot_x = dif_x(pressure.val) / avg_x(dx) p_tot_y = dif_y(pressure.val) / avg_y(dy) p_tot_z = dif_z(pressure.val) / 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) f_u -= p_st_u f_v -= p_st_v f_w -= p_st_w # If external force is given if force is not None: # Unpack the tuple e_u, e_v, e_w = force # Add to right hand side f_u += e_u * avg(u.pos, dv) f_v += e_v * avg(v.pos, dv) f_w += e_w * avg(w.pos, dv) # Take care of obsts in the domian if obstacle is not None: f_u = obst_zero_val(u.pos, f_u, obstacle) f_v = obst_zero_val(v.pos, f_v, obstacle) f_w = obst_zero_val(w.pos, f_w, obstacle) # Solve for velocities ur = under_relaxation u.val[:] = (1 - ur) * u.val[:] + ur * bicgstab(A_u, u, f_u, TOL, False) v.val[:] = (1 - ur) * v.val[:] + ur * bicgstab(A_v, v, f_v, TOL, False) w.val[:] = (1 - ur) * w.val[:] + ur * bicgstab(A_w, w, f_w, TOL, False) # Update velocities in boundary cells adj_o_bnds((u, v, w), (dx, dy, dz), dt) adj_n_bnds(u) adj_n_bnds(v) adj_n_bnds(w) # Update face velocities # (For collocated arrangement also substract cell-centered # pressure gradients and add staggered pressure gradients) if d == C: uf.val[:] = avg_x(u.val) vf.val[:] = avg_y(v.val) wf.val[:] = avg_z(w.val) if pressure is not None: uf.val[:] += avg_x(dt / rho * p_tot_x) uf.val[:] -= dt / avg_x(rho) * (dif_x(pressure.val) / avg_x(dx)) vf.val[:] += avg_y(dt / rho * p_tot_y) vf.val[:] -= dt / avg_y(rho) * (dif_y(pressure.val) / avg_y(dy)) wf.val[:] += avg_z(dt / rho * p_tot_z) wf.val[:] -= dt / avg_z(rho) * (dif_z(pressure.val) / 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 obstacle is not None: uf.val[:] = obst_zero_val(X, uf.val, obstacle) vf.val[:] = obst_zero_val(Y, vf.val, obstacle) wf.val[:] = obst_zero_val(Z, wf.val, obstacle) return # end of function