def get_aspect_ratios2d(mesh, python=False): """ Computes the aspect ratio of each cell in a 2D triangular mesh :arg mesh: the input mesh to do computations on :kwarg python: compute the measure using Python? :rtype: firedrake.function.Function aspect_ratios with aspect ratio data """ P0 = firedrake.FunctionSpace(mesh, "DG", 0) if python: P0_ten = firedrake.TensorFunctionSpace(mesh, "DG", 0) J = firedrake.interpolate(ufl.Jacobian(mesh), P0_ten) edge1 = ufl.as_vector([J[0, 0], J[1, 0]]) edge2 = ufl.as_vector([J[0, 1], J[1, 1]]) edge3 = edge1 - edge2 a = ufl.sqrt(ufl.dot(edge1, edge1)) b = ufl.sqrt(ufl.dot(edge2, edge2)) c = ufl.sqrt(ufl.dot(edge3, edge3)) aspect_ratios = firedrake.interpolate( a * b * c / ((a + b - c) * (b + c - a) * (c + a - b)), P0 ) else: coords = mesh.coordinates aspect_ratios = firedrake.Function(P0) op2.par_loop( get_pyop2_kernel("get_aspect_ratio", 2), mesh.cell_set, aspect_ratios.dat(op2.WRITE, aspect_ratios.cell_node_map()), coords.dat(op2.READ, coords.cell_node_map()), ) return aspect_ratios
def S(self, u_, p_, ivar=None, tang=False): C_ = variable(self.kin.C(u_)) stress = constantvalue.zero((3, 3)) # volumetric (kinematic) growth if self.mat_growth: theta_ = ivar["theta"] # material has to be evaluated with C_e only, however total S has # to be computed by differentiating w.r.t. C (S = 2*dPsi/dC) self.mat = materiallaw(self.C_e(C_, theta_), self.I) else: self.mat = materiallaw(C_, self.I) m = 0 for matlaw in self.matmodels: stress += self.add_stress_mat(matlaw, self.matparams[m], ivar, C_) m += 1 # add remodeled material if self.mat_growth and self.mat_remodel: self.stress_base = stress self.stress_remod = constantvalue.zero((3, 3)) m = 0 for matlaw in self.matmodels_remod: self.stress_remod += self.add_stress_mat( matlaw, self.matparams_remod[m], ivar, C_) m += 1 # update the stress expression: S = (1-phi(theta)) * S_base + phi(theta) * S_remod stress = ( 1. - self.phi_remod(theta_) ) * self.stress_base + self.phi_remod(theta_) * self.stress_remod # if we have p (hydr. pressure) as variable in a 2-field functional if self.incompr_2field: if self.mat_growth: # TeX: S_{\mathrm{vol}} = -2 \frac{\partial[p(J^{\mathrm{e}}-1)]}{\partial \boldsymbol{C}} stress += -2. * diff( p_ * (sqrt(det(self.C_e(C_, theta_))) - 1.), C_) else: # TeX: S_{\mathrm{vol}} = -2 \frac{\partial[p(J-1)]}{\partial \boldsymbol{C}} = -Jp\boldsymbol{C}^{-1} stress += -2. * diff(p_ * (sqrt(det(C_)) - 1.), C_) if tang: return 2. * diff(stress, C_) else: return stress
def tv_reg(lmda, mu, dx, R_lmda=1, R_mu=1, eps=1e-10): grad_lamda = grad(lmda) grad_mu = grad(mu) integrand_lmda = R_lmda * sqrt(inner(grad_lamda, grad_lamda) + eps) * dx integrand_mu = R_mu * sqrt(inner(grad_mu, grad_mu) + eps) * dx return assemble(integrand_lmda + integrand_mu)
def rP(self, u, alpha, v, beta): w_1 = self.w(1) a = self.a sigma = self.sigma eps = u.dx(0) return (sqrt(a(alpha))*sigma(v) + diff(a(alpha), alpha)/sqrt(a(alpha))*sigma(u)*beta)* \ (sqrt(a(alpha))*v.dx(0) + diff(a(alpha), alpha)/sqrt(a(alpha))*eps*beta) + \ 2*w_1*self.ell ** 2 * beta.dx(0)*beta.dx(0)
def rP(self, u, alpha, v, beta): w_1 = self.w(1) a = self.a sigma = self.sigma eps = self.eps return inner(sqrt(a(alpha))*sigma(v) + diff(a(alpha), alpha)/sqrt(a(alpha))*sigma(u)*beta, sqrt(a(alpha))*eps(v) + diff(a(alpha), alpha)/sqrt(a(alpha))*eps(u)*beta) + \ 2*w_1*self.ell ** 2 * dot(grad(beta), grad(beta))
def eigenstate_legacy(A): """Eigenvalues and eigenprojectors of the 3x3 (real-valued) tensor A. Provides the spectral decomposition A = sum_{a=0}^{2} λ_a * E_a with eigenvalues λ_a and their associated eigenprojectors E_a = n_a^R x n_a^L ordered by magnitude. The eigenprojectors of eigenvalues with multiplicity n are returned as 1/n-fold projector. Note: Tensor A must not have complex eigenvalues! """ if ufl.shape(A) != (3, 3): raise RuntimeError( f"Tensor A of shape {ufl.shape(A)} != (3, 3) is not supported!") # eps = 1.0e-10 # A = ufl.variable(A) # # --- determine eigenvalues λ0, λ1, λ2 # # additively decompose: A = tr(A) / 3 * I + dev(A) = q * I + B q = ufl.tr(A) / 3 B = A - q * ufl.Identity(3) # observe: det(λI - A) = 0 with shift λ = q + ω --> det(ωI - B) = 0 = ω**3 - j * ω - b j = ufl.tr( B * B ) / 2 # == -I2(B) for trace-free B, j < 0 indicates A has complex eigenvalues b = ufl.tr(B * B * B) / 3 # == I3(B) for trace-free B # solve: 0 = ω**3 - j * ω - b by substitution ω = p * cos(phi) # 0 = p**3 * cos**3(phi) - j * p * cos(phi) - b | * 4 / p**3 # 0 = 4 * cos**3(phi) - 3 * cos(phi) - 4 * b / p**3 | --> p := sqrt(j * 4 / 3) # 0 = cos(3 * phi) - 4 * b / p**3 # 0 = cos(3 * phi) - r with -1 <= r <= +1 # phi_k = [acos(r) + (k + 1) * 2 * pi] / 3 for k = 0, 1, 2 p = 2 / ufl.sqrt(3) * ufl.sqrt(j + eps**2) # eps: MMM r = 4 * b / p**3 r = ufl.Max(ufl.Min(r, +1 - eps), -1 + eps) # eps: LMM, MMH phi = ufl.acos(r) / 3 # sorted eigenvalues: λ0 <= λ1 <= λ2 λ0 = q + p * ufl.cos(phi + 2 / 3 * ufl.pi) # low λ1 = q + p * ufl.cos(phi + 4 / 3 * ufl.pi) # middle λ2 = q + p * ufl.cos(phi) # high # # --- determine eigenprojectors E0, E1, E2 # E0 = ufl.diff(λ0, A).T E1 = ufl.diff(λ1, A).T E2 = ufl.diff(λ2, A).T # return [λ0, λ1, λ2], [E0, E1, E2]
def freeEnergy(C, Cv): J = sqrt(det(C)) I1 = tr(C) Ce = C * inv(Cv) Ie1 = tr(Ce) Je = J / sqrt(det(Cv)) psiEq = (3**(1 - alph1) / (2.0 * alph1) * mu1 * (I1**alph1 - 3**alph1) + 3**(1 - alph2) / (2.0 * alph2) * mu2 * (I1**alph2 - 3**alph2) - (mu1 + mu2) * ln(J) + mu_pr / 2 * (J - 1)**2) psiNeq = (3**(1 - a1) / (2.0 * a1) * m1 * (Ie1**a1 - 3**a1) + 3**(1 - a2) / (2.0 * a2) * m2 * (Ie1**a2 - 3**a2) - (m1 + m2) * ln(Je)) return psiEq + psiNeq
def test_nonlinear_pde(): """Test Newton solver for a simple nonlinear PDE""" # Create mesh and function space mesh = dolfin.generation.UnitSquareMesh(dolfin.MPI.comm_world, 12, 15) V = dolfin.function.FunctionSpace(mesh, ("Lagrange", 1)) u = dolfin.function.Function(V) v = function.TestFunction(V) F = inner(2.0, v) * dx - ufl.sqrt(u * u) * inner( grad(u), grad(v)) * dx - inner(u, v) * dx def boundary(x): """Define Dirichlet boundary (x = 0 or x = 1).""" return np.logical_or(x[:, 0] < 1.0e-8, x[:, 0] > 1.0 - 1.0e-8) u_bc = function.Function(V) u_bc.vector().set(1.0) u_bc.vector().update_ghosts() bc = fem.DirichletBC(V, u_bc, boundary) # Create nonlinear problem problem = NonlinearPDEProblem(F, u, bc) # Create Newton solver and solve u.vector().set(0.9) u.vector().update_ghosts() solver = dolfin.cpp.nls.NewtonSolver(dolfin.MPI.comm_world) n, converged = solver.solve(problem, u.vector()) assert converged assert n < 6
def bessi0(x): """ Modified Bessel function of the first kind. Code taken from [Flannery et al. 1992] B.P. Flannery, W.H. Press, S.A. Teukolsky, W. Vetterling, "Numerical recipes in C", Press Syndicate of the University of Cambridge, New York (1992). """ ax = abs(x) y1 = x / 3.75 y1 *= y1 expr1 = 1.0 + y1 * (3.5156229 + y1 * (3.0899424 + y1 * (1.2067492 + y1 * (0.2659732 + y1 * (0.360768e-1 + y1 * 0.45813e-2))))) y2 = 3.75 / ax expr2 = (ufl.exp(ax) / ufl.sqrt(ax) * (0.39894228 + y2 * (0.1328592e-1 + y2 * (0.225319e-2 + y2 * (-0.157565e-2 + y2 * (0.916281e-2 + y2 * (-0.2057706e-1 + y2 * (0.2635537e-1 + y2 * (-0.1647633e-1 + y2 * 0.392377e-2))))))))) return ufl.conditional(ax < 3.75, expr1, expr2)
def readin_fibers(self, fibarray, V_fib, dx_): # V_fib_input is function space the fiber vector is defined on (only CG1 or DG0 supported, add further depending on your input...) if list(self.fiber_data.keys())[0] == 'nodal': V_fib_input = VectorFunctionSpace(self.mesh, ("CG", 1)) elif list(self.fiber_data.keys())[0] == 'elemental': V_fib_input = VectorFunctionSpace(self.mesh, ("DG", 0)) else: raise AttributeError("Specify 'nodal' or 'elemental' for the fiber data input!") fib_func = [] fib_func_input = [] si = 0 for s in fibarray: fib_func_input.append(Function(V_fib_input, name='Fiber'+str(si+1)+'_input')) self.readfunction(fib_func_input[si], V_fib_input, list(self.fiber_data.values())[0][si], normalize=True) # project to output fiber function space ff = project(fib_func_input[si], V_fib, dx_, bcs=[], nm='fib_'+s+'') # assure that projected field still has unit length (not always necessarily the case) fib_func.append(ff / sqrt(dot(ff,ff))) ## write input fiber field for checking... #outfile = XDMFFile(self.comm, self.output_path+'/fiber'+str(si+1)+'_input.xdmf', 'w') #outfile.write_mesh(self.mesh) #outfile.write_function(fib_func_input[si]) si+=1 return fib_func
def bessk0(x): """ Modified Bessel function of the second kind. Code taken from [Flannery et al. 1992] B.P. Flannery, W.H. Press, S.A. Teukolsky, W. Vetterling, "Numerical recipes in C", Press Syndicate of the University of Cambridge, New York (1992). """ y1 = x * x / 4.0 expr1 = -ufl.ln(x / 2.0) * bessi0(x) + ( -0.57721566 + y1 * (0.42278420 + y1 * (0.23069756 + y1 * (0.3488590e-1 + y1 * (0.262698e-2 + y1 * (0.10750e-3 + y1 * 0.74e-5)))))) y2 = 2.0 / x expr2 = (ufl.exp(-x) / ufl.sqrt(x) * (1.25331414 + y2 * (-0.7832358e-1 + y2 * (0.2189568e-1 + y2 * (-0.1062446e-1 + y2 * (0.587872e-2 + y2 * (-0.251540e-2 + y2 * 0.53208e-3))))))) return ufl.conditional(x > 2, expr2, expr1)
def test_nonlinear_pde(): """Test Newton solver for a simple nonlinear PDE""" # Create mesh and function space mesh = create_unit_square(MPI.COMM_WORLD, 12, 5) V = FunctionSpace(mesh, ("Lagrange", 1)) u = Function(V) v = TestFunction(V) F = inner(5.0, v) * dx - ufl.sqrt(u * u) * inner( grad(u), grad(v)) * dx - inner(u, v) * dx bc = dirichletbc(PETSc.ScalarType(1.0), locate_dofs_geometrical(V, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))), V) # Create nonlinear problem problem = NonlinearPDEProblem(F, u, bc) # Create Newton solver and solve u.x.array[:] = 0.9 solver = _cpp.nls.NewtonSolver(MPI.COMM_WORLD) solver.setF(problem.F, problem.vector()) solver.setJ(problem.J, problem.matrix()) solver.set_form(problem.form) n, converged = solver.solve(u.vector) assert converged assert n < 6 # Modify boundary condition and solve again bc.g.value[...] = 0.5 n, converged = solver.solve(u.vector) assert converged assert n > 0 and n < 6
def test_pack_coefficients(): """Test packing of form coefficients ahead of main assembly call""" mesh = create_unit_square(MPI.COMM_WORLD, 12, 15) V = FunctionSpace(mesh, ("Lagrange", 1)) # Non-blocked u = Function(V) v = ufl.TestFunction(V) c = Constant(mesh, PETSc.ScalarType(12.0)) F = ufl.inner(c, v) * dx - c * ufl.sqrt(u * u) * ufl.inner(u, v) * dx u.x.array[:] = 10.0 _F = form(F) # -- Test vector b0 = assemble_vector(_F) b0.assemble() constants = pack_constants(_F) coeffs = pack_coefficients(_F) with b0.localForm() as _b0: for c in [(None, None), (None, coeffs), (constants, None), (constants, coeffs)]: b = assemble_vector(_F, coeffs=c) b.assemble() with b.localForm() as _b: assert (_b0.array_r == _b.array_r).all() # Change coefficients constants *= 5.0 for coeff in coeffs.values(): coeff *= 5.0 with b0.localForm() as _b0: for c in [(None, coeffs), (constants, None), (constants, coeffs)]: b = assemble_vector(_F, coeffs=c) b.assemble() with b.localForm() as _b: assert (_b0 - _b).norm() > 1.0e-5 # -- Test matrix du = ufl.TrialFunction(V) J = ufl.derivative(F, u, du) J = form(J) A0 = assemble_matrix(J) A0.assemble() constants = pack_constants(J) coeffs = pack_coefficients(J) for c in [(None, None), (None, coeffs), (constants, None), (constants, coeffs)]: A = assemble_matrix(J, coeffs=c) A.assemble() assert pytest.approx((A - A0).norm(), 1.0e-12) == 0.0 # Change coefficients constants *= 5.0 for coeff in coeffs.values(): coeff *= 5.0 for c in [(None, coeffs), (constants, None), (constants, coeffs)]: A = assemble_matrix(J, coeffs=c) A.assemble() assert (A - A0).norm() > 1.0e-5
def sussmanbathe_vol(self, params, C): kappa = params['kappa'] psi_vol = (kappa / 2.) * (sqrt(self.IIIc) - 1.)**2. S = 2. * diff(psi_vol, C) return S
def velocity_expression(theta_k, theta_kp1, dt): theta_avg = avg(theta_k, theta_kp1, implicitness=.5) velocity_form_ = ( (theta_kp1 - theta_k) / dt / ufl.sqrt(inner(grad(theta_avg), grad(theta_avg)) + DOLFIN_EPS) ) return velocity_form_
def create_initial_conditions(x: ufl.geometry.SpatialCoordinate, undercooling: float): center = (0.0, 0.0) radius = 8.0 epsilon = 1.0 values = [None, None] # phase function distance_function = (ufl.sqrt((x[0] - center[0])**2 + (x[1] - center[1])**2) - radius) values[0] = -ufl.operators.tanh( distance_function / (ufl.sqrt(2.0) * epsilon)) # in range -1 : 1 # temperature values[1] = undercooling return ufl.as_vector(values)
def ogden_vol(self, params, C): kappa = params['kappa'] psi_vol = (kappa / 4.) * (self.IIIc - 2. * ln(sqrt(self.IIIc)) - 1.) S = 2. * diff(psi_vol, C) return S
def test_complex_algebra(self): z1 = ComplexValue(1j) z2 = ComplexValue(1+1j) # Remember that ufl.algebra functions return ComplexValues, but ufl.mathfunctions return complex Python scalar # Any operations with a ComplexValue and a complex Python scalar promote to ComplexValue assert z1*z2 == ComplexValue(-1+1j) assert z2/z1 == ComplexValue(1-1j) assert pow(z2, z1) == ComplexValue((1+1j)**1j) assert sqrt(z2) * as_ufl(1) == ComplexValue(cmath.sqrt(1+1j)) assert ((sin(z2) + cosh(z2) - atan(z2)) * z1) == ComplexValue((cmath.sin(1+1j) + cmath.cosh(1+1j) - cmath.atan(1+1j))*1j) assert (abs(z2) - ln(z2))/exp(z1) == ComplexValue((abs(1+1j) - cmath.log(1+1j))/cmath.exp(1j))
def errornorm(u, uh, norm_type="L2", **kwargs): r""" Overload Firedrake's ``errornorm`` function to allow for :math:`\ell^p` norms. Note that this version is case sensitive, i.e. ``'l2'`` and ``'L2'`` will give different results in general. :arg u: the 'true' value :arg uh: the approximation of the 'truth' :kwarg norm_type: choose from 'l1', 'l2', 'linf', 'L2', 'Linf', 'H1', 'Hdiv', 'Hcurl', or any 'Lp' with :math:`p >= 1`. :kwarg boundary: should the norm be computed over the domain boundary? """ if len(u.ufl_shape) != len(uh.ufl_shape): raise RuntimeError("Mismatching rank between u and uh") if not isinstance(uh, firedrake.Function): raise ValueError("uh should be a Function, is a %r", type(uh)) if norm_type[0] == "l": if not isinstance(u, firedrake.Function): raise ValueError("u should be a Function, is a %r", type(uh)) if isinstance(u, firedrake.Function): degree_u = u.function_space().ufl_element().degree() degree_uh = uh.function_space().ufl_element().degree() if degree_uh > degree_u: firedrake.logging.warning( "Degree of exact solution less than approximation degree" ) # Case 1: point-wise norms if norm_type[0] == "l": v = u v -= uh # Case 2: UFL norms for mixed function spaces elif hasattr(uh.function_space(), "num_sub_spaces"): if norm_type[1:] == "2": vv = [uu - uuh for uu, uuh in zip(u.split(), uh.split())] dX = ufl.ds if kwargs.get("boundary", False) else ufl.dx return ufl.sqrt(firedrake.assemble(sum([ufl.inner(v, v) for v in vv]) * dX)) else: raise NotImplementedError # Case 3: UFL norms for non-mixed spaces else: v = u - uh return norm(v, norm_type=norm_type, **kwargs)
def a(phi): # phi = 1e5*phi n = ufl.grad(phi) / ufl.sqrt(ufl.dot(ufl.grad(phi), ufl.grad(phi)) + 1e-5) theta = ufl.atan_2(n[1], n[0]) n = 1e5 * n + ufl.as_vector([1e-5, 1e-5]) xx = n[1] / n[0] # theta = ufl.asin(xx/ ufl.sqrt(1 + xx**2)) theta = ufl.atan(xx) return 1.0 + epsilon_4 * ufl.cos(m * (theta - theta_0))
def test_nonlinear_pde_snes(): """Test Newton solver for a simple nonlinear PDE""" # Create mesh and function space mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 12, 15) V = function.FunctionSpace(mesh, ("Lagrange", 1)) u = function.Function(V) v = TestFunction(V) F = inner(5.0, v) * dx - ufl.sqrt(u * u) * inner( grad(u), grad(v)) * dx - inner(u, v) * dx def boundary(x): """Define Dirichlet boundary (x = 0 or x = 1).""" return np.logical_or(x[0] < 1.0e-8, x[0] > 1.0 - 1.0e-8) u_bc = function.Function(V) u_bc.vector.set(1.0) u_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) bc = fem.DirichletBC(u_bc, fem.locate_dofs_geometrical(V, boundary)) # Create nonlinear problem problem = NonlinearPDE_SNESProblem(F, u, bc) u.vector.set(0.9) u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) b = dolfinx.cpp.la.create_vector(V.dofmap.index_map) J = dolfinx.cpp.fem.create_matrix(problem.a_comp._cpp_object) # Create Newton solver and solve snes = PETSc.SNES().create() snes.setFunction(problem.F, b) snes.setJacobian(problem.J, J) snes.setTolerances(rtol=1.0e-9, max_it=10) snes.getKSP().setType("preonly") snes.getKSP().setTolerances(rtol=1.0e-9) snes.getKSP().getPC().setType("lu") snes.getKSP().getPC().setFactorSolverType("superlu_dist") snes.solve(None, u.vector) assert snes.getConvergedReason() > 0 assert snes.getIterationNumber() < 6 # Modify boundary condition and solve again u_bc.vector.set(0.5) u_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) snes.solve(None, u.vector) assert snes.getConvergedReason() > 0 assert snes.getIterationNumber() < 6
def test_latex_formatting_of_cmath(): x = ufl.SpatialCoordinate(ufl.triangle)[0] assert expr2latex(ufl.exp(x)) == r"e^{x_0}" assert expr2latex(ufl.ln(x)) == r"\ln(x_0)" assert expr2latex(ufl.sqrt(x)) == r"\sqrt{x_0}" assert expr2latex(abs(x)) == r"\|x_0\|" assert expr2latex(ufl.sin(x)) == r"\sin(x_0)" assert expr2latex(ufl.cos(x)) == r"\cos(x_0)" assert expr2latex(ufl.tan(x)) == r"\tan(x_0)" assert expr2latex(ufl.asin(x)) == r"\arcsin(x_0)" assert expr2latex(ufl.acos(x)) == r"\arccos(x_0)" assert expr2latex(ufl.atan(x)) == r"\arctan(x_0)"
def norm(v, norm_type="L2", mesh=None): """Compute the norm of ``v``. :arg v: a :class:`.Function` to compute the norm of :arg norm_type: the type of norm to compute, see below for options. :arg mesh: an optional mesh on which to compute the norm (currently ignored). Available norm types are: * L2 .. math:: ||v||_{L^2}^2 = \int (v, v) \mathrm{d}x * H1 .. math:: ||v||_{H^1}^2 = \int (v, v) + (\\nabla v, \\nabla v) \mathrm{d}x * Hdiv .. math:: ||v||_{H_\mathrm{div}}^2 = \int (v, v) + (\\nabla\cdot v, \\nabla \cdot v) \mathrm{d}x * Hcurl .. math:: ||v||_{H_\mathrm{curl}}^2 = \int (v, v) + (\\nabla \wedge v, \\nabla \wedge v) \mathrm{d}x """ assert isinstance(v, function.Function) typ = norm_type.lower() mesh = v.function_space().mesh() dx = mesh._dx if typ == 'l2': form = inner(v, v)*dx elif typ == 'h1': form = inner(v, v)*dx + inner(grad(v), grad(v))*dx elif typ == "hdiv": form = inner(v, v)*dx + div(v)*div(v)*dx elif typ == "hcurl": form = inner(v, v)*dx + inner(curl(v), curl(v))*dx else: raise RuntimeError("Unknown norm type '%s'" % norm_type) return sqrt(solving.assemble(form))
def norm(v, norm_type="L2", mesh=None): """Compute the norm of ``v``. :arg v: a :class:`.Function` to compute the norm of :arg norm_type: the type of norm to compute, see below for options. :arg mesh: an optional mesh on which to compute the norm (currently ignored). Available norm types are: * L2 .. math:: ||v||_{L^2}^2 = \int (v, v) \mathrm{d}x * H1 .. math:: ||v||_{H^1}^2 = \int (v, v) + (\\nabla v, \\nabla v) \mathrm{d}x * Hdiv .. math:: ||v||_{H_\mathrm{div}}^2 = \int (v, v) + (\\nabla\cdot v, \\nabla \cdot v) \mathrm{d}x * Hcurl .. math:: ||v||_{H_\mathrm{curl}}^2 = \int (v, v) + (\\nabla \wedge v, \\nabla \wedge v) \mathrm{d}x """ assert isinstance(v, function.Function) typ = norm_type.lower() mesh = v.function_space().mesh() dx = mesh._dx if typ == 'l2': form = inner(v, v)*dx elif typ == 'h1': form = inner(v, v)*dx + inner(grad(v), grad(v))*dx elif typ == "hdiv": form = inner(v, v)*dx + div(v)*div(v)*dx elif typ == "hcurl": form = inner(v, v)*dx + inner(curl(v), curl(v))*dx else: raise RuntimeError("Unknown norm type '%s'" % norm_type) return sqrt(assemble(form))
def norm(v, norm_type="L2", degree=None): """Compute the norm of ``v``. :arg v: a ufl expression (:class:`~.ufl.classes.Expr`) to compute the norm of :arg norm_type: the type of norm to compute, see below for options. Available norm types are: * L2 .. math:: ||v||_{L^2}^2 = \int (v, v) \mathrm{d}x * H1 .. math:: ||v||_{H^1}^2 = \int (v, v) + (\\nabla v, \\nabla v) \mathrm{d}x * Hdiv .. math:: ||v||_{H_\mathrm{div}}^2 = \int (v, v) + (\\nabla\cdot v, \\nabla \cdot v) \mathrm{d}x * Hcurl .. math:: ||v||_{H_\mathrm{curl}}^2 = \int (v, v) + (\\nabla \wedge v, \\nabla \wedge v) \mathrm{d}x """ if not degree == None: dxn = dx(2 * degree + 1) else: dxn = dx typ = norm_type.lower() if typ == 'l2': form = inner(v, v) * dxn elif typ == 'h1': form = inner(v, v) * dxn + inner(grad(v), grad(v)) * dxn elif typ == "hdiv": form = inner(v, v) * dxn + div(v) * div(v) * dxn elif typ == "hcurl": form = inner(v, v) * dxn + inner(curl(v), curl(v)) * dxn else: raise RuntimeError("Unknown norm type '%s'" % norm_type) normform = ZeroForm(form) return sqrt(normform.assembleform())
def physical_edge_lengths(self): expr = ufl.classes.CellEdgeVectors(self.mt.terminal.ufl_domain()) if self.mt.restriction == '+': expr = PositiveRestricted(expr) elif self.mt.restriction == '-': expr = NegativeRestricted(expr) expr = ufl.as_vector([ufl.sqrt(ufl.dot(expr[i, :], expr[i, :])) for i in range(3)]) expr = preprocess_expression(expr) config = {"point_set": PointSingleton([1/3, 1/3])} config.update(self.config) context = PointSetContext(**config) return map_expr_dag(context.translator, expr)
def test_cpp_formatting_of_cmath(): x, y = ufl.SpatialCoordinate(ufl.triangle) # Test cmath functions assert expr2cpp(ufl.exp(x)) == "exp(x[0])" assert expr2cpp(ufl.ln(x)) == "log(x[0])" assert expr2cpp(ufl.sqrt(x)) == "sqrt(x[0])" assert expr2cpp(abs(x)) == "fabs(x[0])" assert expr2cpp(ufl.sin(x)) == "sin(x[0])" assert expr2cpp(ufl.cos(x)) == "cos(x[0])" assert expr2cpp(ufl.tan(x)) == "tan(x[0])" assert expr2cpp(ufl.asin(x)) == "asin(x[0])" assert expr2cpp(ufl.acos(x)) == "acos(x[0])" assert expr2cpp(ufl.atan(x)) == "atan(x[0])"
def test_estimated_degree(): cell = ufl.tetrahedron mesh = ufl.Mesh(ufl.VectorElement('P', cell, 1)) V = ufl.FunctionSpace(mesh, ufl.FiniteElement('P', cell, 1)) f = ufl.Coefficient(V) u = ufl.TrialFunction(V) v = ufl.TestFunction(V) a = u * v * ufl.tanh(ufl.sqrt(ufl.sinh(f) / ufl.sin(f**f))) * ufl.dx handler = MockHandler() logger.addHandler(handler) with pytest.raises(RuntimeError): compile_form(a) logger.removeHandler(handler)
def get_scaled_jacobians2d(mesh, python=False): """ Computes the scaled Jacobian of each cell in a 2D triangular mesh :arg mesh: the input mesh to do computations on :kwarg python: compute the measure using Python? :rtype: firedrake.function.Function scaled_jacobians with scaled jacobian data. """ P0 = firedrake.FunctionSpace(mesh, "DG", 0) if python: P0_ten = firedrake.TensorFunctionSpace(mesh, "DG", 0) J = firedrake.interpolate(ufl.Jacobian(mesh), P0_ten) edge1 = ufl.as_vector([J[0, 0], J[1, 0]]) edge2 = ufl.as_vector([J[0, 1], J[1, 1]]) edge3 = edge1 - edge2 a = ufl.sqrt(ufl.dot(edge1, edge1)) b = ufl.sqrt(ufl.dot(edge2, edge2)) c = ufl.sqrt(ufl.dot(edge3, edge3)) detJ = ufl.JacobianDeterminant(mesh) jacobian_sign = ufl.sign(detJ) max_product = ufl.Max( ufl.Max(ufl.Max(a * b, a * c), ufl.Max(b * c, b * a)), ufl.Max(c * a, c * b) ) scaled_jacobians = firedrake.interpolate(detJ / max_product * jacobian_sign, P0) else: coords = mesh.coordinates scaled_jacobians = firedrake.Function(P0) op2.par_loop( get_pyop2_kernel("get_scaled_jacobian", 2), mesh.cell_set, scaled_jacobians.dat(op2.WRITE, scaled_jacobians.cell_node_map()), coords.dat(op2.READ, coords.cell_node_map()), ) return scaled_jacobians
def norm(v, norm_type="L2", mesh=None): """Compute the norm of ``v``. :arg v: a ufl expression (:class:`~.ufl.classes.Expr`) to compute the norm of :arg norm_type: the type of norm to compute, see below for options. :arg mesh: an optional mesh on which to compute the norm (currently ignored). Available norm types are: * L2 .. math:: ||v||_{L^2}^2 = \int (v, v) \mathrm{d}x * H1 .. math:: ||v||_{H^1}^2 = \int (v, v) + (\\nabla v, \\nabla v) \mathrm{d}x * Hdiv .. math:: ||v||_{H_\mathrm{div}}^2 = \int (v, v) + (\\nabla\cdot v, \\nabla \cdot v) \mathrm{d}x * Hcurl .. math:: ||v||_{H_\mathrm{curl}}^2 = \int (v, v) + (\\nabla \wedge v, \\nabla \wedge v) \mathrm{d}x """ typ = norm_type.lower() if typ == 'l2': form = inner(v, v)*dx elif typ == 'h1': form = inner(v, v)*dx + inner(grad(v), grad(v))*dx elif typ == "hdiv": form = inner(v, v)*dx + div(v)*div(v)*dx elif typ == "hcurl": form = inner(v, v)*dx + inner(curl(v), curl(v))*dx else: raise RuntimeError("Unknown norm type '%s'" % norm_type) return sqrt(assemble(form))
def compute_error(u1, u2): # Reference mesh mesh_resolution_ref = 500 mesh_ref = UnitIntervalMesh(mesh_resolution_ref) # Reference function space V_ref = FunctionSpace(mesh_ref, "CG", 1) # Evaluate the input functions on the reference mesh Iu1 = interpolate(u1, V_ref) Iu2 = interpolate(u2, V_ref) # Compute the error e = Iu1 - Iu2 error = sqrt(assemble(e * e * dx)) return error
def strain_energy(i1, i2, i3): """Strain energy function i1, i2, i3: principal invariants of the Cauchy-Green tensor """ # Determinant of configuration gradient F J = ufl.sqrt(i3) # noqa: F841 # # Classical St. Venant-Kirchhoff # Ψ = la / 8 * (i1 - 3)**2 + mu / 4 * ((i1 - 3)**2 + 4 * (i1 - 3) - 2 * (i2 - 3)) # Modified St. Venant-Kirchhoff # Ψ = la / 2 * (ufl.ln(J))**2 + mu / 4 * ((i1 - 3)**2 + 4 * (i1 - 3) - 2 * (i2 - 3)) # Compressible neo-Hooke Ψ = mu / 2 * (i1 - 3 - 2 * ufl.ln(J)) + la / 2 * (J - 1)**2 # Compressible Mooney-Rivlin (beta = 0) # Ψ = mu / 4 * (i1 - 3) + mu / 4 * (i2 - 3) - mu * ufl.ln(J) + la / 2 * (J - 1)**2 # return Ψ
def __init__(self, mesh, Vh, prior, misfit, simulation_times, wind_velocity, gls_stab): self.mesh = mesh self.Vh = Vh self.prior = prior self.misfit = misfit # Assume constant timestepping self.simulation_times = simulation_times dt = simulation_times[1] - simulation_times[0] u = dl.TrialFunction(Vh[STATE]) v = dl.TestFunction(Vh[STATE]) kappa = dl.Constant(.001) dt_expr = dl.Constant(dt) r_trial = u + dt_expr * (-ufl.div(kappa * ufl.grad(u)) + ufl.inner(wind_velocity, ufl.grad(u))) r_test = v + dt_expr * (-ufl.div(kappa * ufl.grad(v)) + ufl.inner(wind_velocity, ufl.grad(v))) h = dl.CellDiameter(mesh) vnorm = ufl.sqrt(ufl.inner(wind_velocity, wind_velocity)) if gls_stab: tau = ufl.min_value((h * h) / (dl.Constant(2.) * kappa), h / vnorm) else: tau = dl.Constant(0.) self.M = dl.assemble(ufl.inner(u, v) * dl.dx) self.M_stab = dl.assemble(ufl.inner(u, v + tau * r_test) * dl.dx) self.Mt_stab = dl.assemble(ufl.inner(u + tau * r_trial, v) * dl.dx) Nvarf = (ufl.inner(kappa * ufl.grad(u), ufl.grad(v)) + ufl.inner(wind_velocity, ufl.grad(u)) * v) * dl.dx Ntvarf = (ufl.inner(kappa * ufl.grad(v), ufl.grad(u)) + ufl.inner(wind_velocity, ufl.grad(v)) * u) * dl.dx self.N = dl.assemble(Nvarf) self.Nt = dl.assemble(Ntvarf) stab = dl.assemble(tau * ufl.inner(r_trial, r_test) * dl.dx) self.L = self.M + dt * self.N + stab self.Lt = self.M + dt * self.Nt + stab self.solver = dl.PETScLUSolver(dl.as_backend_type(self.L)) self.solvert = dl.PETScLUSolver(dl.as_backend_type(self.Lt)) # Part of model public API self.gauss_newton_approx = False
def rhs(states, time, parameters, dy=None): """ Compute right hand side """ # Imports import ufl import dolfin # Assign states assert(isinstance(states, dolfin.Function)) assert(states.function_space().depth() == 1) assert(states.function_space().num_sub_spaces() == 17) Xr1, Xr2, Xs, m, h, j, d, f, fCa, s, r, Ca_SR, Ca_i, g, Na_i, V, K_i =\ dolfin.split(states) # Assign parameters assert(isinstance(parameters, (dolfin.Function, dolfin.Constant))) if isinstance(parameters, dolfin.Function): assert(parameters.function_space().depth() == 1) assert(parameters.function_space().num_sub_spaces() == 45) else: assert(parameters.value_size() == 45) P_kna, g_K1, g_Kr, g_Ks, g_Na, g_bna, g_CaL, g_bca, g_to, K_mNa, K_mk,\ P_NaK, K_NaCa, K_sat, Km_Ca, Km_Nai, alpha, gamma, K_pCa, g_pCa,\ g_pK, Buf_c, Buf_sr, Ca_o, K_buf_c, K_buf_sr, K_up, V_leak, V_sr,\ Vmax_up, a_rel, b_rel, c_rel, tau_g, Na_o, Cm, F, R, T, V_c,\ stim_amplitude, stim_duration, stim_period, stim_start, K_o =\ dolfin.split(parameters) # Reversal potentials E_Na = R*T*ufl.ln(Na_o/Na_i)/F E_K = R*T*ufl.ln(K_o/K_i)/F E_Ks = R*T*ufl.ln((Na_o*P_kna + K_o)/(Na_i*P_kna + K_i))/F E_Ca = 0.5*R*T*ufl.ln(Ca_o/Ca_i)/F # Inward rectifier potassium current alpha_K1 = 0.1/(1.0 + 6.14421235332821e-6*ufl.exp(0.06*V - 0.06*E_K)) beta_K1 = (3.06060402008027*ufl.exp(0.0002*V - 0.0002*E_K) +\ 0.367879441171442*ufl.exp(0.1*V - 0.1*E_K))/(1.0 + ufl.exp(0.5*E_K -\ 0.5*V)) xK1_inf = alpha_K1/(alpha_K1 + beta_K1) i_K1 = 0.430331482911935*ufl.sqrt(K_o)*(-E_K + V)*g_K1*xK1_inf # Rapid time dependent potassium current i_Kr = 0.430331482911935*ufl.sqrt(K_o)*(-E_K + V)*Xr1*Xr2*g_Kr # Rapid time dependent potassium current xr1 gate xr1_inf = 1.0/(1.0 + 0.0243728440732796*ufl.exp(-0.142857142857143*V)) alpha_xr1 = 450.0/(1.0 + ufl.exp(-9/2 - V/10.0)) beta_xr1 = 6.0/(1.0 + 13.5813245225782*ufl.exp(0.0869565217391304*V)) tau_xr1 = alpha_xr1*beta_xr1 # Rapid time dependent potassium current xr2 gate xr2_inf = 1.0/(1.0 + 39.1212839981532*ufl.exp(0.0416666666666667*V)) alpha_xr2 = 3.0/(1.0 + 0.0497870683678639*ufl.exp(-0.05*V)) beta_xr2 = 1.12/(1.0 + 0.0497870683678639*ufl.exp(0.05*V)) tau_xr2 = alpha_xr2*beta_xr2 # Slow time dependent potassium current i_Ks = (Xs*Xs)*(V - E_Ks)*g_Ks # Slow time dependent potassium current xs gate xs_inf = 1.0/(1.0 + 0.69967253737513*ufl.exp(-0.0714285714285714*V)) alpha_xs = 1100.0/ufl.sqrt(1.0 +\ 0.188875602837562*ufl.exp(-0.166666666666667*V)) beta_xs = 1.0/(1.0 + 0.0497870683678639*ufl.exp(0.05*V)) tau_xs = alpha_xs*beta_xs # Fast sodium current i_Na = (m*m*m)*(-E_Na + V)*g_Na*h*j # Fast sodium current m gate m_inf = 1.0/((1.0 +\ 0.00184221158116513*ufl.exp(-0.110741971207087*V))*(1.0 +\ 0.00184221158116513*ufl.exp(-0.110741971207087*V))) alpha_m = 1.0/(1.0 + ufl.exp(-12.0 - V/5.0)) beta_m = 0.1/(1.0 + 0.778800783071405*ufl.exp(0.005*V)) + 0.1/(1.0 +\ ufl.exp(7.0 + V/5.0)) tau_m = alpha_m*beta_m # Fast sodium current h gate h_inf = 1.0/((1.0 + 15212.5932856544*ufl.exp(0.134589502018843*V))*(1.0 +\ 15212.5932856544*ufl.exp(0.134589502018843*V))) alpha_h = 4.43126792958051e-7*ufl.exp(-0.147058823529412*V)/(1.0 +\ 2.3538526683702e+17*ufl.exp(1.0*V)) beta_h = (310000.0*ufl.exp(0.3485*V) + 2.7*ufl.exp(0.079*V))/(1.0 +\ 2.3538526683702e+17*ufl.exp(1.0*V)) + 0.77*(1.0 - 1.0/(1.0 +\ 2.3538526683702e+17*ufl.exp(1.0*V)))/(0.13 +\ 0.0497581410839387*ufl.exp(-0.0900900900900901*V)) tau_h = 1.0/(alpha_h + beta_h) # Fast sodium current j gate j_inf = 1.0/((1.0 + 15212.5932856544*ufl.exp(0.134589502018843*V))*(1.0 +\ 15212.5932856544*ufl.exp(0.134589502018843*V))) alpha_j = (37.78 + V)*(-6.948e-6*ufl.exp(-0.04391*V) -\ 25428.0*ufl.exp(0.2444*V))/((1.0 +\ 2.3538526683702e+17*ufl.exp(1.0*V))*(1.0 +\ 50262745825.954*ufl.exp(0.311*V))) beta_j = 0.6*(1.0 - 1.0/(1.0 +\ 2.3538526683702e+17*ufl.exp(1.0*V)))*ufl.exp(0.057*V)/(1.0 +\ 0.0407622039783662*ufl.exp(-0.1*V)) +\ 0.02424*ufl.exp(-0.01052*V)/((1.0 +\ 2.3538526683702e+17*ufl.exp(1.0*V))*(1.0 +\ 0.00396086833990426*ufl.exp(-0.1378*V))) tau_j = 1.0/(alpha_j + beta_j) # Sodium background current i_b_Na = (-E_Na + V)*g_bna # L type ca current i_CaL = 4.0*(F*F)*(-0.341*Ca_o +\ Ca_i*ufl.exp(2.0*F*V/(R*T)))*V*d*f*fCa*g_CaL/((-1.0 +\ ufl.exp(2.0*F*V/(R*T)))*R*T) # L type ca current d gate d_inf = 1.0/(1.0 + 0.513417119032592*ufl.exp(-0.133333333333333*V)) alpha_d = 0.25 + 1.4/(1.0 +\ 0.0677244716592409*ufl.exp(-0.0769230769230769*V)) beta_d = 1.4/(1.0 + ufl.exp(1.0 + V/5.0)) gamma_d = 1.0/(1.0 + 12.1824939607035*ufl.exp(-0.05*V)) tau_d = gamma_d + alpha_d*beta_d # L type ca current f gate f_inf = 1.0/(1.0 + 17.4117080633276*ufl.exp(0.142857142857143*V)) tau_f = 80.0 + 165.0/(1.0 + ufl.exp(5/2 - V/10.0)) +\ 1125.0*ufl.exp(-0.00416666666666667*((27.0 + V)*(27.0 + V))) # L type ca current fca gate alpha_fCa = 1.0/(1.0 + 8.03402376701711e+27*ufl.elem_pow(Ca_i, 8.0)) beta_fCa = 0.1/(1.0 + 0.00673794699908547*ufl.exp(10000.0*Ca_i)) gama_fCa = 0.2/(1.0 + 0.391605626676799*ufl.exp(1250.0*Ca_i)) fCa_inf = 0.157534246575342 + 0.684931506849315*gama_fCa +\ 0.684931506849315*beta_fCa + 0.684931506849315*alpha_fCa tau_fCa = 2.0 d_fCa = (-fCa + fCa_inf)/tau_fCa # Calcium background current i_b_Ca = (V - E_Ca)*g_bca # Transient outward current i_to = (-E_K + V)*g_to*r*s # Transient outward current s gate s_inf = 1.0/(1.0 + ufl.exp(4.0 + V/5.0)) tau_s = 3.0 + 85.0*ufl.exp(-0.003125*((45.0 + V)*(45.0 + V))) + 5.0/(1.0 +\ ufl.exp(-4.0 + V/5.0)) # Transient outward current r gate r_inf = 1.0/(1.0 + 28.0316248945261*ufl.exp(-0.166666666666667*V)) tau_r = 0.8 + 9.5*ufl.exp(-0.000555555555555556*((40.0 + V)*(40.0 + V))) # Sodium potassium pump current i_NaK = K_o*Na_i*P_NaK/((K_mk + K_o)*(Na_i + K_mNa)*(1.0 +\ 0.0353*ufl.exp(-F*V/(R*T)) + 0.1245*ufl.exp(-0.1*F*V/(R*T)))) # Sodium calcium exchanger current i_NaCa = (-(Na_o*Na_o*Na_o)*Ca_i*alpha*ufl.exp((-1.0 + gamma)*F*V/(R*T))\ + (Na_i*Na_i*Na_i)*Ca_o*ufl.exp(F*V*gamma/(R*T)))*K_NaCa/((1.0 +\ K_sat*ufl.exp((-1.0 + gamma)*F*V/(R*T)))*((Na_o*Na_o*Na_o) +\ (Km_Nai*Km_Nai*Km_Nai))*(Km_Ca + Ca_o)) # Calcium pump current i_p_Ca = Ca_i*g_pCa/(K_pCa + Ca_i) # Potassium pump current i_p_K = (-E_K + V)*g_pK/(1.0 +\ 65.4052157419383*ufl.exp(-0.167224080267559*V)) # Calcium dynamics i_rel = ((Ca_SR*Ca_SR)*a_rel/((Ca_SR*Ca_SR) + (b_rel*b_rel)) + c_rel)*d*g i_up = Vmax_up/(1.0 + (K_up*K_up)/(Ca_i*Ca_i)) i_leak = (-Ca_i + Ca_SR)*V_leak g_inf = (1.0 - 1.0/(1.0 + 0.0301973834223185*ufl.exp(10000.0*Ca_i)))/(1.0 +\ 1.97201988740492e+55*ufl.elem_pow(Ca_i, 16.0)) + 1.0/((1.0 +\ 0.0301973834223185*ufl.exp(10000.0*Ca_i))*(1.0 +\ 5.43991024148102e+20*ufl.elem_pow(Ca_i, 6.0))) d_g = (-g + g_inf)/tau_g Ca_i_bufc = 1.0/(1.0 + Buf_c*K_buf_c/((K_buf_c + Ca_i)*(K_buf_c + Ca_i))) Ca_sr_bufsr = 1.0/(1.0 + Buf_sr*K_buf_sr/((K_buf_sr + Ca_SR)*(K_buf_sr +\ Ca_SR))) # Sodium dynamics # Membrane i_Stim = -(1.0 - 1.0/(1.0 + ufl.exp(-5.0*stim_start +\ 5.0*time)))*stim_amplitude/(1.0 + ufl.exp(-5.0*stim_start + 5.0*time\ - 5.0*stim_duration)) # Potassium dynamics # The ODE system: 17 states # Init test function _v = dolfin.TestFunction(states.function_space()) # Derivative for state Xr1 dy = ((-Xr1 + xr1_inf)/tau_xr1)*_v[0] # Derivative for state Xr2 dy += ((-Xr2 + xr2_inf)/tau_xr2)*_v[1] # Derivative for state Xs dy += ((-Xs + xs_inf)/tau_xs)*_v[2] # Derivative for state m dy += ((-m + m_inf)/tau_m)*_v[3] # Derivative for state h dy += ((-h + h_inf)/tau_h)*_v[4] # Derivative for state j dy += ((j_inf - j)/tau_j)*_v[5] # Derivative for state d dy += ((d_inf - d)/tau_d)*_v[6] # Derivative for state f dy += ((-f + f_inf)/tau_f)*_v[7] # Derivative for state fCa dy += ((1.0 - 1.0/((1.0 + ufl.exp(60.0 + V))*(1.0 + ufl.exp(-10.0*fCa +\ 10.0*fCa_inf))))*d_fCa)*_v[8] # Derivative for state s dy += ((-s + s_inf)/tau_s)*_v[9] # Derivative for state r dy += ((-r + r_inf)/tau_r)*_v[10] # Derivative for state Ca_SR dy += ((-i_leak + i_up - i_rel)*Ca_sr_bufsr*V_c/V_sr)*_v[11] # Derivative for state Ca_i dy += ((-i_up - (i_CaL + i_p_Ca + i_b_Ca - 2.0*i_NaCa)*Cm/(2.0*F*V_c) +\ i_leak + i_rel)*Ca_i_bufc)*_v[12] # Derivative for state g dy += ((1.0 - 1.0/((1.0 + ufl.exp(60.0 + V))*(1.0 + ufl.exp(-10.0*g +\ 10.0*g_inf))))*d_g)*_v[13] # Derivative for state Na_i dy += ((-3.0*i_NaK - 3.0*i_NaCa - i_Na - i_b_Na)*Cm/(F*V_c))*_v[14] # Derivative for state V dy += (-i_Ks - i_to - i_Kr - i_p_K - i_NaK - i_NaCa - i_Na - i_p_Ca -\ i_b_Na - i_CaL - i_Stim - i_K1 - i_b_Ca)*_v[15] # Derivative for state K_i dy += ((-i_Ks - i_to - i_Kr - i_p_K - i_Stim - i_K1 +\ 2.0*i_NaK)*Cm/(F*V_c))*_v[16] # Return dy return dy
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) # TODO check proper use of watches self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True) self.tc.init_watch('updateBC', 'Updated velocity BC', True, count_to_percent=True) self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True) self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True) self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True) self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True) self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True) self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True) self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True) self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True) self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True) self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') # Define function spaces (P2-P1) mesh = self.problem.mesh self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space if self.bc == 'lagrange': L = FunctionSpace(mesh, "R", 0) QL = self.Q*L problem.initialize(self.V, self.Q, self.PS, self.D) # Define trial and test functions u = TrialFunction(self.V) v = TestFunction(self.V) if self.bc == 'lagrange': (pQL, rQL) = TrialFunction(QL) (qQL, lQL) = TestFunction(QL) else: p = TrialFunction(self.Q) q = TestFunction(self.Q) n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt}, {'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) if doSave: problem.save_vel(False, u0, 0.0) problem.save_vel(True, u0, 0.0) u_ = Function(self.V) # current tentative velocity u_cor = Function(self.V) # current corrected velocity if self.bc == 'lagrange': p_QL = Function(QL) # current pressure or pressure help function from rotation scheme pQ = Function(self.Q) # auxiliary function for conversion between QL.sub(0) and Q else: p_ = Function(self.Q) # current pressure or pressure help function from rotation scheme p_mod = Function(self.Q) # current modified pressure from rotation scheme # Define coefficients k = Constant(self.metadata['dt']) f = Constant((0, 0, 0)) # Define forms # step 1: Tentative velocity, solve to u_ u_ext = 1.5*u0 - 0.5*u1 # extrapolation for convection term # Stabilisation h = CellSize(mesh) # CBC delta: if self.cbcDelta: delta = Constant(self.stabCoef)*h/(sqrt(inner(u_ext, u_ext))+h) else: delta = Constant(self.stabCoef)*h**2/(2*nu*k + k*h*inner(u_ext, u_ext)+h**2) if self.use_full_SUPG: v1 = v + delta*0.5*k*dot(grad(v), u_ext) parameters['form_compiler']['quadrature_degree'] = 6 else: v1 = v def nonlinearity(function): if self.use_ema: return 2*inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function)*u_ext, v1) * dx # return 2*inner(dot(sym(grad(function)), u_ext), v) * dx + inner(div(u_ext)*function, v) * dx # QQ implement this way? else: return inner(dot(grad(function), u_ext), v1) * dx def diffusion(fce): if self.useLaplace: return nu*inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': # IMP will work only if p=0 on output, or we must add term # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer return form if self.bcv == 'LAP': return form - inner(nu*dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form() if self.bcv == 'DDN': # IMP will work only if p=0 on output, or we must add term # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer return form # additional term must be added to non-constant part def pressure_rhs(): if self.useLaplace or self.bcv == 'LAP': return inner(p0, div(v1)) * dx - inner(p0*n, v1) * problem.get_outflow_measure_form() # NT term inner(inner(p, n), v) is 0 when p=0 on outflow else: return inner(p0, div(v1)) * dx a1_const = (1./k)*inner(u, v1)*dx + diffusion(0.5*u) a1_change = nonlinearity(0.5*u) if self.bcv == 'DDN': # IMP Problem: Does not penalize influx for current step, only for the next one # IMP this can lead to oscilation: DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx... # u and u_ext cannot be switched, min_value is nonlinear function a1_change += -0.5*min_value(Constant(0.), inner(u_ext, n))*inner(u, v1)*problem.get_outflow_measure_form() # IMP works only with uflacs compiler L1 = (1./k)*inner(u0, v1)*dx - nonlinearity(0.5*u0) - diffusion(0.5*u0) + pressure_rhs() if self.bcv == 'DDN': L1 += 0.5*min_value(0., inner(u_ext, n))*inner(u0, v1)*problem.get_outflow_measure_form() # Non-consistent SUPG stabilisation if self.stabilize and not self.use_full_SUPG: # a1_stab = delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx a1_stab = 0.5*delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) # NT optional: use Crank Nicolson in stabilisation term: change RHS # L1 += -0.5*delta*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) outflow_area = Constant(problem.outflow_area) need_outflow = Constant(0.0) if self.useRotationScheme: # Rotation scheme if self.bc == 'lagrange': F2 = inner(grad(pQL), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx else: F2 = inner(grad(p), grad(q))*dx + (1./k)*q*div(u_)*dx else: # Projection, solve to p_ if self.bc == 'lagrange': F2 = inner(grad(pQL - p0), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx else: if self.forceOutflow and problem.can_force_outflow: info('Forcing outflow.') F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx for m in problem.get_outflow_measures(): F2 += (1./k)*(1./outflow_area)*need_outflow*q*m else: F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx a2, L2 = system(F2) # step 3: Finalize, solve to u_ if self.useRotationScheme: # Rotation scheme if self.bc == 'lagrange': F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0)), v)*dx else: F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_), v)*dx else: if self.bc == 'lagrange': F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0) - p0), v)*dx else: F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_ - p0), v)*dx a3, L3 = system(F3) if self.useRotationScheme: # Rotation scheme: modify pressure if self.bc == 'lagrange': pr = TrialFunction(self.Q) qr = TestFunction(self.Q) F4 = (pr - p0 - p_QL.sub(0) + nu*div(u_))*qr*dx else: F4 = (p - p0 - p_ + nu*div(u_))*q*dx # TODO zkusit, jestli to nebude rychlejsi? nepocitat soustavu, ale p.assign(...), nutno project(div(u),Q) coz je pocitani podobne soustavy # TODO zkusit v project zadat solver_type='lu' >> primy resic by mel byt efektivnejsi a4, L4 = system(F4) # Assemble matrices self.tc.start('assembleMatrices') A1_const = assemble(a1_const) # need to be here, so A1 stays one Python object during repeated assembly A1_change = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwriten) if self.stabilize and not self.use_full_SUPG: A1_stab = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwriten) A2 = assemble(a2) A3 = assemble(a3) if self.useRotationScheme: A4 = assemble(a4) self.tc.end('assembleMatrices') if self.solvers == 'direct': self.solver_vel_tent = LUSolver('mumps') self.solver_vel_cor = LUSolver('mumps') self.solver_p = LUSolver('umfpack') if self.useRotationScheme: self.solver_rot = LUSolver('umfpack') else: # NT not needed, chosen not to use hypre_parasails # if self.prec_v == 'hypre_parasails': # in FEniCS 1.6.0 inaccessible using KrylovSolver class # self.solver_vel_tent = PETScKrylovSolver('gmres') # PETSc4py object # self.solver_vel_tent.ksp().getPC().setType('hypre') # PETScOptions.set('pc_hypre_type', 'parasails') # # this is global setting, but preconditioners for pressure solvers are set by their constructors # else: self.solver_vel_tent = KrylovSolver('gmres', self.prec_v) # nonsymetric > gmres # IMP cannot use 'ilu' in parallel (choose different default option) self.solver_vel_cor = KrylovSolver('cg', 'hypre_amg') # nonsymetric > gmres self.solver_p = KrylovSolver('cg', self.prec_p) # symmetric > CG if self.useRotationScheme: self.solver_rot = KrylovSolver('cg', self.prec_p) solver_options = {'monitor_convergence': True, 'maximum_iterations': 1000, 'nonzero_initial_guess': True} # 'nonzero_initial_guess': True with solver.solbe(A, u, b) means that # Solver will use anything stored in u as an initial guess # Get the nullspace if there are no pressure boundary conditions foo = Function(self.Q) # auxiliary vector for setting pressure nullspace if self.bc in ['nullspace', 'nullspace_s']: null_vec = Vector(foo.vector()) self.Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0/null_vec.norm('l2') self.null_space = VectorSpaceBasis([null_vec]) if self.bc == 'nullspace': as_backend_type(A2).set_nullspace(self.null_space) # apply global options for Krylov solvers self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.precision_rel_v_tent) self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.precision_abs_v_tent) self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12 self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4 self.solver_p.parameters['relative_tolerance'] = 10**(-self.precision_p) self.solver_p.parameters['absolute_tolerance'] = 10E-10 if self.useRotationScheme: self.solver_rot.parameters['relative_tolerance'] = 10**(-self.precision_p) self.solver_rot.parameters['absolute_tolerance'] = 10E-10 if self.solvers == 'krylov': for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_p, self.solver_rot] if \ self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]: for key, value in solver_options.items(): try: solver.parameters[key] = value except KeyError: info('Invalid option %s for KrylovSolver' % key) return 1 solver.parameters['preconditioner']['structure'] = 'same' # matrices A2-A4 do not change, so we can reuse preconditioners self.solver_vel_tent.parameters['preconditioner']['structure'] = 'same_nonzero_pattern' # matrix A1 changes every time step, so change of preconditioner must be allowed if self.bc == 'lagrange': fa = FunctionAssigner(self.Q, QL.sub(0)) # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.V, self.Q) self.tc.end('init') # Time-stepping info("Running of Incremental pressure correction scheme n. 1") ttime = self.metadata['time'] t = dt step = 1 while t < (ttime + dt/2.0): info("t = %f" % t) self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # DDN debug # u_ext_in = assemble(inner(u_ext, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_ext, n))*problem.get_outflow_measure_form()) # print('DDN: u_ext*n dSout = ', u_ext_in) # print('DDN: negative part of u_ext*n dSout = ', DDN_triggered) # assemble matrix (it depends on solution) self.tc.start('assembleA1') assemble(a1_change, tensor=A1_change) # assembling into existing matrix is faster than assembling new one A1 = A1_const.copy() # we dont want to change A1_const A1.axpy(1, A1_change, True) self.tc.end('assembleA1') self.tc.start('assembleA1stab') if self.stabilize and not self.use_full_SUPG: assemble(a1_stab, tensor=A1_stab) # assembling into existing matrix is faster than assembling new one A1.axpy(1, A1_stab, True) self.tc.end('assembleA1stab') # Compute tentative velocity step begin("Computing tentative velocity") self.tc.start('rhs') b = assemble(L1) self.tc.end('rhs') self.tc.start('applybc1') [bc.apply(A1, b) for bc in bcu] self.tc.end('applybc1') try: self.tc.start('solve 1') self.solver_vel_tent.solve(A1, u_.vector(), b) self.tc.end('solve 1') if save_this_step: self.tc.start('saveVel') problem.save_vel(True, u_, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(True, u_) problem.compute_err(True, u_, t) problem.compute_div(True, u_) except RuntimeError as inst: problem.report_fail(t) return 1 end() # DDN debug # u_ext_in = assemble(inner(u_, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_, n))*problem.get_outflow_measure_form()) # print('DDN: u_tent*n dSout = ', u_ext_in) # print('DDN: negative part of u_tent*n dSout = ', DDN_triggered) if self.useRotationScheme: begin("Computing tentative pressure") else: begin("Computing pressure") if self.forceOutflow and problem.can_force_outflow: out = problem.compute_outflow(u_) info('Tentative outflow: %f' % out) n_o = -problem.last_inflow-out info('Needed outflow: %f' % n_o) need_outflow.assign(n_o) self.tc.start('rhs') b = assemble(L2) self.tc.end('rhs') self.tc.start('applybcP') [bc.apply(A2, b) for bc in bcp] if self.bc in ['nullspace', 'nullspace_s']: self.null_space.orthogonalize(b) self.tc.end('applybcP') try: self.tc.start('solve 2') if self.bc == 'lagrange': self.solver_p.solve(A2, p_QL.vector(), b) else: self.solver_p.solve(A2, p_.vector(), b) self.tc.end('solve 2') except RuntimeError as inst: problem.report_fail(t) return 1 if self.useRotationScheme: foo = Function(self.Q) if self.bc == 'lagrange': fa.assign(pQ, p_QL.sub(0)) foo.assign(pQ + p0) else: foo.assign(p_+p0) problem.averaging_pressure(foo) if save_this_step and not onlyVel: problem.save_pressure(True, foo) else: if self.bc == 'lagrange': fa.assign(pQ, p_QL.sub(0)) problem.averaging_pressure(pQ) if save_this_step and not onlyVel: problem.save_pressure(False, pQ) else: # we do not want to change p=0 on outflow, it conflicts with do-nothing conditions foo = Function(self.Q) foo.assign(p_) problem.averaging_pressure(foo) if save_this_step and not onlyVel: problem.save_pressure(False, foo) end() begin("Computing corrected velocity") self.tc.start('rhs') b = assemble(L3) self.tc.end('rhs') if not self.B: self.tc.start('applybc3') [bc.apply(A3, b) for bc in bcu] self.tc.end('applybc3') try: self.tc.start('solve 3') self.solver_vel_cor.solve(A3, u_cor.vector(), b) self.tc.end('solve 3') problem.compute_err(False, u_cor, t) problem.compute_div(False, u_cor) except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step: self.tc.start('saveVel') problem.save_vel(False, u_cor, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u_cor) end() # DDN debug # u_ext_in = assemble(inner(u_cor, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_cor, n))*problem.get_outflow_measure_form()) # print('DDN: u_cor*n dSout = ', u_ext_in) # print('DDN: negative part of u_cor*n dSout = ', DDN_triggered) if self.useRotationScheme: begin("Rotation scheme pressure correction") self.tc.start('rhs') b = assemble(L4) self.tc.end('rhs') try: self.tc.start('solve 4') self.solver_rot.solve(A4, p_mod.vector(), b) self.tc.end('solve 4') except RuntimeError as inst: problem.report_fail(t) return 1 problem.averaging_pressure(p_mod) if save_this_step and not onlyVel: problem.save_pressure(False, p_mod) end() # compute functionals (e. g. forces) problem.compute_functionals(u_cor, p_mod if self.useRotationScheme else (pQ if self.bc == 'lagrange' else p_), t) # Move to next time step self.tc.start('next') u1.assign(u0) u0.assign(u_cor) u_.assign(u_cor) # use corretced velocity as initial guess in first step if self.useRotationScheme: p0.assign(p_mod) else: if self.bc == 'lagrange': p0.assign(pQ) else: p0.assign(p_) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: Incremental pressure correction scheme n. 1") problem.report() return 0
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True) self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True) self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True) self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True) self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True) self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True) self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True) self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True) self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True) self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True) self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') # Define function spaces (P2-P1) mesh = self.problem.mesh self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space problem.initialize(self.V, self.Q, self.PS, self.D) # Define trial and test functions u = TrialFunction(self.V) v = TestFunction(self.V) p = TrialFunction(self.Q) q = TestFunction(self.Q) n = FacetNormal(mesh) I = Identity(find_geometric_dimension(u)) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt}, {'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) u_ = Function(self.V) # current tentative velocity u_cor = Function(self.V) # current corrected velocity p_ = Function(self.Q) # current pressure or pressure help function from rotation scheme p_mod = Function(self.Q) # current modified pressure from rotation scheme # Define coefficients k = Constant(self.metadata['dt']) f = Constant((0, 0, 0)) # Define forms # step 1: Tentative velocity, solve to u_ u_ext = 1.5 * u0 - 0.5 * u1 # extrapolation for convection term # Stabilisation h = CellSize(mesh) if self.args.cbc_tau: # used in Simula cbcflow project tau = Constant(self.stabCoef) * h / (sqrt(inner(u_ext, u_ext)) + h) else: # proposed in R. Codina: On stabilized finite element methods for linear systems of # convection-diffusion-reaction equations. tau = Constant(self.stabCoef) * k * h ** 2 / ( 2 * nu * k + k * h * sqrt(DOLFIN_EPS + inner(u_ext, u_ext)) + h ** 2) # DOLFIN_EPS is added because of FEniCS bug that inner(u_ext, u_ext) can be negative when u_ext = 0 if self.use_full_SUPG: v1 = v + tau * 0.5 * dot(grad(v), u_ext) parameters['form_compiler']['quadrature_degree'] = 6 else: v1 = v def nonlinearity(function): if self.args.ema: return 2 * inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function) * u_ext, v1) * dx else: return inner(dot(grad(function), u_ext), v1) * dx def diffusion(fce): if self.useLaplace: return nu * inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': return form if self.bcv == 'LAP': return form - inner(nu * dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form() if self.bcv == 'DDN': return form # additional term must be added to non-constant part def pressure_rhs(): if self.args.bc == 'outflow': return inner(p0, div(v1)) * dx else: return inner(p0, div(v1)) * dx - inner(p0 * n, v1) * problem.get_outflow_measure_form() a1_const = (1. / k) * inner(u, v1) * dx + diffusion(0.5 * u) a1_change = nonlinearity(0.5 * u) if self.bcv == 'DDN': # does not penalize influx for current step, only for the next one # this can lead to oscilation: # DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx... # u and u_ext cannot be switched, min_value is nonlinear function a1_change += -0.5 * min_value(Constant(0.), inner(u_ext, n)) * inner(u, v1) * problem.get_outflow_measure_form() # NT works only with uflacs compiler L1 = (1. / k) * inner(u0, v1) * dx - nonlinearity(0.5 * u0) - diffusion(0.5 * u0) + pressure_rhs() if self.bcv == 'DDN': L1 += 0.5 * min_value(0., inner(u_ext, n)) * inner(u0, v1) * problem.get_outflow_measure_form() # Non-consistent SUPG stabilisation if self.stabilize and not self.use_full_SUPG: # a1_stab = tau*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx a1_stab = 0.5 * tau * inner(dot(grad(u), u_ext), dot(grad(v), u_ext)) * dx(None, {'quadrature_degree': 6}) # optional: to use Crank Nicolson in stabilisation term following change of RHS is needed: # L1 += -0.5*tau*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) outflow_area = Constant(problem.outflow_area) need_outflow = Constant(0.0) if self.useRotationScheme: # Rotation scheme F2 = inner(grad(p), grad(q)) * dx + (1. / k) * q * div(u_) * dx else: # Projection, solve to p_ if self.forceOutflow and problem.can_force_outflow: info('Forcing outflow.') F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx for m in problem.get_outflow_measures(): F2 += (1. / k) * (1. / outflow_area) * need_outflow * q * m else: F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx a2, L2 = system(F2) # step 3: Finalize, solve to u_ if self.useRotationScheme: # Rotation scheme F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_), v) * dx else: F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_ - p0), v) * dx a3, L3 = system(F3) if self.useRotationScheme: # Rotation scheme: modify pressure F4 = (p - p0 - p_ + nu * div(u_)) * q * dx a4, L4 = system(F4) # Assemble matrices self.tc.start('assembleMatrices') A1_const = assemble(a1_const) # must be here, so A1 stays one Python object during repeated assembly A1_change = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwritten) if self.stabilize and not self.use_full_SUPG: A1_stab = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwritten) A2 = assemble(a2) A3 = assemble(a3) if self.useRotationScheme: A4 = assemble(a4) self.tc.end('assembleMatrices') if self.solvers == 'direct': self.solver_vel_tent = LUSolver('mumps') self.solver_vel_cor = LUSolver('mumps') self.solver_p = LUSolver('mumps') if self.useRotationScheme: self.solver_rot = LUSolver('mumps') else: # NT 2016-1 KrylovSolver >> PETScKrylovSolver # not needed, chosen not to use hypre_parasails: # if self.prec_v == 'hypre_parasails': # in FEniCS 1.6.0 inaccessible using KrylovSolver class # self.solver_vel_tent = PETScKrylovSolver('gmres') # PETSc4py object # self.solver_vel_tent.ksp().getPC().setType('hypre') # PETScOptions.set('pc_hypre_type', 'parasails') # # this is global setting, but preconditioners for pressure solvers are set by their constructors # else: self.solver_vel_tent = PETScKrylovSolver('gmres', self.args.precV) # nonsymetric > gmres # cannot use 'ilu' in parallel self.solver_vel_cor = PETScKrylovSolver('cg', self.args.precVC) self.solver_p = PETScKrylovSolver(self.args.solP, self.args.precP) # almost (up to BC) symmetric > CG if self.useRotationScheme: self.solver_rot = PETScKrylovSolver('cg', 'hypre_amg') # setup Krylov solvers if self.solvers == 'krylov': # Get the nullspace if there are no pressure boundary conditions foo = Function(self.Q) # auxiliary vector for setting pressure nullspace if self.args.bc == 'nullspace': null_vec = Vector(foo.vector()) self.Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0 / null_vec.norm('l2') self.null_space = VectorSpaceBasis([null_vec]) as_backend_type(A2).set_nullspace(self.null_space) # apply global options for Krylov solvers solver_options = {'monitor_convergence': True, 'maximum_iterations': 10000, 'nonzero_initial_guess': True} # 'nonzero_initial_guess': True with solver.solve(A, u, b) means that # Solver will use anything stored in u as an initial guess for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_rot, self.solver_p] if \ self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]: for key, value in solver_options.items(): try: solver.parameters[key] = value except KeyError: info('Invalid option %s for KrylovSolver' % key) return 1 if self.args.solP == 'richardson': self.solver_p.parameters['monitor_convergence'] = False self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.args.prv1) self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.args.pav1) self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12 self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4 self.solver_p.parameters['relative_tolerance'] = 10 ** (-self.args.prp) self.solver_p.parameters['absolute_tolerance'] = 10 ** (-self.args.pap) if self.useRotationScheme: self.solver_rot.parameters['relative_tolerance'] = 10E-10 self.solver_rot.parameters['absolute_tolerance'] = 10E-10 if self.args.Vrestart > 0: self.solver_vel_tent.parameters['gmres']['restart'] = self.args.Vrestart if self.args.solP == 'gmres' and self.args.Prestart > 0: self.solver_p.parameters['gmres']['restart'] = self.args.Prestart # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.args.bc == 'outflow', self.V, self.Q) self.tc.end('init') # Time-stepping info("Running of Incremental pressure correction scheme n. 1") ttime = self.metadata['time'] t = dt step = 1 # debug function if problem.args.debug_rot: plot_cor_v = Function(self.V) while t < (ttime + dt / 2.0): self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # assemble matrix (it depends on solution) self.tc.start('assembleA1') assemble(a1_change, tensor=A1_change) # assembling into existing matrix is faster than assembling new one A1 = A1_const.copy() # we dont want to change A1_const A1.axpy(1, A1_change, True) self.tc.end('assembleA1') self.tc.start('assembleA1stab') if self.stabilize and not self.use_full_SUPG: assemble(a1_stab, tensor=A1_stab) # assembling into existing matrix is faster than assembling new one A1.axpy(1, A1_stab, True) self.tc.end('assembleA1stab') # Compute tentative velocity step begin("Computing tentative velocity") self.tc.start('rhs') b = assemble(L1) self.tc.end('rhs') self.tc.start('applybc1') [bc.apply(A1, b) for bc in bcu] self.tc.end('applybc1') try: self.tc.start('solve 1') self.solver_vel_tent.solve(A1, u_.vector(), b) self.tc.end('solve 1') if save_this_step: self.tc.start('saveVel') problem.save_vel(True, u_) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(True, u_) problem.compute_err(True, u_, t) problem.compute_div(True, u_) except RuntimeError as inst: problem.report_fail(t) return 1 end() if self.useRotationScheme: begin("Computing tentative pressure") else: begin("Computing pressure") if self.forceOutflow and problem.can_force_outflow: out = problem.compute_outflow(u_) info('Tentative outflow: %f' % out) n_o = -problem.last_inflow - out info('Needed outflow: %f' % n_o) need_outflow.assign(n_o) self.tc.start('rhs') b = assemble(L2) self.tc.end('rhs') self.tc.start('applybcP') [bc.apply(A2, b) for bc in bcp] if self.args.bc == 'nullspace': self.null_space.orthogonalize(b) self.tc.end('applybcP') try: self.tc.start('solve 2') self.solver_p.solve(A2, p_.vector(), b) self.tc.end('solve 2') except RuntimeError as inst: problem.report_fail(t) return 1 if self.useRotationScheme: foo = Function(self.Q) foo.assign(p_ + p0) if save_this_step and not onlyVel: problem.averaging_pressure(foo) problem.save_pressure(True, foo) else: foo = Function(self.Q) foo.assign(p_) # we do not want to change p_ by averaging if save_this_step and not onlyVel: problem.averaging_pressure(foo) problem.save_pressure(False, foo) end() begin("Computing corrected velocity") self.tc.start('rhs') b = assemble(L3) self.tc.end('rhs') if not self.args.B: self.tc.start('applybc3') [bc.apply(A3, b) for bc in bcu] self.tc.end('applybc3') try: self.tc.start('solve 3') self.solver_vel_cor.solve(A3, u_cor.vector(), b) self.tc.end('solve 3') problem.compute_err(False, u_cor, t) problem.compute_div(False, u_cor) except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step: self.tc.start('saveVel') problem.save_vel(False, u_cor) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u_cor) end() if self.useRotationScheme: begin("Rotation scheme pressure correction") self.tc.start('rhs') b = assemble(L4) self.tc.end('rhs') try: self.tc.start('solve 4') self.solver_rot.solve(A4, p_mod.vector(), b) self.tc.end('solve 4') except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step and not onlyVel: problem.averaging_pressure(p_mod) problem.save_pressure(False, p_mod) end() if problem.args.debug_rot: # save applied pressure correction (expressed as a term added to RHS of next tentative vel. step) # see comment next to argument definition plot_cor_v.assign(project(k * grad(nu * div(u_)), self.V)) problem.fileDict['grad_cor']['file'].write(plot_cor_v, t) # compute functionals (e. g. forces) problem.compute_functionals(u_cor, p_mod if self.useRotationScheme else p_, t, step) # Move to next time step self.tc.start('next') u1.assign(u0) u0.assign(u_cor) u_.assign(u_cor) # use corrected velocity as initial guess in first step if self.useRotationScheme: p0.assign(p_mod) else: p0.assign(p_) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: Incremental pressure correction scheme n. 1") problem.report() return 0