class TVPD(TV): """ Total variation using primal-dual Newton """ def isPD(self): return True def updatePD(self): """ Update the parameters. parameters should be: - k(x) = factor inside TV - eps = regularization parameter - Vm = FunctionSpace for parameter. ||f||_TV = int k(x) sqrt{|grad f|^2 + eps} dx """ # primal dual variables self.Vw = FunctionSpace(self.Vm.mesh(), 'DG', 0) self.wH = Function(self.Vw*self.Vw) # dual variable used in Hessian (re-scaled) #self.wH = nabla_grad(self.m)/sqrt(self.fTV) # full Hessian self.w = Function(self.Vw*self.Vw) # dual variable for primal-dual, initialized at 0 self.dm = Function(self.Vm) self.dw = Function(self.Vw*self.Vw) self.testw = TestFunction(self.Vw*self.Vw) self.trialw = TrialFunction(self.Vw*self.Vw) # investigate convergence of dual variable self.dualres = self.w*sqrt(self.fTV) - nabla_grad(self.m) self.dualresnorm = inner(self.dualres, self.dualres)*dx self.normgraddm = inner(nabla_grad(self.dm), nabla_grad(self.dm))*dx # Hessian self.wkformPDhess = self.kovsq * ( \ inner(nabla_grad(self.trial), nabla_grad(self.test)) - \ 0.5*( inner(self.wH, nabla_grad(self.test))*\ inner(nabla_grad(self.trial), nabla_grad(self.m)) + \ inner(nabla_grad(self.m), nabla_grad(self.test))*\ inner(nabla_grad(self.trial), self.wH) ) / sqrt(self.fTV) \ )*dx # update dual variable self.Mw = assemble(inner(self.trialw, self.testw)*dx) self.rhswwk = inner(-self.w, self.testw)*dx + \ inner(nabla_grad(self.m)+nabla_grad(self.dm), self.testw) \ /sqrt(self.fTV)*dx + \ inner(-inner(nabla_grad(self.m),nabla_grad(self.dm))* \ self.wH/sqrt(self.fTV), self.testw)*dx def compute_dw(self, dm): """ Compute dw """ setfct(self.dm, dm) b = assemble(self.rhswwk) solve(self.Mw, self.dw.vector(), b) def update_w(self, alpha): """ update w and re-scale wH """ self.w.vector().axpy(alpha, self.dw.vector()) # project each w (coord-wise) onto unit sphere to get wH (wx, wy) = self.w.split(deepcopy=True) wxa, wya = wx.vector().array(), wy.vector().array() normw = np.sqrt(wxa**2 + wya**2) factorw = [max(1.0, ii) for ii in normw] setfct(wx, wxa/factorw) setfct(wy, wya/factorw) assign(self.wH.sub(0), wx) assign(self.wH.sub(1), wy) # check (wx,wy) = self.wH.split(deepcopy=True) wxa, wya = wx.vector().array(), wy.vector().array() assert np.amax(np.sqrt(wxa**2 + wya**2)) <= 1.0 + 1e-14 # Print results dualresnorm = assemble(self.dualresnorm) normgraddm = assemble(self.normgraddm) print 'line search dual variable: max(|w|)={}, err(w,df)={}, |grad(dm)|={}'.\ format(np.amax(np.sqrt(normw)), np.sqrt(dualresnorm), np.sqrt(normgraddm))
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('solve', 'Running nonlinear solver', 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') mesh = self.problem.mesh # Define function spaces (P2-P1) self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.W = MixedFunctionSpace([self.V, self.Q]) self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space # to assign solution in space W.sub(0) to Function(V) we need FunctionAssigner (cannot be assigned directly) fa = FunctionAssigner(self.V, self.W.sub(0)) velSp = Function(self.V) problem.initialize(self.V, self.Q, self.PS, self.D) # Define unknown and test function(s) NS v, q = TestFunctions(self.W) w = Function(self.W) dw = TrialFunction(self.W) u, p = split(w) # Define fields n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) theta = 0.5 # Crank-Nicholson k = Constant(self.metadata['dt']) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) if doSave: problem.save_vel(False, u0, 0.0) # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.W.sub(0), self.W.sub(1)) # NT bcp is not used # Define steady part of the equation def T(u): return -p * I + 2.0 * nu * sym(grad(u)) def F(u, v, q): return (inner(T(u), grad(v)) - q * div(u)) * dx + inner(grad(u) * u, v) * dx # Define variational forms F_ns = (inner((u - u0), v) / k) * dx + (1.0 - theta) * F(u0, v, q) + theta * F(u, v, q) J_ns = derivative(F_ns, w, dw) # J_ns = derivative(F_ns, w) # did not work # NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns, form_compiler_parameters=ffc_options) NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns) # (var. formulation, unknown, Dir. BC, jacobian, optional) NS_solver = NonlinearVariationalSolver(NS_problem) prm = NS_solver.parameters prm['newton_solver']['absolute_tolerance'] = 1E-08 prm['newton_solver']['relative_tolerance'] = 1E-08 # prm['newton_solver']['maximum_iterations'] = 45 # prm['newton_solver']['relaxation_parameter'] = 1.0 prm['newton_solver']['linear_solver'] = 'mumps' info(NS_solver.parameters, True) self.tc.end('init') # Time-stepping info("Running of direct method") 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 # Compute begin("Solving NS ....") try: self.tc.start('solve') NS_solver.solve() self.tc.end('solve') except RuntimeError as inst: problem.report_fail(t) return 1 end() # Extract solutions: (u, p) = w.split() fa.assign(velSp, u) # we are assigning twice (now and inside save_vel), but it works with one method save_vel for direct and # projection (we could split save_vel to save one assign) if save_this_step: self.tc.start('saveVel') problem.save_vel(False, velSp, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u) problem.compute_err(False, u, t) problem.compute_div(False, u) # foo = Function(self.Q) # foo.assign(p) # problem.averaging_pressure(foo) # if save_this_step and not onlyVel: # problem.save_pressure(False, foo) if save_this_step and not onlyVel: problem.save_pressure(False, p) # compute functionals (e. g. forces) problem.compute_functionals(u, p, t) # Move to next time step self.tc.start('next') u0.assign(velSp) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: direct method") problem.report() return 0
def _evaluateGlobalMixedEstimator(cls, mu, w, coeff_field, pde, f, quadrature_degree, vectorspace_type='BDM'): """Evaluation of global mixed equilibrated estimator.""" # set quadrature degree # quadrature_degree_old = parameters["form_compiler"]["quadrature_degree"] # parameters["form_compiler"]["quadrature_degree"] = quadrature_degree # logger.debug("residual quadrature order = " + str(quadrature_degree)) # prepare numerical flux and f sigma_mu, f_mu = evaluate_numerical_flux(w, mu, coeff_field, f) # ################### # ## MIXED PROBLEM ## # ################### # get setup data for mixed problem V = w[mu]._fefunc.function_space() mesh = V.mesh() degree = element_degree(w[mu]._fefunc) # create function spaces DG0 = FunctionSpace(mesh, 'DG', 0) DG0_dofs = [DG0.dofmap().cell_dofs(c.index())[0] for c in cells(mesh)] RT = FunctionSpace(mesh, vectorspace_type, degree) W = RT * DG0 # setup boundary conditions # bcs = pde.create_dirichlet_bcs(W.sub(1)) # debug === # from dolfin import DOLFIN_EPS, DirichletBC # def boundary(x): # return x[0] < DOLFIN_EPS or x[0] > 1.0 + DOLFIN_EPS or x[1] < DOLFIN_EPS or x[1] > 1.0 + DOLFIN_EPS # bcs = [DirichletBC(W.sub(1), Constant(0.0), boundary)] # === debug # create trial and test functions (sigma, u) = TrialFunctions(W) (tau, v) = TestFunctions(W) # define variational form a_eq = (dot(sigma, tau) + div(tau) * u + div(sigma) * v) * dx L_eq = (- f_mu * v + dot(sigma_mu, tau)) * dx # compute solution w_eq = Function(W) solve(a_eq == L_eq, w_eq) (sigma_mixed, u_mixed) = w_eq.split() # ############################# # ## EQUILIBRATION ESTIMATOR ## # ############################# # evaluate error estimator dg0 = TestFunction(DG0) eta_mu = inner(sigma_mu, sigma_mu) * dg0 * dx eta_T = assemble(eta_mu, form_compiler_parameters={'quadrature_degree': quadrature_degree}) eta_T = np.array([sqrt(e) for e in eta_T]) # evaluate global error eta = sqrt(sum(i**2 for i in eta_T)) # reorder array entries for local estimators eta_T = eta_T[DG0_dofs] # restore quadrature degree # parameters["form_compiler"]["quadrature_degree"] = quadrature_degree_old return eta, FlatVector(eta_T)
def solve_fixed_point( mesh, W_element, P_element, Q_element, u0, p0, theta0, kappa, rho, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs, theta_neumann_bcs, my_dx, my_ds, max_iter, tol ): # Solve the coupled heat-Stokes equation approximately. Do this # iteratively by solving the heat equation, then solving Stokes with the # updated heat, the heat equation with the updated velocity and so forth # until the change is 'small'. WP = FunctionSpace(mesh, MixedElement([W_element, P_element])) Q = FunctionSpace(mesh, Q_element) # Initialize functions. up0 = Function(WP) u0, p0 = up0.split() theta1 = Function(Q) for _ in range(max_iter): heat_problem = heat.Heat( Q, kappa=kappa, rho=rho(theta0), cp=cp, convection=u0, source=heat_source, dirichlet_bcs=theta_dirichlet_bcs, neumann_bcs=theta_neumann_bcs, my_dx=my_dx, my_ds=my_ds ) theta1.assign(heat_problem.solve_stationary()) # Solve problem for velocity, pressure. f = rho(theta0) * g # coupling if extra_force: f += as_vector((extra_force[0], extra_force[1], 0.0)) # up1 = up0.copy() stokes.stokes_solve( up0, mu, u_bcs, p_bcs, f, my_dx=my_dx, tol=1.0e-10, verbose=False, maxiter=1000 ) # from dolfin import plot # plot(u0) # plot(theta0) theta_diff = errornorm(theta0, theta1) info('||theta - theta0|| = {:e}'.format(theta_diff)) # info('||u - u0|| = {:e}'.format(u_diff)) # info('||p - p0|| = {:e}'.format(p_diff)) # diff = theta_diff + u_diff + p_diff diff = theta_diff info('sum = {:e}'.format(diff)) # # Show the iterates. # plot(theta0, title='theta0') # plot(u0, title='u0') # interactive() # #exit() if diff < tol: break theta0.assign(theta1) # Create a *deep* copy of u0, p0, to be able to deal with them as # actually separate entities. u0, p0 = up0.split(deepcopy=True) return u0, p0, theta0
def ab2tr_step0(u0, P, f, # right-hand side rho, mu, dudt_bcs=[], p_bcs=[], eps=1.0e-4, # relative error tolerance verbose=True ): # Make sure that the initial velocity is divergence-free. alpha = norm(u0, 'Hdiv0') if abs(alpha) > DOLFIN_EPS: warn('Initial velocity not divergence-free (||u||_div = %e).' % alpha ) # Get the initial u0' and p0 by solving the linear equation system # # [M C] [u0'] [f0 - (K+N(u0)u0)] # [C^T 0] [p0 ] = [ g0' ], # # i.e., # # rho u0' + nabla(p0) = f0 + mu\Delta(u0) - rho u0.nabla(u0), # div(u0') = 0. # W = u0.function_space() WP = W*P # Translate the boundary conditions into product space. See # <http://fenicsproject.org/qa/703/boundary-conditions-in-product-space>. dudt_bcs_new = [] for dudt_bc in dudt_bcs: dudt_bcs_new.append(DirichletBC(WP.sub(0), dudt_bc.value(), dudt_bc.user_sub_domain())) p_bcs_new = [] for p_bc in p_bcs: p_bcs_new.append(DirichletBC(WP.sub(1), p_bc.value(), p_bc.user_sub_domain())) new_bcs = dudt_bcs_new + p_bcs_new (u, p) = TrialFunctions(WP) (v, q) = TestFunctions(WP) #a = rho * dot(u, v) * dx + dot(grad(p), v) * dx \ a = rho * inner(u, v) * dx - p * div(v) * dx \ - div(u) * q * dx L = _rhs_weak(u0, v, f, rho, mu) A, b = assemble_system(a, L, new_bcs) # Similar preconditioner as for the Stokes problem. # TODO implement something better! prec = rho * inner(u, v) * dx \ - p*q*dx M, _ = assemble_system(prec, L, new_bcs) solver = KrylovSolver('gmres', 'amg') solver.parameters['monitor_convergence'] = verbose solver.parameters['report'] = verbose solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['relative_tolerance'] = 1.0e-6 solver.parameters['maximum_iterations'] = 10000 # Associate operator (A) and preconditioner matrix (M) solver.set_operators(A, M) #solver.set_operator(A) # Solve up = Function(WP) solver.solve(up.vector(), b) # Get sub-functions dudt0, p0 = up.split() # Choosing the first step size for the trapezoidal rule can be tricky. # Chapters 2.7.4a, 2.7.4e of the book # # Incompressible flow and the finite element method, # volume 1: advection-diffusion; # P.M. Gresho, R.L. Sani, # # give some hints. # # eps ... relative error tolerance # tau ... estimate of the initial 'time constant' tau = None if tau: dt0 = tau * eps**(1.0/3.0) else: # Choose something 'reasonably small'. dt0 = 1.0e-3 # Alternative: # Use a dissipative scheme like backward Euler or BDF2 for the first # couple of steps. This makes sure that noisy initial data is damped # out. return dudt0, p0, dt0
def solve(W, P, mu, u_bcs, p_bcs, f, verbose=True, tol=1.0e-10 ): # Some initial sanity checks. assert mu > 0.0 WP = MixedFunctionSpace([W, P]) # Translate the boundary conditions into the product space. # This conditional loop is able to deal with conditions of the kind # # DirichletBC(W.sub(1), 0.0, right_boundary) # new_bcs = [] for k, bcs in enumerate([u_bcs, p_bcs]): for bc in bcs: space = bc.function_space() C = space.component() if len(C) == 0: new_bcs.append(DirichletBC(WP.sub(k), bc.value(), bc.domain_args[0])) elif len(C) == 1: new_bcs.append(DirichletBC(WP.sub(k).sub(int(C[0])), bc.value(), bc.domain_args[0])) else: raise RuntimeError('Illegal number of subspace components.') # Define variational problem (u, p) = TrialFunctions(WP) (v, q) = TestFunctions(WP) # Build system. # The sign of the div(u)-term is somewhat arbitrary since the right-hand # side is 0 here. We can either make the system symmetric or positive- # definite. # On a second note, we have # # \int grad(p).v = - \int p * div(v) + \int_\Gamma p n.v. # # Since, we have either p=0 or n.v=0 on the boundary, we could as well # replace the term dot(grad(p), v) by -p*div(v). # a = mu * inner(grad(u), grad(v))*dx \ - p * div(v) * dx \ - q * div(u) * dx #a = mu * inner(grad(u), grad(v))*dx + dot(grad(p), v) * dx \ # - div(u) * q * dx L = dot(f, v)*dx A, b = assemble_system(a, L, new_bcs) if has_petsc(): # For an assortment of preconditioners, see # # Performance and analysis of saddle point preconditioners # for the discrete steady-state Navier-Stokes equations; # H.C. Elman, D.J. Silvester, A.J. Wathen; # Numer. Math. (2002) 90: 665-688; # <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>. # # Set up field split. W = SubSpace(WP, 0) P = SubSpace(WP, 1) u_dofs = W.dofmap().dofs() p_dofs = P.dofmap().dofs() prec = PETScPreconditioner() prec.set_fieldsplit([u_dofs, p_dofs], ['u', 'p']) PETScOptions.set('pc_type', 'fieldsplit') PETScOptions.set('pc_fieldsplit_type', 'additive') PETScOptions.set('fieldsplit_u_pc_type', 'lu') PETScOptions.set('fieldsplit_p_pc_type', 'jacobi') ## <http://scicomp.stackexchange.com/questions/7288/which-preconditioners-and-solver-in-petsc-for-indefinite-symmetric-systems-sho> #PETScOptions.set('pc_type', 'fieldsplit') ##PETScOptions.set('pc_fieldsplit_type', 'schur') ##PETScOptions.set('pc_fieldsplit_schur_fact_type', 'upper') #PETScOptions.set('pc_fieldsplit_detect_saddle_point') ##PETScOptions.set('fieldsplit_u_pc_type', 'lsc') ##PETScOptions.set('fieldsplit_u_ksp_type', 'preonly') #PETScOptions.set('pc_type', 'fieldsplit') #PETScOptions.set('fieldsplit_u_pc_type', 'hypre') #PETScOptions.set('fieldsplit_u_ksp_type', 'preonly') #PETScOptions.set('fieldsplit_p_pc_type', 'jacobi') #PETScOptions.set('fieldsplit_p_ksp_type', 'preonly') ## From PETSc/src/ksp/ksp/examples/tutorials/ex42-fsschur.opts: #PETScOptions.set('pc_type', 'fieldsplit') #PETScOptions.set('pc_fieldsplit_type', 'SCHUR') #PETScOptions.set('pc_fieldsplit_schur_fact_type', 'UPPER') #PETScOptions.set('fieldsplit_p_ksp_type', 'preonly') #PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi') ## From ## ## Composable Linear Solvers for Multiphysics; ## J. Brown, M. Knepley, D.A. May, L.C. McInnes, B. Smith; ## <http://www.computer.org/csdl/proceedings/ispdc/2012/4805/00/4805a055-abs.html>; ## <http://www.mcs.anl.gov/uploads/cels/papers/P2017-0112.pdf>. ## #PETScOptions.set('pc_type', 'fieldsplit') #PETScOptions.set('pc_fieldsplit_type', 'schur') #PETScOptions.set('pc_fieldsplit_schur_factorization_type', 'upper') ## #PETScOptions.set('fieldsplit_u_ksp_type', 'cg') #PETScOptions.set('fieldsplit_u_ksp_rtol', 1.0e-6) #PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi') #PETScOptions.set('fieldsplit_u_sub_pc_type', 'cholesky') ## #PETScOptions.set('fieldsplit_p_ksp_type', 'fgmres') #PETScOptions.set('fieldsplit_p_ksp_constant_null_space') #PETScOptions.set('fieldsplit_p_pc_type', 'lsc') ## #PETScOptions.set('fieldsplit_p_lsc_ksp_type', 'cg') #PETScOptions.set('fieldsplit_p_lsc_ksp_rtol', 1.0e-2) #PETScOptions.set('fieldsplit_p_lsc_ksp_constant_null_space') ##PETScOptions.set('fieldsplit_p_lsc_ksp_converged_reason') #PETScOptions.set('fieldsplit_p_lsc_pc_type', 'bjacobi') #PETScOptions.set('fieldsplit_p_lsc_sub_pc_type', 'icc') # Create Krylov solver with custom preconditioner. solver = PETScKrylovSolver('gmres', prec) solver.set_operator(A) else: # Use the preconditioner as recommended in # <http://fenicsproject.org/documentation/dolfin/dev/python/demo/pde/stokes-iterative/python/documentation.html>, # # prec = inner(grad(u), grad(v))*dx - p*q*dx # # although it doesn't seem to be too efficient. # The sign on the last term doesn't matter. prec = mu * inner(grad(u), grad(v))*dx \ - p*q*dx M, _ = assemble_system(prec, L, new_bcs) #solver = KrylovSolver('tfqmr', 'amg') solver = KrylovSolver('gmres', 'amg') solver.set_operators(A, M) solver.parameters['monitor_convergence'] = verbose solver.parameters['report'] = verbose solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['relative_tolerance'] = tol solver.parameters['maximum_iterations'] = 500 # Solve up = Function(WP) solver.solve(up.vector(), b) # Get sub-functions u, p = up.split() return u, p
def solve( mesh, W_element, P_element, Q_element, u0, p0, theta0, kappa, rho, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs, theta_neumann_bcs, dx_submesh, ds_submesh ): # First do a fixed_point iteration. This is usually quite robust and leads # to a point from where Newton can converge reliably. u0, p0, theta0 = solve_fixed_point( mesh, W_element, P_element, Q_element, theta0, kappa, rho, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs, theta_neumann_bcs, my_dx=dx_submesh, my_ds=ds_submesh, max_iter=100, tol=1.0e-8 ) WPQ = FunctionSpace( mesh, MixedElement([W_element, P_element, Q_element]) ) uptheta0 = Function(WPQ) # Initial guess assign(uptheta0.sub(0), u0) assign(uptheta0.sub(1), p0) assign(uptheta0.sub(2), theta0) rho_const = rho(_average(theta0)) stokes_heat_problem = StokesHeat( WPQ, kappa, rho, rho_const, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs=theta_dirichlet_bcs, theta_neumann_bcs=theta_neumann_bcs, my_dx=dx_submesh, my_ds=ds_submesh ) # solver = FixedPointSolver() from dolfin import PETScSNESSolver solver = PETScSNESSolver() # http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/SNES/SNESType.html solver.parameters['method'] = 'newtonls' # The Jacobian system for Stokes (+heat) are hard to solve. # Use LU for now. solver.parameters['linear_solver'] = 'lu' solver.parameters['maximum_iterations'] = 100 # TODO tighten tolerance. might not always work though... solver.parameters['absolute_tolerance'] = 1.0e-3 solver.parameters['relative_tolerance'] = 0.0 solver.parameters['report'] = True solver.solve(stokes_heat_problem, uptheta0.vector()) # u0, p0, theta0 = split(uptheta0) # Create a *deep* copy of u0, p0, theta0 to be able to deal with them # as actually separate entities vectors. u0, p0, theta0 = uptheta0.split(deepcopy=True) return u0, p0, theta0
class NavierStokesCylindrical(NonlinearProblem): def __init__(self, WP, rho, mu, f, u_bcs, p_bcs, dx, stabilization=None): super(NavierStokesCylindrical, self).__init__() # self.WP = WP # Translate the boundary conditions into the product space. self.bcs = [] for k, bcs in enumerate([u_bcs, p_bcs]): for bc in bcs: space = bc.function_space() C = space.component() if len(C) == 0: self.bcs.append(DirichletBC(self.WP.sub(k), bc.value(), bc.domain_args[0])) elif len(C) == 1: self.bcs.append(DirichletBC(self.WP.sub(k).sub(int(C[0])), bc.value(), bc.domain_args[0])) else: raise RuntimeError('Illegal number of ' 'subspace components.' ) # self.up = Function(self.WP) u, p = self.up.split() v, q = TestFunctions(self.WP) # Momentum equation. self.F0 = momentum_equation(u, v, p, f, rho, mu, stabilization=stabilization, dx=dx ) # div_u = 1/r * div(r*u) r = Expression('x[0]', degree=1, domain=WP.mesh()) self.F0 += (1.0 / r * (r * u[0]).dx(0) + u[1].dx(1)) * q \ * 2 * pi * r * dx self.jacobian = derivative(self.F0, self.up) self.reset_sparsity = True return def F(self, b, x): self.up.vector()[:] = x assemble(self.F0, tensor=b, form_compiler_parameters={'optimize': True} ) for bc in self.bcs: bc.apply(b, x) return def J(self, A, x): self.up.vector()[:] = x assemble(self.jacobian, tensor=A, reset_sparsity=self.reset_sparsity, form_compiler_parameters={'optimize': True} ) for bc in self.bcs: bc.apply(A) self.reset_sparsity = False return
def RunJob(Tb, mu_value, path): runtimeInit = clock() tfile = File(path + '/t6t.pvd') mufile = File(path + "/mu.pvd") ufile = File(path + '/velocity.pvd') gradpfile = File(path + '/gradp.pvd') pfile = File(path + '/pstar.pvd') parameters = open(path + '/parameters', 'w', 0) vmeltfile = File(path + '/vmelt.pvd') rhofile = File(path + '/rhosolid.pvd') for name in dir(): ev = str(eval(name)) if name[0] != '_' and ev[0] != '<': parameters.write(name + ' = ' + ev + '\n') temp_values = [27. + 273, Tb + 273, 1300. + 273, 1305. + 273] dTemp = temp_values[3] - temp_values[0] temp_values = [x / dTemp for x in temp_values] # non dimensionalising temp mu_a = mu_value # this was taken from the blankenbach paper, can change.. Ep = b / dTemp mu_bot = exp(-Ep * (temp_values[3] * dTemp - 1573) + cc) * mu_a Ra = rho_0 * alpha * g * dTemp * h**3 / (kappa_0 * mu_a) w0 = rho_0 * alpha * g * dTemp * h**2 / mu_a tau = h / w0 p0 = mu_a * w0 / h print(mu_a, mu_bot, Ra, w0, p0) vslipx = 1.6e-09 / w0 vslip = Constant((vslipx, 0.0)) # nondimensional noslip = Constant((0.0, 0.0)) dt = 3.E11 / tau tEnd = 3.E13 / tau # non-dimensionalising times class PeriodicBoundary(SubDomain): def inside(self, x, on_boundary): return left(x, on_boundary) def map(self, x, y): y[0] = x[0] - MeshWidth y[1] = x[1] pbc = PeriodicBoundary() class TempExp(Expression): def eval(self, value, x): if x[1] >= LAB(x): value[0] = temp_values[0] + (temp_values[1] - temp_values[0]) * (MeshHeight - x[1]) / (MeshHeight - LAB(x)) else: value[0] = temp_values[3] - (temp_values[3] - temp_values[2]) * (x[1]) / (LAB(x)) class FluidTemp(Expression): def eval(self, value, x): if value[0] < 1295: value[0] = 1295 mesh = RectangleMesh(Point(0.0, 0.0), Point(MeshWidth, MeshHeight), nx, ny) Svel = VectorFunctionSpace(mesh, 'CG', 2, constrained_domain=pbc) Spre = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Stemp = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Smu = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Sgradp = VectorFunctionSpace(mesh, 'CG', 2, constrained_domain=pbc) Srho = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) S0 = MixedFunctionSpace([Svel, Spre, Stemp]) u = Function(S0) v, p, T = split(u) v_t, p_t, T_t = TestFunctions(S0) T0 = interpolate(TempExp(), Stemp) muExp = Expression('exp(-Ep * (T_val * dTemp - 1573) + cc * x[2] / meshHeight)', Smu.ufl_element(), Ep=Ep, dTemp=dTemp, cc=cc, meshHeight=MeshHeight, T_val=T0) mu = interpolate(muExp, Smu) rhosolid = Function(Srho) deltarho = Function(Srho) v0 = Function(Svel) vmelt = Function(Svel) v_theta = (1. - theta)*v0 + theta*v T_theta = (1. - theta)*T + theta*T0 r_v = (inner(sym(grad(v_t)), 2.*mu*sym(grad(v))) \ - div(v_t)*p \ - T*v_t[1] )*dx r_p = p_t*div(v)*dx r_T = (T_t*((T - T0) \ + dt*inner(v_theta, grad(T_theta))) \ + (dt/Ra)*inner(grad(T_t), grad(T_theta)) )*dx # + k_s*(Tf-T_theta)*dt Tf = T0.interpolate(FluidTemp()) # Tf = T0.interpolate(Expression('value[0] >= 1295.0 ? value[0] : 1295.0')) # Tf.interpolate(Expression('value[0] >= 1295 ? value[0] : 1295')) # project(Expression('value[0] >= 1295 ? value[0] : 1295'), Tf) # Alex, a question for you: # can you see if there is a way to set Tf = T in regions where T >=1295 celsius # # 1295 celsius is my arbitrary choice for the LAB isotherm. In regions # where T < 1295 C, set Tf to be some constant for now, such as 1295 C. # Once we do this, then we can add in a term like that last line above where # it will only be non-zero when the solid temperature, T, is cooler than 1295 # can you do this? After this is done, we will then worry about a calculation # where we solve for Tf as a function of time in the regions cooler than 1295 C # Makes sense? If not, we can skype soon -- email me with questions # 3/19/16 r = r_v + r_p + r_T bcv0 = DirichletBC(S0.sub(0), noslip, top) bcv1 = DirichletBC(S0.sub(0), vslip, bottom) bcp0 = DirichletBC(S0.sub(1), Constant(0.0), bottom) bct0 = DirichletBC(S0.sub(2), Constant(temp_values[0]), top) bct1 = DirichletBC(S0.sub(2), Constant(temp_values[3]), bottom) bcs = [bcv0, bcv1, bcp0, bct0, bct1] t = 0 count = 0 while (t < tEnd): solve(r == 0, u, bcs) t += dt nV, nP, nT = u.split() gp = grad(nP) rhosolid = rho_0 * (1 - alpha * (nT * dTemp - 1573)) deltarho = rhosolid - rhomelt yvec = Constant((0.0, 1.0)) vmelt = nV * w0 - darcy * (gp * p0 / h - deltarho * yvec * g) if (count % 100 == 0): pfile << nP ufile << nV tfile << nT mufile << mu gradpfile << project(grad(nP), Sgradp) mufile << project(mu * mu_a, Smu) rhofile << project(rhosolid, Srho) vmeltfile << project(vmelt, Svel) count += 1 assign(T0, nT) assign(v0, nV) mu.interpolate(muExp) print('Case mu=%g, Tb=%g complete.' % (mu_a, Tb), ' Run time =', clock() - runtimeInit, 's')
def run_with_params(Tb, mu_value, k_s, path): run_time_init = clock() mesh = BoxMesh(Point(0.0, 0.0, 0.0), Point(mesh_width, mesh_width, mesh_height), nx, ny, nz) pbc = PeriodicBoundary() WE = VectorElement('CG', mesh.ufl_cell(), 2) SE = FiniteElement('CG', mesh.ufl_cell(), 1) WSSS = FunctionSpace(mesh, MixedElement(WE, SE, SE, SE), constrained_domain=pbc) # W = FunctionSpace(mesh, WE, constrained_domain=pbc) # S = FunctionSpace(mesh, SE, constrained_domain=pbc) W = WSSS.sub(0).collapse() S = WSSS.sub(1).collapse() temperature_vals = [27.0 + 273, Tb + 273, 1300.0 + 273, 1305.0 + 273] temp_prof = TemperatureProfile(temperature_vals, element=S.ufl_element()) mu_a = mu_value # this was taken from the Blankenbach paper, can change Ep = b / temp_prof.delta mu_bot = exp(-Ep * (temp_prof.bottom * temp_prof.delta - 1573.0) + cc) * mu_a # TODO: verify exponentiation Ra = rho_0 * alpha * g * temp_prof.delta * h ** 3 / (kappa_0 * mu_a) w0 = rho_0 * alpha * g * temp_prof.delta * h ** 2 / mu_a tau = h / w0 p0 = mu_a * w0 / h log(mu_a, mu_bot, Ra, w0, p0) slip_vx = 1.6E-09 / w0 # Non-dimensional slip_velocity = Constant((slip_vx, 0.0, 0.0)) zero_slip = Constant((0.0, 0.0, 0.0)) time_step = 3.0E11 / tau * 2 dt = Constant(time_step) t_end = 3.0E15 / tau / 5.0 # Non-dimensional times u = Function(WSSS) # Instead of TrialFunctions, we use split(u) for our non-linear problem v, p, T, Tf = split(u) v_t, p_t, T_t, Tf_t = TestFunctions(WSSS) T0 = interpolate(temp_prof, S) mu_exp = Expression('exp(-Ep * (T_val * dTemp - 1573.0) + cc * x[2] / mesh_height)', Ep=Ep, dTemp=temp_prof.delta, cc=cc, mesh_height=mesh_height, T_val=T0, element=S.ufl_element()) Tf0 = interpolate(temp_prof, S) mu = Function(S) v0 = Function(W) v_theta = (1.0 - theta) * v0 + theta * v T_theta = (1.0 - theta) * T0 + theta * T Tf_theta = (1.0 - theta) * Tf0 + theta * Tf # TODO: Verify forms r_v = (inner(sym(grad(v_t)), 2.0 * mu * sym(grad(v))) - div(v_t) * p - T * v_t[2]) * dx r_p = p_t * div(v) * dx heat_transfer = Constant(k_s) * (Tf_theta - T_theta) * dt r_T = (T_t * ((T - T0) + dt * inner(v_theta, grad(T_theta))) # TODO: Inner vs dot + (dt / Ra) * inner(grad(T_t), grad(T_theta)) - T_t * heat_transfer) * dx v_melt = Function(W) z_hat = Constant((0.0, 0.0, 1.0)) # TODO: inner -> dot, take out Tf_t r_Tf = (Tf_t * ((Tf - Tf0) + dt * inner(v_melt, grad(Tf_theta))) + Tf_t * heat_transfer) * dx r = r_v + r_p + r_T + r_Tf bcv0 = DirichletBC(WSSS.sub(0), zero_slip, top) bcv1 = DirichletBC(WSSS.sub(0), slip_velocity, bottom) bcv2 = DirichletBC(WSSS.sub(0).sub(1), Constant(0.0), back) bcv3 = DirichletBC(WSSS.sub(0).sub(1), Constant(0.0), front) bcp0 = DirichletBC(WSSS.sub(1), Constant(0.0), bottom) bct0 = DirichletBC(WSSS.sub(2), Constant(temp_prof.surface), top) bct1 = DirichletBC(WSSS.sub(2), Constant(temp_prof.bottom), bottom) bctf1 = DirichletBC(WSSS.sub(3), Constant(temp_prof.bottom), bottom) bcs = [bcv0, bcv1, bcv2, bcv3, bcp0, bct0, bct1, bctf1] t = 0 count = 0 files = DefaultDictByKey(partial(create_xdmf, path)) while t < t_end: mu.interpolate(mu_exp) rhosolid = rho_0 * (1.0 - alpha * (T0 * temp_prof.delta - 1573.0)) deltarho = rhosolid - rho_melt # TODO: project (accuracy) vs interpolate assign(v_melt, project(v0 - darcy * (grad(p) * p0 / h - deltarho * z_hat * g) / w0, W)) # TODO: Written out one step later? # v_melt.assign(v0 - darcy * (grad(p) * p0 / h - deltarho * yvec * g) / w0) # TODO: use nP after to avoid projection? solve(r == 0, u, bcs) nV, nP, nT, nTf = u.split() # TODO: write with Tf, ... etc if count % output_every == 0: time_left(count, t_end / time_step, run_time_init) # TODO: timestep vs dt # TODO: Make sure all writes are to the same function for each time step files['T_fluid'].write(nTf, t) files['p'].write(nP, t) files['v_solid'].write(nV, t) files['T_solid'].write(nT, t) files['mu'].write(mu, t) files['v_melt'].write(v_melt, t) files['gradp'].write(project(grad(nP), W), t) files['rho'].write(project(rhosolid, S), t) files['Tf_grad'].write(project(grad(Tf), W), t) files['advect'].write(project(dt * dot(v_melt, grad(nTf))), t) files['ht'].write(project(heat_transfer, S), t) assign(T0, nT) assign(v0, nV) assign(Tf0, nTf) t += time_step count += 1 log('Case mu={}, Tb={}, k={} complete. Run time = {:.2f} minutes'.format(mu_a, Tb, k_s, (clock() - run_time_init) / 60.0))
def adjoint(t, mu_expression, lmbda_expression, rho, Lx=10, Ly=10, t_end=1, omega_p=5, amplitude=5000, center=0): Lpml = Lx / 10 #c_p = cp(mu.vector(), lmbda.vector(), rho) max_velocity = 150 #c_p.max() stable_hx = stable_dx(max_velocity, omega_p) nx = int(Lx / stable_hx) + 1 nx = max(nx, 60) ny = int(Ly * nx / Lx) + 1 mesh = mesh_generator(Lx, Ly, Lpml, nx, ny) used_hx = Lx / nx dt = stable_dt(used_hx, max_velocity) cfl_ct = cfl_constant(max_velocity, dt, used_hx) PE = FunctionSpace(mesh, "DG", 0) mu = interpolate(mu_expression, PE) lmbda = interpolate(lmbda_expression, PE) m = 2 R = 10e-8 t = 0.0 gamma = 0.50 beta = 0.25 ff = MeshFunction("size_t", mesh, mesh.geometry().dim() - 1) Dirichlet(Lx, Ly, Lpml).mark(ff, 1) # Create function spaces VE = VectorElement("CG", mesh.ufl_cell(), 1, dim=2) TE = TensorElement("DG", mesh.ufl_cell(), 0, shape=(2, 2), symmetry=True) W = FunctionSpace(mesh, MixedElement([VE, TE])) F = FunctionSpace(mesh, "CG", 2) V = W.sub(0).collapse() M = W.sub(1).collapse() alpha_0 = Alpha_0(m, stable_hx, R, Lpml) alpha_1 = Alpha_1(alpha_0, Lx, Lpml, degree=2) alpha_2 = Alpha_2(alpha_0, Ly, Lpml, degree=2) beta_0 = Beta_0(m, max_velocity, R, Lpml) beta_1 = Beta_1(beta_0, Lx, Lpml, degree=2) beta_2 = Beta_2(beta_0, Ly, Lpml, degree=2) alpha_1 = interpolate(alpha_1, F) alpha_2 = interpolate(alpha_2, F) beta_1 = interpolate(beta_1, F) beta_2 = interpolate(beta_2, F) a_ = alpha_1 * alpha_2 b_ = alpha_1 * beta_2 + alpha_2 * beta_1 c_ = beta_1 * beta_2 Lambda_e = as_tensor([[alpha_2, 0], [0, alpha_1]]) Lambda_p = as_tensor([[beta_2, 0], [0, beta_1]]) a_ = alpha_1 * alpha_2 b_ = alpha_1 * beta_2 + alpha_2 * beta_1 c_ = beta_1 * beta_2 Lambda_e = as_tensor([[alpha_2, 0], [0, alpha_1]]) Lambda_p = as_tensor([[beta_2, 0], [0, beta_1]]) # Set up boundary condition bc = DirichletBC(W.sub(0), Constant(("0.0", "0.0")), ff, 1) # Create measure for the source term dx = Measure("dx", domain=mesh) ds = Measure("ds", domain=mesh, subdomain_data=ff) # Set up initial values u0 = Function(V) u0.set_allow_extrapolation(True) v0 = Function(V) a0 = Function(V) U0 = Function(M) V0 = Function(M) A0 = Function(M) u_obs = Function(V) u_sta = Function(V) load = Function(V) # Test and trial functions (theta_u, theta_S) = TrialFunctions(W) (w, T) = TestFunctions(W) g = ModifiedRickerPulse(0, omega_p, amplitude, center) F = rho * inner(a_ * N_ddot(theta_u, u0, a0, v0, dt, beta) - b_ * N_dot(theta_u, u0, v0, a0, dt, beta, gamma) + c_ * theta_u, w) * dx \ + inner(- N_dot(theta_S, U0, V0, A0, dt, beta, gamma) * Lambda_e + theta_S * Lambda_p, grad(w)) * dx \ - inner(load, w) * ds \ + inner(compliance(a_ * N_ddot(theta_S, U0, A0, V0, dt, beta) - b_ * N_dot(theta_S, U0, V0, A0, dt, beta, gamma) + c_ * theta_S, theta_u, mu, lmbda), T) * dx \ - inner(- Lambda_e * grad(N_dot(theta_u, u0, a0, v0, dt, beta)).T + Lambda_p * grad(theta_u).T, T) * dx a, L = lhs(F), rhs(F) # Assemble rhs (once) A = assemble(a) # Create GMRES Krylov solver solver = KrylovSolver(A, "gmres") # Create solution function S = Function(W) rec_counter = 0 tau = final_time - t while t > 0.0 + 0.5 * dt: t -= float(dt) tau += dt if rec_counter % 10 == 0: print( '\n\rtime: {:.3f} (Progress: {:.2f}%)'.format( t, 100 * t / t_end), ) g.t = t # Assemble rhs and apply boundary condition b = assemble(L) bc.apply(A, b) # Compute solution solver.solve(S.vector(), b) (u, U) = S.split(True) # Update previous time step update(u, u0, v0, a0, beta, gamma, dt) update(U, U0, V0, A0, beta, gamma, dt) if rec_counter % 2 == 0: xdmffile_u.write(u, t) energy = inner(u, u) * dx E = assemble(energy) print("E = ", E) print(u.vector().max())
def solve(WP, bcs, mu, f, verbose=True, tol=1.0e-13, max_iter=500): # Some initial sanity checks. assert mu > 0.0 # Define variational problem (u, p) = TrialFunctions(WP) (v, q) = TestFunctions(WP) # Build system. # The sign of the div(u)-term is somewhat arbitrary since the right-hand # side is 0 here. We can either make the system symmetric or positive- # definite. # On a second note, we have # # \int grad(p).v = - \int p * div(v) + \int_\Gamma p n.v. # # Since, we have either p=0 or n.v=0 on the boundary, we could as well # replace the term dot(grad(p), v) by -p*div(v). # a = mu * inner(grad(u), grad(v))*dx \ - p * div(v) * dx \ - q * div(u) * dx # a = mu * inner(grad(u), grad(v))*dx + dot(grad(p), v) * dx \ # - div(u) * q * dx L = inner(f, v) * dx A, b = assemble_system(a, L, bcs) # Use the preconditioner as recommended in # <http://fenicsproject.org/documentation/dolfin/dev/python/demo/pde/stokes-iterative/python/documentation.html>, # # prec = inner(grad(u), grad(v))*dx - p*q*dx # # although it doesn't seem to be too efficient. # The sign on the last term doesn't matter. prec = mu * inner(grad(u), grad(v))*dx \ - p*q*dx M, _ = assemble_system(prec, L, bcs) # solver = KrylovSolver('tfqmr', 'hypre_amg') solver = KrylovSolver('gmres', 'hypre_amg') solver.set_operators(A, M) # For an assortment of preconditioners, see # # Performance and analysis of saddle point preconditioners # for the discrete steady-state Navier-Stokes equations; # H.C. Elman, D.J. Silvester, A.J. Wathen; # Numer. Math. (2002) 90: 665-688; # <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>. # # Set up field split. # <https://fenicsproject.org/qa/12856/fieldsplit-petscpreconditioner_set_fieldsplit-arguments> # PETScOptions.set('ksp_view') # PETScOptions.set('ksp_monitor_true_residual') # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'additive') # PETScOptions.set('pc_fieldsplit_detect_saddle_point') # PETScOptions.set('fieldsplit_0_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_0_pc_type', 'lu') # PETScOptions.set('fieldsplit_1_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_1_pc_type', 'jacobi') # solver = PETScKrylovSolver('gmres') # solver.set_operator(A) # solver.set_from_options() # http://scicomp.stackexchange.com/questions/7288/which-preconditioners-and-solver-in-petsc-for-indefinite-symmetric-systems-sho # PETScOptions.set('pc_type', 'fieldsplit') # #PETScOptions.set('pc_fieldsplit_type', 'schur') # #PETScOptions.set('pc_fieldsplit_schur_fact_type', 'upper') # PETScOptions.set('pc_fieldsplit_detect_saddle_point') # #PETScOptions.set('fieldsplit_u_pc_type', 'lsc') # #PETScOptions.set('fieldsplit_u_ksp_type', 'preonly') # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('fieldsplit_u_pc_type', 'hypre') # PETScOptions.set('fieldsplit_u_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_p_pc_type', 'jacobi') # PETScOptions.set('fieldsplit_p_ksp_type', 'preonly') # # From PETSc/src/ksp/ksp/examples/tutorials/ex42-fsschur.opts: # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'SCHUR') # PETScOptions.set('pc_fieldsplit_schur_fact_type', 'UPPER') # PETScOptions.set('fieldsplit_p_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi') # From # # Composable Linear Solvers for Multiphysics; # J. Brown, M. Knepley, D.A. May, L.C. McInnes, B. Smith; # <http://www.computer.org/csdl/proceedings/ispdc/2012/4805/00/4805a055-abs.html>; # <http://www.mcs.anl.gov/uploads/cels/papers/P2017-0112.pdf>. # # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'schur') # PETScOptions.set('pc_fieldsplit_schur_factorization_type', 'upper') # # # PETScOptions.set('fieldsplit_u_ksp_type', 'cg') # PETScOptions.set('fieldsplit_u_ksp_rtol', 1.0e-6) # PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi') # PETScOptions.set('fieldsplit_u_sub_pc_type', 'cholesky') # # # PETScOptions.set('fieldsplit_p_ksp_type', 'fgmres') # PETScOptions.set('fieldsplit_p_ksp_constant_null_space') # PETScOptions.set('fieldsplit_p_pc_type', 'lsc') # # # PETScOptions.set('fieldsplit_p_lsc_ksp_type', 'cg') # PETScOptions.set('fieldsplit_p_lsc_ksp_rtol', 1.0e-2) # PETScOptions.set('fieldsplit_p_lsc_ksp_constant_null_space') # #PETScOptions.set('fieldsplit_p_lsc_ksp_converged_reason') # PETScOptions.set('fieldsplit_p_lsc_pc_type', 'bjacobi') # PETScOptions.set('fieldsplit_p_lsc_sub_pc_type', 'icc') solver.parameters['monitor_convergence'] = verbose solver.parameters['report'] = verbose solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['relative_tolerance'] = tol solver.parameters['maximum_iterations'] = max_iter solver.parameters['error_on_nonconvergence'] = True # Solve up = Function(WP) solver.solve(up.vector(), b) # Get sub-functions u, p = up.split(True) return u, p
class VelocityBDMProjection: def __init__( self, simulation, w, incompressibility_flux_type='central', D12=None, degree=None, use_bcs=True, use_nedelec=True, ): """ Implement equation 4a and 4b in "Two new techniques for generating exactly incompressible approximate velocities" by Bernardo Cocburn (2009) For each element K in the mesh: <u⋅n, φ> = <û⋅n, φ> ∀ ϕ ∈ P_{k}(F) for any face F ∈ ∂K (u, ϕ) = (w, ϕ) ∀ φ ∈ P_{k-2}(K)^2 (u, ϕ) = (w, ϕ) ∀ φ ∈ {ϕ ∈ P_{k}(K)^2 : ∇⋅ϕ = 0 in K, ϕ⋅n = 0 on ∂K} Here w is the input velocity function in DG2 space and û is the flux at each face. P_{x} is the space of polynomials of order k The flux type can be 'central' or 'upwind' """ self.simulation = simulation simulation.log.info(' Setting up velocity BDM projection') V = w[0].function_space() ue = V.ufl_element() gdim = w.ufl_shape[0] if degree is None: pdeg = ue.degree() Vout = V else: pdeg = degree Vout = FunctionSpace(V.mesh(), 'DG', degree) pg = (pdeg, gdim) assert ue.family() == 'Discontinuous Lagrange' assert incompressibility_flux_type in ('central', 'upwind') if use_nedelec and pdeg > 1: a, L, V = self._setup_projection_nedelec( w, incompressibility_flux_type, D12, use_bcs, pdeg, gdim) elif gdim == 2 and pdeg == 1: a, L, V = self._setup_dg1_projection_2D( w, incompressibility_flux_type, D12, use_bcs) elif gdim == 2 and pdeg == 2: a, L, V = self._setup_dg2_projection_2D( w, incompressibility_flux_type, D12, use_bcs) else: raise NotImplementedError('VelocityBDMProjection does not support ' 'degree %d and dimension %d' % pg) # Pre-factorize matrices and store for usage in projection self.local_solver = LocalSolver(a, L) self.local_solver.factorize() self.temp_function = Function(V) self.w = w # Create function assigners self.assigners = [] for i in range(gdim): self.assigners.append(dolfin.FunctionAssigner(Vout, V.sub(i))) def _setup_dg1_projection_2D(self, w, incompressibility_flux_type, D12, use_bcs): """ Implement the projection where the result is BDM embeded in a DG1 function """ sim = self.simulation k = 1 gdim = 2 mesh = w[0].function_space().mesh() V = VectorFunctionSpace(mesh, 'DG', k) W = FunctionSpace(mesh, 'DGT', k) n = FacetNormal(mesh) v1 = TestFunction(W) u = TrialFunction(V) # The same fluxes that are used in the incompressibility equation if incompressibility_flux_type == 'central': u_hat_dS = dolfin.avg(w) elif incompressibility_flux_type == 'upwind': w_nU = (dot(w, n) + abs(dot(w, n))) / 2.0 switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0) u_hat_dS = switch * w('+') + (1 - switch) * w('-') if D12 is not None: u_hat_dS += dolfin.Constant([D12, D12]) * dolfin.jump(w, n) # Equation 1 - flux through the sides a = L = 0 for R in '+-': a += dot(u(R), n(R)) * v1(R) * dS L += dot(u_hat_dS, n(R)) * v1(R) * dS # Eq. 1 cont. - flux through external boundaries a += dot(u, n) * v1 * ds if use_bcs: for d in range(gdim): dirichlet_bcs = sim.data['dirichlet_bcs']['u%d' % d] neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, []) robin_bcs = sim.data['robin_bcs'].get('u%d' % d, []) outlet_bcs = sim.data['outlet_bcs'] for dbc in dirichlet_bcs: u_bc = dbc.func() L += u_bc * n[d] * v1 * dbc.ds() for nbc in neumann_bcs + robin_bcs + outlet_bcs: if nbc.enforce_zero_flux: pass # L += 0 else: L += w[d] * n[d] * v1 * nbc.ds() for sbc in sim.data['slip_bcs'].get('u', []): pass # L += 0 else: L += dot(w, n) * v1 * ds # Equation 2 - internal shape : empty for DG1 # Equation 3 - BDM Phi : empty for DG1 return a, L, V def _setup_dg2_projection_2D(self, w, incompressibility_flux_type, D12, use_bcs): """ Implement the projection where the result is BDM embeded in a DG2 function """ sim = self.simulation k = 2 gdim = 2 mesh = w[0].function_space().mesh() V = VectorFunctionSpace(mesh, 'DG', k) n = FacetNormal(mesh) # The mixed function space of the projection test functions e1 = FiniteElement('DGT', mesh.ufl_cell(), k) e2 = VectorElement('DG', mesh.ufl_cell(), k - 2) e3 = FiniteElement('Bubble', mesh.ufl_cell(), 3) em = MixedElement([e1, e2, e3]) W = FunctionSpace(mesh, em) v1, v2, v3b = TestFunctions(W) u = TrialFunction(V) # The same fluxes that are used in the incompressibility equation if incompressibility_flux_type == 'central': u_hat_dS = dolfin.avg(w) elif incompressibility_flux_type == 'upwind': w_nU = (dot(w, n) + abs(dot(w, n))) / 2.0 switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0) u_hat_dS = switch * w('+') + (1 - switch) * w('-') if D12 is not None: u_hat_dS += dolfin.Constant([D12, D12]) * dolfin.jump(w, n) # Equation 1 - flux through the sides a = L = 0 for R in '+-': a += dot(u(R), n(R)) * v1(R) * dS L += dot(u_hat_dS, n(R)) * v1(R) * dS # Eq. 1 cont. - flux through external boundaries a += dot(u, n) * v1 * ds if use_bcs: for d in range(gdim): dirichlet_bcs = sim.data['dirichlet_bcs']['u%d' % d] neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, []) robin_bcs = sim.data['robin_bcs'].get('u%d' % d, []) outlet_bcs = sim.data['outlet_bcs'] for dbc in dirichlet_bcs: u_bc = dbc.func() L += u_bc * n[d] * v1 * dbc.ds() for nbc in neumann_bcs + robin_bcs + outlet_bcs: if nbc.enforce_zero_flux: pass # L += 0 else: L += w[d] * n[d] * v1 * nbc.ds() for sbc in sim.data['slip_bcs'].get('u', []): pass # L += 0 else: L += dot(w, n) * v1 * ds # Equation 2 - internal shape a += dot(u, v2) * dx L += dot(w, v2) * dx # Equation 3 - BDM Phi v3 = as_vector([v3b.dx(1), -v3b.dx(0)]) # Curl of [0, 0, v3b] a += dot(u, v3) * dx L += dot(w, v3) * dx return a, L, V def _setup_projection_nedelec(self, w, incompressibility_flux_type, D12, use_bcs, pdeg, gdim): """ Implement the BDM-like projection using Nedelec elements in the test function """ sim = self.simulation k = pdeg mesh = w[0].function_space().mesh() V = VectorFunctionSpace(mesh, 'DG', k) n = FacetNormal(mesh) # The mixed function space of the projection test functions e1 = FiniteElement('DGT', mesh.ufl_cell(), k) e2 = FiniteElement('N1curl', mesh.ufl_cell(), k - 1) em = MixedElement([e1, e2]) W = FunctionSpace(mesh, em) v1, v2 = TestFunctions(W) u = TrialFunction(V) # The same fluxes that are used in the incompressibility equation if incompressibility_flux_type == 'central': u_hat_dS = dolfin.avg(w) elif incompressibility_flux_type == 'upwind': w_nU = (dot(w, n) + abs(dot(w, n))) / 2.0 switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0) u_hat_dS = switch * w('+') + (1 - switch) * w('-') if D12 is not None: u_hat_dS += dolfin.Constant([D12] * gdim) * dolfin.jump(w, n) # Equation 1 - flux through the sides a = L = 0 for R in '+-': a += dot(u(R), n(R)) * v1(R) * dS L += dot(u_hat_dS, n(R)) * v1(R) * dS # Eq. 1 cont. - flux through external boundaries a += dot(u, n) * v1 * ds if use_bcs: for d in range(gdim): dirichlet_bcs = sim.data['dirichlet_bcs'].get('u%d' % d, []) neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, []) robin_bcs = sim.data['robin_bcs'].get('u%d' % d, []) outlet_bcs = sim.data['outlet_bcs'] for dbc in dirichlet_bcs: u_bc = dbc.func() L += u_bc * n[d] * v1 * dbc.ds() for nbc in neumann_bcs + robin_bcs + outlet_bcs: if nbc.enforce_zero_flux: pass # L += 0 else: L += w[d] * n[d] * v1 * nbc.ds() for sbc in sim.data['slip_bcs'].get('u', []): pass # L += 0 else: L += dot(w, n) * v1 * ds # Equation 2 - internal shape using 'Nedelec 1st kind H(curl)' elements a += dot(u, v2) * dx L += dot(w, v2) * dx return a, L, V def run(self, w=None): """ Perform the projection based on the current state of the Function w """ # Find the projected velocity self.local_solver.solve_local_rhs(self.temp_function) # Assign to w w = self.w if w is None else w U = self.temp_function.split() for i, a in enumerate(self.assigners): a.assign(w[i], U[i])
# Solve variational problem for time step solve(F == 0, u, solver_parameters={ "newton_solver": { "relative_tolerance": 1e-6 }, "newton_solver": { "maximum_iterations": 10 } }) print('solver done') # Save solution to files for visualization and data postprocessing _u_A, _u_B, _u_C, _u_T = u_n.split() Uvector = 3 * as_backend_type(u_n.vector()).get_local() _3u_n.vector().set_local(Uvector) _3u_A, _3u_B, _3u_C, _3u_T = _3u_n.split() _u_D = _3u_C if Data_postprocessing: fA_out.write_checkpoint(_u_A, "A", t, XDMFFile.Encoding.HDF5, True) fB_out.write_checkpoint(_u_B, "B", t, XDMFFile.Encoding.HDF5, True) fC_out.write_checkpoint(_u_C, "C", t, XDMFFile.Encoding.HDF5, True) fD_out.write_checkpoint(_u_D, "D", t, XDMFFile.Encoding.HDF5, True) fT_out.write_checkpoint(_u_T, "T", t, XDMFFile.Encoding.HDF5, True) Twall_n = Twall ufl_integral = assemble(_u_T * ds_wall)