Source code for oasismove.solvers.NSfracStep.IPCS_ABCN_Move

__author__ = "Mikael Mortensen <mikaem@math.uio.no>"
__date__ = "2013-11-06"
__copyright__ = "Copyright (C) 2013 " + __author__
__license__ = "GNU Lesser GPL version 3 or any later version"

from dolfin import *
from petsc4py import PETSc as _PETSc

from oasismove.problems import u_dot_n
from oasismove.solvers.NSfracStep import *
from oasismove.solvers.NSfracStep import __all__


[docs]def setup( u_components, u, v, p, q, u_w, v_w, bcs, les_model, nut_, scalar_components, V, Q, x_, p_, u_, velocity_update_solver, assemble_matrix, homogenize, GradFunction, DivFunction, LESsource, nn_model, nunn_, NNsource, mesh, **NS_namespace ): """Preassemble mass and diffusion matrices. Set up and prepare all equations to be solved. Called once, before going into time loop. """ # Mass matrix M = assemble_matrix(inner(u, v) * dx) # Stiffness matrix (without viscosity coefficient) K = assemble_matrix(inner(grad(u), grad(v)) * dx) # Allocate stiffness matrix for LES that changes with time KT = ( None if les_model == "NoModel" and nn_model == "NoModel" else (Matrix(M), inner(grad(u), grad(v))) ) # Pressure Laplacian. Ap = assemble_matrix(inner(grad(q), grad(p)) * dx, bcs["p"]) # Mesh velocity Laplacian # alpha = 1 / CellVolume(mesh) a_mesh = inner(grad(u_w), grad(v_w)) * dx A_mesh = Matrix(assemble_matrix(a_mesh)) # Allocate coefficient matrix (needs reassembling) A = Matrix(M) # Allocate Function for holding and computing the velocity divergence on Q divu = DivFunction(u_, Q, name="divu", method=velocity_update_solver) # Allocate a dictionary of Functions for holding and computing pressure gradients gradp = { ui: GradFunction( p_, V, i=i, name="dpd" + ("x", "y", "z")[i], bcs=homogenize(bcs[ui]), method=velocity_update_solver, ) for i, ui in enumerate(u_components) } # Create dictionary to be returned into global NS namespace d = dict(A=A, M=M, K=K, Ap=Ap, a_mesh=a_mesh, A_mesh=A_mesh, divu=divu, gradp=gradp) # Allocate coefficient matrix and work vectors for scalars. Matrix differs # from velocity in boundary conditions only if len(scalar_components) > 0: d.update(Ta=Matrix(M)) if len(scalar_components) > 1: # For more than one scalar we use the same linear algebra solver for all. # For this to work we need some additional tensors. The extra matrix # is required since different scalars may have different boundary conditions Tb = Matrix(M) bb = Vector(x_[scalar_components[0]]) bx = Vector(x_[scalar_components[0]]) d.update(Tb=Tb, bb=bb, bx=bx) # Setup for solving convection u_ab = as_vector([Function(V) for i in range(len(u_components))]) a_conv = inner(v, dot(u_ab, nabla_grad(u))) * dx a_scalar = a_conv LT = None if les_model == "NoModel" else LESsource(nut_, u_ab, V, name="LTd") NT = None if nn_model == "NoModel" else NNsource(nunn_, u_ab, V, name="NTd") if bcs["p"] == []: attach_pressure_nullspace(Ap, x_, Q) d.update(u_ab=u_ab, a_conv=a_conv, a_scalar=a_scalar, LT=LT, KT=KT, NT=NT) return d
[docs]def get_solvers( use_krylov_solvers, krylov_solvers, krylov_solvers_w, scalar_components, velocity_krylov_solver, pressure_krylov_solver, scalar_krylov_solver, mesh_velocity_krylov_solver, **NS_namespace ): """Return linear solvers. We are solving for - mesh velocity - tentative velocity - pressure correction and possibly: - scalars """ if use_krylov_solvers: # tentative velocity solver u_prec = PETScPreconditioner(velocity_krylov_solver["preconditioner_type"]) u_sol = PETScKrylovSolver(velocity_krylov_solver["solver_type"], u_prec) u_sol.parameters.update(krylov_solvers) # pressure solver p_prec = PETScPreconditioner(pressure_krylov_solver["preconditioner_type"]) p_sol = PETScKrylovSolver(pressure_krylov_solver["solver_type"], p_prec) p_sol.parameters.update(krylov_solvers) p_sol.set_reuse_preconditioner(True) # mesh equation solver w_prec = PETScPreconditioner(mesh_velocity_krylov_solver["preconditioner_type"]) w_sol = PETScKrylovSolver(mesh_velocity_krylov_solver["solver_type"], w_prec) w_sol.parameters.update(krylov_solvers_w) sols = [u_sol, p_sol, w_sol] # scalar solver if len(scalar_components) > 0: c_prec = PETScPreconditioner(scalar_krylov_solver["preconditioner_type"]) c_sol = PETScKrylovSolver(scalar_krylov_solver["solver_type"], c_prec) c_sol.parameters.update(krylov_solvers) sols.append(c_sol) else: sols.append(None) else: # tentative velocity solver u_sol = LUSolver() # pressure solver p_sol = LUSolver() sols = [u_sol, p_sol] # scalar solver if len(scalar_components) > 0: c_sol = LUSolver() sols.append(c_sol) else: sols.append(None) return sols
[docs]def assemble_first_inner_iter( A, a_conv, dt, M, scalar_components, les_model, nn_model, a_scalar, K, nu, nut_, nunn_, u_components, LT, KT, NT, b_tmp, b0, x_1, x_2, u_ab, bcs, mesh, boundary, u, v, backflow_facets, backflow_beta, wx_, bw_tmp, bw0, use_supg, **NS_namespace ): """Called on first inner iteration of velocity/pressure system. Assemble convection matrix, compute rhs of tentative velocity and reset coefficient matrix for solve. """ Timer("Assemble first inner iter") # Update u_ab used as convecting velocity for i, ui in enumerate(u_components): u_ab[i].vector().zero() u_ab[i].vector().axpy(1.5, x_1[ui]) u_ab[i].vector().axpy(-0.5, x_2[ui]) u_ab[i].vector().axpy(-1, wx_[ui]) A = assemble(a_conv, tensor=A) A *= -0.5 # Negative convection on the rhs A.axpy(1.0 / dt, M, True) # Add mass # Set up scalar matrix for rhs using the same convection as velocity if len(scalar_components) > 0: Ta = NS_namespace["Ta"] if a_scalar is a_conv and not use_supg: Ta.zero() Ta.axpy(1.0, A, True) # Add diffusion and compute rhs for all velocity components A.axpy(-0.5 * nu, K, True) if les_model != "NoModel": assemble(nut_ * KT[1] * dx, tensor=KT[0]) A.axpy(-0.5, KT[0], True) if nn_model != "NoModel": assemble(nunn_ * KT[1] * dx, tensor=KT[0]) A.axpy(-0.5, KT[0], True) for i, ui in enumerate(u_components): # Start with body force b_tmp[ui].zero() b_tmp[ui].axpy(1.0, b0[ui]) # Add transient, convection and diffusion b_tmp[ui].axpy(1.0, A * x_1[ui]) if les_model != "NoModel": LT.assemble_rhs(i) b_tmp[ui].axpy(1.0, LT.vector()) if nn_model != "NoModel": NT.assemble_rhs(i) b_tmp[ui].axpy(1.0, NT.vector()) bw_tmp[ui].zero() bw_tmp[ui].axpy(1.0, bw0[ui]) # Reset matrix for lhs A *= -1.0 A.axpy(2.0 / dt, M, True) # Add backflow stabilization for ID in backflow_facets: ds = Measure("ds", domain=mesh, subdomain_data=boundary) n = FacetNormal(mesh) B = assemble(u_dot_n(u_ab, n) * dot(u, v) * ds(ID)) as_backend_type(A).mat().axpy( -backflow_beta * 0.5, as_backend_type(B).mat(), _PETSc.Mat.Structure.SUBSET_NONZERO_PATTERN, ) for i, ui in enumerate(u_components): b_tmp[ui].axpy(backflow_beta * 0.5, B * x_1[ui]) [bc.apply(A) for bc in bcs["u0"]]
def attach_pressure_nullspace(Ap, x_, Q): """Create null space basis object and attach to Krylov solver.""" null_vec = Vector(x_["p"]) Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0 / null_vec.norm("l2") Aa = as_backend_type(Ap) null_space = VectorSpaceBasis([null_vec]) Aa.set_nullspace(null_space) Aa.null_space = null_space
[docs]def velocity_tentative_assemble(ui, b, b_tmp, p_, gradp, **NS_namespace): """Add pressure gradient to rhs of tentative velocity system.""" b[ui].zero() b[ui].axpy(1.0, b_tmp[ui]) gradp[ui].assemble_rhs(p_) b[ui].axpy(-1.0, gradp[ui].rhs)
[docs]def velocity_tentative_solve( ui, A, bcs, x_, x_2, u_sol, b, udiff, use_krylov_solvers, **NS_namespace ): """Linear algebra solve of tentative velocity component.""" [bc.apply(b[ui]) for bc in bcs[ui]] # x_2 only used on inner_iter 1, so use here as work vector x_2[ui].zero() x_2[ui].axpy(1.0, x_[ui]) t1 = Timer("Tentative Linear Algebra Solve") u_sol.solve(A, x_[ui], b[ui]) t1.stop() udiff[0] += norm(x_2[ui] - x_[ui])
[docs]def pressure_assemble(b, x_, dt, Ap, divu, **NS_namespace): """Assemble rhs of pressure equation.""" divu.assemble_rhs() # Computes div(u_)*q*dx b["p"][:] = divu.rhs b["p"] *= -1.0 / dt b["p"].axpy(1.0, Ap * x_["p"])
[docs]def pressure_solve(dp_, x_, Ap, b, p_sol, bcs, **NS_namespace): """Solve pressure equation.""" [bc.apply(b["p"]) for bc in bcs["p"]] dp_.vector().zero() dp_.vector().axpy(1.0, x_["p"]) # KrylovSolvers use nullspace for normalization of pressure if hasattr(Ap, "null_space"): p_sol.null_space.orthogonalize(b["p"]) t1 = Timer("Pressure Linear Algebra Solve") p_sol.solve(Ap, x_["p"], b["p"]) t1.stop() # LUSolver use normalize directly for normalization of pressure if bcs["p"] == []: normalize(x_["p"]) dpv = dp_.vector() dpv.axpy(-1.0, x_["p"]) dpv *= -1.0
[docs]def velocity_update(u_components, bcs, gradp, dp_, dt, x_, **NS_namespace): """Update the velocity after regular pressure velocity iterations.""" for ui in u_components: gradp[ui](dp_) x_[ui].axpy(-dt, gradp[ui].vector()) [bc.apply(x_[ui]) for bc in bcs[ui]]
[docs]def mesh_velocity_assemble( A_mesh, ui, bw, bw_tmp, a_mesh, bc_mesh, A_cache, **NS_namespace ): """Assemble rhs of mesh velocity equation.""" A_mesh.zero() A_mesh.axpy(1, A_cache[(a_mesh, tuple(bc_mesh[ui]))], True) bw[ui].zero()
[docs]def mesh_velocity_solve( A_mesh, bw, wx_, w_, dof_map, dt, dx_, coordinates, w_sol, ui, bc_mesh, **NS_namespace ): """Solve mesh equation.""" [bc.apply(bw[ui]) for bc in bc_mesh[ui]] w_sol.solve(A_mesh, wx_[ui], bw[ui]) arr = w_[ui].vector().get_local(dof_map) mesh_tolerance = 1e-15 if mesh_tolerance < abs(arr.min()) + abs(arr.max()): coordinates[:, int(ui[-1])] += arr * dt dx_[ui].axpy(dt, w_[ui].vector())
[docs]def scalar_assemble( a_scalar, a_conv, Ta, dt, M, scalar_components, Schmidt_T, KT, nu, Schmidt, b, K, x_1, b0, les_model, nn_model, use_supg, u_ab, u, v, mesh, **NS_namespace ): """Assemble scalar equation.""" # Just in case you want to use a different scalar convection if use_supg: # Define SUPG tau tau = compute_tau(u_ab, mesh) # Define advection term a_scalar = a_conv + tau * dot(u_ab, grad(u)) * dot(u_ab, grad(v)) * dx if a_scalar is not a_conv: assemble(a_scalar, tensor=Ta) Ta *= -0.5 # Negative convection on the rhs Ta.axpy(1.0 / dt, M, True) # Add mass # Compute rhs for all scalars for ci in scalar_components: # Add diffusion if not use_supg: Ta.axpy(-0.5 * nu / Schmidt[ci], K, True) if les_model != "NoModel": Ta.axpy(-0.5 / Schmidt_T[ci], KT[0], True) if nn_model != "NoModel": Ta.axpy(-0.5 / Schmidt_T[ci], KT[0], True) # Compute rhs b[ci].zero() b[ci].axpy(1.0, Ta * x_1[ci]) b[ci].axpy(1.0, b0[ci]) # Subtract diffusion if not use_supg: Ta.axpy(0.5 * nu / Schmidt[ci], K, True) if les_model != "NoModel": Ta.axpy(0.5 / Schmidt_T[ci], KT[0], True) if nn_model != "NoModel": Ta.axpy(0.5 / Schmidt_T[ci], KT[0], True) # Reset matrix for lhs - Note scalar matrix does not contain diffusion Ta *= -1.0 Ta.axpy(2.0 / dt, M, True)
[docs]def scalar_solve( ci, scalar_components, Ta, b, x_, bcs, c_sol, nu, Schmidt, K, use_supg, **NS_namespace ): """Solve scalar equation.""" if not use_supg: Ta.axpy(0.5 * nu / Schmidt[ci], K, True) # Add diffusion if len(scalar_components) > 1: # Reuse solver for all scalars. This requires the same matrix and vectors to be used by c_sol. Tb, bb, bx = NS_namespace["Tb"], NS_namespace["bb"], NS_namespace["bx"] Tb.zero() Tb.axpy(1.0, Ta, True) bb.zero() bb.axpy(1.0, b[ci]) bx.zero() bx.axpy(1.0, x_[ci]) [bc.apply(Tb, bb) for bc in bcs[ci]] c_sol.solve(Tb, bx, bb) x_[ci].zero() x_[ci].axpy(1.0, bx) else: [bc.apply(Ta, b[ci]) for bc in bcs[ci]] c_sol.solve(Ta, x_[ci], b[ci]) if not use_supg: Ta.axpy(-0.5 * nu / Schmidt[ci], K, True) # Subtract diffusion
def compute_tau(u_ab, mesh): """ Compute the stabilization parameter tau for a given mesh and velocity vector. Args: u_ab (ndarray): A 2D or 3D vector representing the convecting velocity. mesh (Mesh): The mesh used to compute the cell diameter. Returns: tau (float): The stabilization parameter tau. """ h = CellDiameter(mesh) u_mag = sqrt(dot(u_ab, u_ab) + 1e-10) tau = h / (2.0 * u_mag) return tau