def weighted_H1_norm(w, vec, piecewise=False): if piecewise: DG = FunctionSpace(vec.basis.mesh, "DG", 0) s = TestFunction(DG) ae = assemble(w * inner(nabla_grad(vec._fefunc), nabla_grad(vec._fefunc)) * s * dx) norm_vec = np.array([sqrt(e) for e in ae]) # map DG dofs to cell indices dofs = [DG.dofmap().cell_dofs(c.index())[0] for c in cells(vec.basis.mesh)] norm_vec = norm_vec[dofs] else: ae = assemble(w * inner(nabla_grad(vec._fefunc), nabla_grad(vec._fefunc)) * dx) norm_vec = sqrt(ae) return norm_vec
V = FunctionSpace(mesh, 'Lagrange', 2) # Vplot = FunctionSpace(mesh_plot, 'Lagrange', 1) Vplot = V Q = FunctionSpace(mesh, 'Lagrange', 1) # BC conditions, nullspace v_in_expr = Constant(v_in) plt = plot(interpolate(v_in_expr, V), range_min=0., range_max=2*v_in, window_width= width, window_height= height) plt.write_png('%s/correct' % dir) # v_in_expr = Expression('(t<1.0)?t*v:v', v=Constant(v_in), t=0.0) # v_in_expr = Expression('(t<1.0)?(1-cos(pi*t))*v*0.5:v', v=Constant(v_in), t=0.0) bcp = DirichletBC(Q, Constant(0.0), boundary_parts, 2) bcu = DirichletBC(V, v_in_expr, boundary_parts, 1) foo = Function(Q) null_vec = Vector(foo.vector()) Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0/null_vec.norm('l2') # print(null_vec.array()) null_space = VectorSpaceBasis([null_vec]) ds = Measure("ds", subdomain_data=boundary_parts) # Define forms (dont redefine functions used here) # step 1 u0 = Function(V) u1 = Function(V) p0 = Function(Q) u_tent = TrialFunction(V) v = TestFunction(V) # U_ = 1.5*u0 - 0.5*u1 # nonlinearity = inner(dot(0.5 * (u_tent.dx(0) + u0.dx(0)), U_), v) * dx
def _evaluateLocalEstimator(cls, mu, w, coeff_field, pde, f, quadrature_degree, epsilon=1e-5): """Evaluation of patch local equilibrated estimator.""" # 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() mesh.init() degree = element_degree(w[mu]._fefunc) # data for nodal bases V_dm = V.dofmap() V_dofs = dict([(i, V_dm.cell_dofs(i)) for i in range(mesh.num_cells())]) V1 = FunctionSpace(mesh, 'CG', 1) # V1 is to define nodal base functions phi_z = Function(V1) phi_coeffs = np.ndarray(V1.dim()) vertex_dof_map = V1.dofmap().vertex_to_dof_map(mesh) # vertex_dof_map = vertex_to_dof_map(V1) dof_list = vertex_dof_map.tolist() # DG0 localisation DG0 = FunctionSpace(mesh, 'DG', 0) DG0_dofs = dict([(c.index(),DG0.dofmap().cell_dofs(c.index())[0]) for c in cells(mesh)]) dg0 = TestFunction(DG0) # characteristic function of patch xi_z = Function(DG0) xi_coeffs = np.ndarray(DG0.dim()) # mesh data h = CellSize(mesh) n = FacetNormal(mesh) cf = CellFunction('size_t', mesh) # setup error estimator vector eq_est = np.zeros(DG0.dim()) # setup global equilibrated flux vector DG = VectorFunctionSpace(mesh, "DG", degree) DG_dofmap = DG.dofmap() # define form functions tau = TrialFunction(DG) v = TestFunction(DG) # define global tau tau_global = Function(DG) tau_global.vector()[:] = 0.0 # iterate vertices for vertex in vertices(mesh): # get patch cell indices vid = vertex.index() patch_cid, FF_inner, FF_boundary = get_vertex_patch(vid, mesh, layers=1) # set nodal base function phi_coeffs[:] = 0 phi_coeffs[dof_list.index(vid)] = 1 phi_z.vector()[:] = phi_coeffs # set characteristic function and mark patch cf.set_all(0) xi_coeffs[:] = 0 for cid in patch_cid: xi_coeffs[DG0_dofs[int(cid)]] = 1 cf[int(cid)] = 1 xi_z.vector()[:] = xi_coeffs # determine local dofs lDG_cell_dofs = dict([(cid, DG_dofmap.cell_dofs(cid)) for cid in patch_cid]) lDG_dofs = [cd.tolist() for cd in lDG_cell_dofs.values()] lDG_dofs = list(iter.chain(*lDG_dofs)) # print "\nlocal DG subspace has dimension", len(lDG_dofs), "degree", degree, "cells", len(patch_cid), patch_cid # print "local DG_cell_dofs", lDG_cell_dofs # print "local DG_dofs", lDG_dofs # create patch measures dx = Measure('dx')[cf] dS = Measure('dS')[FF_inner] # define forms alpha = Constant(1 / epsilon) / h a = inner(tau,v) * phi_z * dx(1) + alpha * div(tau) * div(v) * dx(1) + avg(alpha) * jump(tau,n) * jump(v,n) * dS(1)\ + avg(alpha) * jump(xi_z * tau,n) * jump(v,n) * dS(2) L = -alpha * (div(sigma_mu) + f) * div(v) * phi_z * dx(1)\ - avg(alpha) * jump(sigma_mu,n) * jump(v,n) * avg(phi_z)*dS(1) # print "L2 f + div(sigma)", assemble((f + div(sigma)) * (f + div(sigma)) * dx(0)) # assemble forms lhs = assemble(a, form_compiler_parameters={'quadrature_degree': quadrature_degree}) rhs = assemble(L, form_compiler_parameters={'quadrature_degree': quadrature_degree}) # convert DOLFIN representation to scipy sparse arrays rows, cols, values = lhs.data() lhsA = sps.csr_matrix((values, cols, rows)).tocoo() # slice sparse matrix and solve linear problem lhsA = coo_submatrix_pull(lhsA, lDG_dofs, lDG_dofs) lx = spsolve(lhsA, rhs.array()[lDG_dofs]) # print ">>> local solution lx", type(lx), lx local_tau = Function(DG) local_tau.vector()[lDG_dofs] = lx # print "div(tau)", assemble(inner(div(local_tau),div(local_tau))*dx(1)) # add up local fluxes tau_global.vector()[lDG_dofs] += lx # evaluate estimator # maybe TODO: re-define measure dx eq_est = assemble( inner(tau_global, tau_global) * dg0 * (dx(0)+dx(1)),\ form_compiler_parameters={'quadrature_degree': quadrature_degree}) # reorder according to cell ids eq_est = eq_est[DG0_dofs.values()].array() global_est = np.sqrt(np.sum(eq_est)) # eq_est_global = assemble( inner(tau_global, tau_global) * (dx(0)+dx(1)), form_compiler_parameters={'quadrature_degree': quadrature_degree} ) # global_est2 = np.sqrt(np.sum(eq_est_global)) return global_est, FlatVector(np.sqrt(eq_est))#, tau_global
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 _evaluateResidualEstimator(cls, mu, w, coeff_field, pde, f, quadrature_degree): """Evaluate the residual error according to EGSZ (5.7) which consists of volume terms (5.3) and jump terms (5.5). .. math:: \eta_{\mu,T}(w_N) &:= h_T || \overline{a}^{-1/2} (f\delta_{\mu,0} + \nabla\overline{a}\cdot\nabla w_{N,\mu} + \sum_{m=1}^\infty \nabla a_m\cdot\nabla( \alpha^m_{\mu_m+1}\Pi_\mu^{\mu+e_m} w_{N,\mu+e_m} - \alpha_{\mu_m}^m w_{N,\mu} + \alpha_{\mu_m-1}^m\Pi_\mu^{\mu_m-e_m} w_{N,\mu-e_m} ||_{L^2(T)}\\ \eta_{\mu,S}(w_N) &:= h_S^{-1/2} || \overline{a}^{-1/2} [(\overline{a}\nabla w_{N,\mu} + \sum_{m=1}^\infty a_m\nabla ( \alpha_{\mu_m+1}^m\Pi_\mu^{\mu+e_m} w_{N,\mu+e_m} - \alpha_{\mu_m}^m w_{N,\mu} + \alpha_{\mu_m-1}^m\Pi_\mu^{\mu-e_m} w_{N,\mu-e_m})\cdot\nu] ||_{L^2(S)}\\ """ # 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)) # get pde residual terms r_T = pde.volume_residual r_E = pde.edge_residual r_Nb = pde.neumann_residual # get mean field of coefficient a0_f = coeff_field.mean_func # prepare some FEM variables V = w[mu]._fefunc.function_space() mesh = V.mesh() nu = FacetNormal(mesh) # initialise volume and edge residual with deterministic part # R_T = dot(nabla_grad(a0_f), nabla_grad(w[mu]._fefunc)) R_T = r_T(a0_f, w[mu]._fefunc) if not mu: R_T = R_T + f # R_E = a0_f * dot(nabla_grad(w[mu]._fefunc), nu) R_E = r_E(a0_f, w[mu]._fefunc, nu) # get Neumann residual homogeneousNBC = False if mu.order == 0 else True R_Nb = r_Nb(a0_f, w[mu]._fefunc, nu, mesh, homogeneous=homogeneousNBC) # iterate m Lambda = w.active_indices() maxm = w.max_order if len(coeff_field) < maxm: logger.warning("insufficient length of coefficient field for MultiVector (%i < %i)", len(coeff_field), maxm) maxm = len(coeff_field) # assert coeff_field.length >= maxm # ensure coeff_field expansion is sufficiently long for m in range(maxm): am_f, am_rv = coeff_field[m] # prepare polynom coefficients beta = am_rv.orth_polys.get_beta(mu[m]) # mu res = -beta[0] * w[mu] # mu+1 mu1 = mu.inc(m) if mu1 in Lambda: w_mu1 = w.get_projection(mu1, mu) res += beta[1] * w_mu1 # mu-1 mu2 = mu.dec(m) if mu2 in Lambda: w_mu2 = w.get_projection(mu2, mu) res += beta[-1] * w_mu2 # add volume contribution for m # r_t = dot(nabla_grad(am_f), nabla_grad(res._fefunc)) R_T = R_T + r_T(am_f, res._fefunc) # add edge contribution for m # r_e = am_f * dot(nabla_grad(res._fefunc), nu) R_E = R_E + r_E(am_f, res._fefunc, nu) # prepare more FEM variables for residual assembly DG = FunctionSpace(mesh, "DG", 0) s = TestFunction(DG) h = CellSize(mesh) # scaling of residual terms and definition of residual form a0_s = a0_f[0] if isinstance(a0_f, tuple) else a0_f # required for elasticity parameters res_form = (h ** 2 * (1 / a0_s) * dot(R_T, R_T) * s * dx + avg(h) * dot(avg(R_E) / avg(a0_s), avg(R_E)) * 2 * avg(s) * dS) resT = h ** 2 * (1 / a0_s) * dot(R_T, R_T) * s * dx resE = 0 * s * dx + avg(h) * dot(avg(R_E) / avg(a0_s), avg(R_E)) * 2 * avg(s) * dS resNb = 0 * s * dx # add Neumann residuals if R_Nb is not None: for rj, dsj in R_Nb: res_form = res_form + h * (1 / a0_s) * dot(rj, rj) * s * dsj resNb += h * (1 / a0_s) * dot(rj, rj) * s * dsj # FEM evaluate residual on mesh eta = assemble(res_form) eta_indicator = np.array([sqrt(e) for e in eta]) # map DG dofs to cell indices dofs = [DG.dofmap().cell_dofs(c.index())[0] for c in cells(mesh)] eta_indicator = eta_indicator[dofs] global_error = sqrt(sum(e for e in eta)) # debug --- if False: etaT = assemble(resT) etaT_indicator = etaT #np.array([sqrt(e) for e in etaT]) etaT = sqrt(sum(e for e in etaT)) etaE = assemble(resE) etaE_indicator = etaE #np.array([sqrt(e) for e in etaE]) etaE = sqrt(sum(e for e in etaE)) etaNb = assemble(resNb) etaNb_indicator = etaNb #np.array([sqrt(e) for e in etaNb]) etaNb = sqrt(sum(e for e in etaNb)) print "==========RESIDUAL ESTIMATOR============" print "eta", eta print "eta_indicator", eta_indicator print "global =", global_error print "volume =", etaT print "edge =", etaE print "Neumann =", etaNb if False: plot_indicators(((eta, "overall residual"), (etaT_indicator, "volume residual"), (etaE_indicator, "edge residual"), (etaNb_indicator, "Neumann residual")), mesh) # ---debug # restore quadrature degree parameters["form_compiler"]["quadrature_degree"] = quadrature_degree_old return (FlatVector(eta_indicator), global_error)
class Solver(gs.GeneralSolver): def __init__(self, args, tc, metadata): gs.GeneralSolver.__init__(self, args, tc, metadata) self.metadata['hasTentativeV'] = True self.solver_vel_tent = None self.solver_vel_cor = None self.solver_p = None self.solver_rot = None self.null_space = None # input parameters self.bc = args.bc self.forceOutflow = args.fo self.useLaplace = args.laplace self.use_full_SUPG = args.cs self.bcv = 'NOT' if self.useLaplace else args.bcv if self.bcv == 'CDN': info('Using classical do nothing condition (Tn=0).') if self.bcv == 'DDN': info('Using directional do nothing condition (Tn=0.5*negative(u.n)u).') if self.bcv == 'LAP': info('Using laplace neutral condition (grad(u)n=0).') self.stabCoef = args.stab self.stabilize = (args.stab > DOLFIN_EPS) if self.stabilize: if self.use_full_SUPG: info('Used consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info('Used non-consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info('No stabilization used.') self.solvers = args.solvers self.useRotationScheme = args.r self.metadata['hasTentativeP'] = self.useRotationScheme self.B = args.B self.use_ema = args.ema self.cbcDelta = args.cbcDelta self.prec_v = args.precV self.prec_p = args.precP self.precision_rel_v_tent = args.prv1 self.precision_abs_v_tent = args.pav1 self.precision_p = args.pp def __str__(self): return 'ipcs1 - incremental pressure correction scheme with nonlinearity treated by Adam-Bashword + ' \ 'Crank-Nicolson and viscosity term treated semi-explicitly (Crank-Nicholson)' @staticmethod def setup_parser_options(parser): gs.GeneralSolver.setup_parser_options(parser) parser.add_argument('-s', '--solvers', help='Solvers', choices=['direct', 'krylov'], default='krylov') parser.add_argument('--prv1', help='relative tentative velocity Krylov solver precision', type=int, default=6) parser.add_argument('--pav1', help='absolute tentative velocity Krylov solver precision', type=int, default=10) parser.add_argument('--pp', help='pressure Krylov solver precision', type=int, default=10) parser.add_argument('-b', '--bc', help='Pressure boundary condition mode', choices=['outflow', 'nullspace', 'nullspace_s', 'lagrange'], default='outflow') parser.add_argument('--precV', help='Preconditioner for tentative velocity solver', type=str, default='ilu') parser.add_argument('--precP', help='Preconditioner for pressure solver', choices=['hypre_amg', 'ilu'], default='hypre_amg') parser.add_argument('-r', help='Use rotation scheme', action='store_true') parser.add_argument('-B', help='Use no BC in correction step', action='store_true') parser.add_argument('--fo', help='Force Neumann outflow boundary for pressure', action='store_true') parser.add_argument('--laplace', help='Use laplace(u) instead of div(symgrad(u))', action='store_true') parser.add_argument('--stab', help='Use stabilization (positive constant)', type=float, default=0.) parser.add_argument('--bcv', help='Oufflow BC for velocity (with stress formulation)', choices=['CDN', 'DDN', 'LAP'], default='CDN') parser.add_argument('--ema', help='Use EMA conserving scheme for convection term', action='store_true') # described in Charnyi, Heister, Olshanskii, Rebholz: # "On conservation laws of Navier-Stokes Galerkin discretizations" (2016) parser.add_argument('--cs', help='Use consistent SUPG stabilisation.', action='store_true') parser.add_argument('--cbcDelta', help='Use simpler cbcflow parameter for SUPG', action='store_true') 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
class Solver(object): """ First order FEM with CR elements. Nonconforming CR elements give lower bounds for eigenvalues after apropriate postprocessing is applied. """ def __init__(self, mesh, num, denom, method="CR"): """ Assemble matrices and cache matrices. """ self.V = FunctionSpace(mesh, method, 1) # save coordinates of DOFs self.dofs = np.reshape(self.V.dofmap().tabulate_all_coordinates(mesh), (-1, 2)) u = TrialFunction(self.V) v = TestFunction(self.V) self.boundary = MeshFunction("size_t", mesh, 1) # assemble matrices a = inner(grad(u), grad(v)) * dx b = u * v * dx self.A = uBLASSparseMatrix() self.A = assemble(a, tensor=self.A) self.A.compress() self.B = uBLASSparseMatrix() self.B = assemble(b, tensor=self.B) self.B.compress() size = mesh.size(2) # cell diameter calculation self.H2 = (num ** 2 + denom ** 2) * 1.0 / size # print "Theoretical cell diameter: ", sqrt(self.H2) # print "FEniCS calculated: ", mesh.hmax() # Matrices have rational entries. We can rescale to get integers # and save as scipy matrices scaleB = 6.0*size/num/denom self.scaleB = scaleB self.B *= scaleB r, c, val = self.B.data() val = np.round(val) self.B = sps.csr_matrix((val, c, r)) # check if B is diagonal assert len(self.B.diagonal()) == self.B.nnz # find B inverse self.Binv = sps.csr_matrix((1.0/val, c, r)) scaleA = 1.0*num*denom/2 self.scaleA = scaleA self.A *= scaleA r, c, val = self.A.data() self.A = sps.csr_matrix((np.round(val), c, r)) self.scale = scaleA/scaleB print 'scaling A by: ', scaleA print 'scaling B by: ', scaleB print 'eigenvalues scale by:', self.scale def solve(self, dirichlet=lambda x: False, plotted=None): """ Find eigenvalues given a boundary condition function. dirichlet is a function returning True if x is on Dirichlet BC. """ self.boundary.set_all(0) d = Dirichlet() d.init(dirichlet) d.mark(self.boundary, 1) if plotted is not None: plotted.plot(self.boundary) # plotted.write_png() # indices for non-Dirichlet rows/columns indices = np.nonzero(np.apply_along_axis( lambda x: not dirichlet(x), 1, self.dofs))[0] # remove Dirichlet rows and columns from A self.AA = (self.A[indices, :]).tocsc()[:, indices] # remove Dirichlet rows and columns from B self.BB = (self.B[indices, :]).tocsc()[:, indices] self.BBinv = (self.Binv[indices, :]).tocsc()[:, indices] # solve using scipy eigs, eigfs = ssl.eigsh(self.AA, k=2, M=self.BB, sigma=0, which='LM') self.raweigs = [eigs[0], eigs[1]] eig = eigs[0] # turn eigf into a function eigf = np.array(eigfs[:, 0]).flatten() # we were solving only for some dofs, rest is 0 # u = Function(self.V) # u.vector()[:] = 0 # u.vector()[indices] = eigf # find algebraic residual # both L2 norm of u and B norm of eigf should equal 1 # print assemble(u*u*dx), self.BB.dot(eigf).dot(eigf) res = self.AA.dot(eigf) - eig * self.BB.dot(eigf) resnorm = np.sqrt(self.BBinv.dot(res).dot(res)) self.residual = [resnorm, 0] # apply Carstensen-Gedicke transformations to eig # # kappa^2 less than 0.1932 eig = (eig - resnorm) / (1 + 0.1932 * (eig - resnorm) * self.H2 / self.scale) # scale back the eigenvalue eig = eig/self.scale # find residual for the second eigenvalue (for gap calculations) eigf = np.array(eigfs[:, 1]).flatten() res = self.AA.dot(eigf) - eigs[1] * self.BB.dot(eigf) resnorm = np.sqrt(self.BBinv.dot(res).dot(res)) self.residual[1] = resnorm # return (eig, u) # pair (eigenvalue,eigenfunctions) return (eig, None) # pair (eigenvalue,eigenfunctions)
def test_block_size_real(mesh): mesh = UnitIntervalMesh(12) V = FiniteElement('DG', mesh.ufl_cell(), 0) R = FiniteElement('R', mesh.ufl_cell(), 0) X = FunctionSpace(mesh, V * R) assert X.dofmap().index_map.block_size == 1
def test_entity_dofs(mesh): """Test that num entity dofs is correctly wrapped to dolfin::DofMap""" V = FunctionSpace(mesh, ("CG", 1)) assert V.dofmap().num_entity_dofs(0) == 1 assert V.dofmap().num_entity_dofs(1) == 0 assert V.dofmap().num_entity_dofs(2) == 0 V = VectorFunctionSpace(mesh, ("CG", 1)) assert V.dofmap().num_entity_dofs(0) == 2 assert V.dofmap().num_entity_dofs(1) == 0 assert V.dofmap().num_entity_dofs(2) == 0 V = FunctionSpace(mesh, ("CG", 2)) assert V.dofmap().num_entity_dofs(0) == 1 assert V.dofmap().num_entity_dofs(1) == 1 assert V.dofmap().num_entity_dofs(2) == 0 V = FunctionSpace(mesh, ("CG", 3)) assert V.dofmap().num_entity_dofs(0) == 1 assert V.dofmap().num_entity_dofs(1) == 2 assert V.dofmap().num_entity_dofs(2) == 1 V = FunctionSpace(mesh, ("DG", 0)) assert V.dofmap().num_entity_dofs(0) == 0 assert V.dofmap().num_entity_dofs(1) == 0 assert V.dofmap().num_entity_dofs(2) == 1 V = FunctionSpace(mesh, ("DG", 1)) assert V.dofmap().num_entity_dofs(0) == 0 assert V.dofmap().num_entity_dofs(1) == 0 assert V.dofmap().num_entity_dofs(2) == 3 V = VectorFunctionSpace(mesh, ("CG", 1)) # Note this numbering is dependent on FFC and can change This test # is here just to check that we get correct numbers mapped from ufc # generated code to dolfin for i, cdofs in enumerate([[0, 3], [1, 4], [2, 5]]): dofs = V.dofmap().tabulate_entity_dofs(0, i) assert all(d == cd for d, cd in zip(dofs, cdofs))
def visualize_form(A, b, c): ''' Study the quadratic form phi(x) = 0.5*x*A*x - b*x + c - definiteness - extrema - constraints ''' # Iput check assert isinstance(A, np.ndarray) assert A.shape == (2, 2) assert isinstance(b, np.ndarray) assert b.shape == (2, ) assert isinstance(c, (float, int)) # Decide symmetry is_sym = la.norm(A - A.T) < _TOL_ is_skew = la.norm(A + A.T) < _TOL_ print 'A is symmetric', is_sym, 'A is skew', is_skew # Decide definitness from eigenvalues vals, vecs = la.eig(A) vecs = vecs.T # Eigenvectors are now row, easies for iter access print 'A eigenvalues', vals print 'A eigenvectors', vecs[0], vecs[1] is_singular, is_posdef, is_negdef, is_indef = False, False, False, False # Don't know about definiteness of forms from A with complex eigenvalues if not vals.dtype == 'complex128': if np.any(np.abs(vals) < _TOL_): is_singular = True else: if np.all(vals > 0): is_posdef = True elif np.all(vals < 0): is_negdef = True elif abs(np.sum(np.sign(vals))) < _TOL_: is_indef = True print 'A is singular', is_singular,\ 'A is posdef', is_posdef,\ 'A is negdef', is_negdef,\ 'A is indef', is_indef # lambda for computing the quadratic form value, and the gradient phi = lambda x: 0.5*x.dot(A.dot(x)) - b.dot(x) + c grad_phi = lambda x: A.dot(x) - b # Width for grids w = 3 # If the system is not singular we can compute Ax=b (straight forward) # and use x to decide visualiz. grid width if not is_singular: if is_sym: x = la.solve(A, b) elif not is_skew: # If the system is not symmetric then the minima is sought # via the `symmetrized` system x = la.solve(0.5*(A + A.T), b) else: # For the skew system the above gives zero matrix and the gradient # is just b -- the quadratic surface is just a plane perpend to b x = b else: # For the singular system only constrained minimization makes sense # The functional is modified to include costraint that x is # perpendicular to nullspace vector # Get eigenvalues and eigenvectors of A # Find the eigenvector corresponding to 0. Is in the nullspace z = None for vec in vecs: if abs(vec.dot(A.dot(vec.T))) < _TOL_: z = vec # Build the system that is yields extrema of # phi(x) + constraint of orthogonality to z AA = np.zeros((3, 3)) AA[:2, :2] = A AA[2, :2] = z AA[:2, 2] = z.T bb = np.zeros(3) bb[:2] = b xx = la.solve(AA, bb) # Extract x and the multiplier x = xx[:2] lmbda = xx[-1] print 'Multiplier', lmbda # lambda for computing surface whose contours are lines that satisfy # the constraint psi = lambda x: x.dot(z) # These are poits on the line that satisfies the constraint and the # solution passed through it cs = lambda s: x + np.array([z[1], -z[0]])*s # Perperndicular line to cs cs_perp = lambda s: x + z*s # Chi is the constrained functional whose value we want to plot chi = lambda x: phi(x[:2]) + x[-1]*psi(x[:2]) mesh = BoxMesh(x[0]-w, x[1]-w, lmbda-w, x[0]+w, x[1]+w, lmbda+w, 10, 10, 10) V = FunctionSpace(mesh, 'CG', 1) dofs_x = V.dofmap().tabulate_all_coordinates(mesh).reshape((-1, 3)) values = np.array([chi(dof_x) for dof_x in dofs_x]) # The plot should show that the solution has chosen a plane at certain # height = lagrange multiplier, that has normal = z chi_f = Function(V) chi_f.vector().set_local(values) chi_f.vector().apply('') plot(chi_f, interactive=True) print 'Saved constrained functional to chi.pvd' File('chi.pvd') << chi_f # Generate data for plot showing the quadratic function values over # cs line s_array = np.linspace(-2, 2, 1000) PHI_cs = np.zeros_like(s_array) for i, s in enumerate(s_array): P = cs(s) PHI_cs[i] = phi(P) # Add three contour plot x, y @ a* X, Y = np.meshgrid(np.linspace(x[0]-w, x[0]+w, 100), np.linspace(x[1]-w, x[1]+w, 100)) PHI_a = np.zeros_like(X) for i in range(PHI_a.shape[0]): for j in range(PHI_a.shape[1]): P = np.array([X[i, j], Y[i, j], lmbda]) PHI_a[i, j] = chi(P) plt.figure() plt.suptitle('lambda constant') plt.pcolor(X, Y, PHI_a) plt.contour(X, Y, PHI_a, 10, colors='k') plt.plot(x[0], x[1], 'rx') # # x, a @ y* eval chi X, LMBDA = np.meshgrid(np.linspace(x[0]-w, x[0]+w, 100), np.linspace(lmbda-w, lmbda+w, 100)) PHI_y = np.zeros_like(X) for i in range(PHI_y.shape[0]): for j in range(PHI_y.shape[1]): P = np.array([X[i, j], x[1], LMBDA[i, j]]) PHI_y[i, j] = chi(P) plt.figure() plt.suptitle('x[1] constant') plt.pcolor(X, LMBDA, PHI_y) plt.contour(X, LMBDA, PHI_y, 10, colors='k') plt.plot(x[0], lmbda, 'rx') # y, a @ x* Y, LMBDA = np.meshgrid(np.linspace(x[1]-w, x[1]+w, 100), np.linspace(lmbda-w, lmbda+w, 100)) PHI_x = np.zeros_like(Y) for i in range(PHI_x.shape[0]): for j in range(PHI_x.shape[1]): P = np.array([x[0], Y[i, j], LMBDA[i, j]]) PHI_x[i, j] = chi(P) plt.figure() plt.suptitle('x[0] constant') plt.pcolor(Y, LMBDA, PHI_x) plt.contour(Y, LMBDA, PHI_x, 10, colors='k') plt.plot(x[1], lmbda, 'rx') # Slice through selected nullspace line and perpr to it s = np.linspace(-2, 2, 100) S, T = np.meshgrid(s, s) Z = np.zeros_like(S) Z_perp = np.zeros_like(S) for i in range(len(s)): for j in range(len(s)): P = np.r_[cs(s[j]), lmbda+s[i]] # print P, psi(cs(s[j])), phi(cs(s[j])), chi(cs(s[j])) # On the line phi is chi! Z[i, j] = chi(P) P = np.r_[cs_perp(s[j]), lmbda+s[i]] Z_perp[i, j] = chi(P) plt.figure() plt.suptitle('Nullspace slice') pcolor = plt.pcolor(S, T, Z) plt.colorbar(pcolor) plt.contour(S, T, Z, 10, colors='k') plt.figure() plt.suptitle('Nullspace perp. slice') plt.pcolor(S, T, Z_perp) plt.contour(S, T, Z_perp, 10, colors='k') print 'Potential extremum', x X, Y = np.meshgrid(np.linspace(x[0]-w, x[0]+w, 100), np.linspace(x[1]-w, x[1]+w, 100)) PHI = np.zeros_like(X) GRAD_PHI = np.zeros((X.shape[0], X.shape[1], 2)) # Generate values of qudratic function and its gradient for i in range(PHI.shape[0]): for j in range(PHI.shape[1]): P = np.array([X[i, j], Y[i, j]]) PHI[i, j] = phi(P) GRAD_PHI[i, j, :] = grad_phi(P) # Plot the quadratic form as 3d + the extreme point fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.plot_surface(X, Y, PHI) ax.plot([x[0]], [x[1]], [phi(x)], 'rx') # ------------------------------------------------------------------------ # Plot contours of the quadratic function plt.figure() plt.pcolor(X, Y, PHI) plt.contour(X, Y, PHI, 10, colors='k') plt.plot(x[0], x[1], 'rx') # Plot eigenvectors e0 = x + vecs[0] plt.plot(np.linspace(x[0], e0[0], 10), np.linspace(x[1], e0[1], 10), 'k') plt.text(0.5*x[0] + 0.5*e0[0], 0.5*x[1] + 0.5*e0[1], str(vals[0])) e1 = x + vecs[1] plt.plot(np.linspace(x[0], e1[0], 10), np.linspace(x[1], e1[1], 10), 'k') plt.text(0.5*x[0] + 0.5*e1[0], 0.5*x[1] + 0.5*e1[1], str(vals[1])) # For singular system add 'constraint lines' if is_singular: PSI = np.zeros_like(X) for i in range(PSI.shape[0]): for j in range(PSI.shape[1]): P = np.array([X[i, j], Y[i, j]]) PSI[i, j] = psi(P) plt.contour(X, Y, PSI, 10, colors='g') # ------------------------------------------------------------------------ # Finally plot curves tangent to gradient (better for visualiz. than # quiver) plt.figure() plt.streamplot(X, Y, GRAD_PHI[:, :, 0], GRAD_PHI[:, :, 1]) plt.plot(x[0], x[1], 'rx') if is_singular: plt.contour(X, Y, PSI, 10, colors='g') # ------------------------------------------------------------------------ # Add the plot of phi(cs) to check that found point is the extremum if is_singular: plt.figure() plt.plot(s_array, PHI_cs, 'b') plt.plot([0], [phi(cs(0))], 'rx') plt.figure() plt.plot(s_array, np.array([chi(np.array([x[0], x[1], s])) for s in s_array]), label='via min') plt.plot(s_array, np.array([chi(np.array([x[0]+0.01, x[1]-0.01, s])) for s in s_array]), label='no min') plt.legend() # ------------------------------------------------------------------------ plt.show() # For positve def and negdef forms see that we found minima and maxima # respectively # Something similar could be done for cs with singular system but the plot # tells the same story if is_posdef or is_negdef or is_indef: phi_s = np.array([PHI[random_index(PHI.shape[0], PHI.shape[1])] for i in range(1000)]) phi_s -= phi(x) if is_posdef: assert np.all(phi_s > 0) elif is_negdef: assert np.all(phi_s < 0)
def test_multiplication(self): print( '== testing multiplication of system matrix for problem of weighted projection ====' ) for dim, pol_order in itertools.product([2, 3], [1, 2]): N = 2 # no. of elements print('dim={0}, pol_order={1}, N={2}'.format(dim, pol_order, N)) # creating MESH and defining MATERIAL if dim == 2: mesh = UnitSquareMesh(N, N) m = Expression("1+10*16*x[0]*(1-x[0])*x[1]*(1-x[1])", degree=4) # material coefficients elif dim == 3: mesh = UnitCubeMesh(N, N, N) m = Expression("1+10*16*x[0]*(1-x[0])*(1-x[1])*x[2]", degree=1) # material coefficients mesh.coordinates()[:] += 0.1 * np.random.random( mesh.coordinates().shape) # mesh perturbation V = FunctionSpace(mesh, "CG", pol_order) # original FEM space W = FunctionSpace(mesh, "DG", 2 * (pol_order - 1)) # double-grid space print('assembling local matrices for DoGIP...') Bhat = get_Bhat( dim, pol_order, problem=1 ) # interpolation between V on W on a reference element AT_dogip = get_A_T(m, V, W, problem=1) dofmapV = V.dofmap() def system_multiplication_DoGIP(AT_dogip, Bhat, u_vec): # matrix-vector mutliplication in DoGIP Au = np.zeros_like(u_vec) for ii, cell in enumerate(cells(mesh)): ind = dofmapV.cell_dofs(ii) # local to global map Bu = Bhat.dot(u_vec[ind]) ABu = np.einsum('rsj,sj->rj', AT_dogip[ii], Bu) Au[ind] += np.einsum('rjl,rj->l', Bhat, ABu) return Au print('assembling system matrix for FEM') u, v = TrialFunction(V), TestFunction(V) Asp = assemble(m * inner(grad(u), grad(v)) * dx, tensor=EigenMatrix()) Asp = Asp.sparray() # sparse FEM matrix print('multiplication...') ur = Function(V) # creating random vector ur_vec = 10 * np.random.random(V.dim()) ur.vector().set_local(ur_vec) Au_DoGIP = system_multiplication_DoGIP( AT_dogip, Bhat, ur_vec) # DoGIP multiplication Auex = Asp.dot(ur_vec) # FEM multiplication with sparse matrix # testing the difference between DoGIP and FEniCS self.assertAlmostEqual(0, np.linalg.norm(Auex - Au_DoGIP)) print('...ok')
class Solver(gs.GeneralSolver): def __init__(self, args, tc, metadata): gs.GeneralSolver.__init__(self, args, tc, metadata) self.metadata['hasTentativeV'] = True self.solver_vel_tent = None self.solver_vel_cor = None self.solver_p = None self.solver_rot = None self.null_space = None # input parameters self.forceOutflow = args.fo self.useLaplace = args.laplace self.use_full_SUPG = args.cs self.bcv = 'NOT' if self.useLaplace else args.bcv if self.bcv == 'CDN': info('Using classical do nothing condition (Tn=0).') if self.bcv == 'DDN': info( 'Using directional do nothing condition (Tn=0.5*negative(u.n)u).' ) if self.bcv == 'LAP': info('Using laplace neutral condition (grad(u)n=0).') self.stabCoef = args.stab self.stabilize = (args.stab > DOLFIN_EPS) if self.stabilize: if self.use_full_SUPG: info( 'Used consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info( 'Used non-consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info('No stabilization used.') self.solvers = args.solvers self.useRotationScheme = args.r self.metadata['hasTentativeP'] = self.useRotationScheme def __str__(self): return 'ipcs1 - incremental pressure correction scheme with nonlinearity treated by Adam-Bashword + ' \ 'Crank-Nicolson and viscosity term treated semi-explicitly (Crank-Nicholson)' @staticmethod def setup_parser_options(parser): gs.GeneralSolver.setup_parser_options(parser) parser.add_argument('--stab', help='Use stabilization (positive constant)', type=float, default=0.) parser.add_argument('--cs', help='Use consistent SUPG stabilisation.', action='store_true') parser.add_argument('--cbc_tau', help='Use simpler cbcflow parameter for SUPG', action='store_true') parser.add_argument('-r', help='Use rotation scheme', action='store_true') parser.add_argument('-B', help='Use no BC in correction step', action='store_true') parser.add_argument('-s', '--solvers', help='Solvers', choices=['direct', 'krylov'], default='krylov') parser.add_argument('-b', '--bc', help='Pressure boundary condition mode', choices=['outflow', 'nullspace'], default='outflow') # 'outflow' should be used with direct solvers (cannot LU-decompose singular matrix) parser.add_argument( '--prv1', help='relative tentative velocity Krylov solver precision', type=int, default=6) parser.add_argument( '--pav1', help='absolute tentative velocity Krylov solver precision', type=int, default=10) parser.add_argument('--pap', help='pressure Krylov solver absolute precision', type=int, default=6) parser.add_argument('--prp', help='pressure Krylov solver relative precision', type=int, default=10) parser.add_argument( '--precV', help='Preconditioner for tentative velocity GMRES solver', type=str, default='sor') parser.add_argument( '--precVC', help='Preconditioner for corrected velocity CG solver', type=str, default='sor') parser.add_argument( '--precP', help='Preconditioner for 2nd step solver (Poisson)', choices=['hypre_amg', 'ilu', 'sor'], default='sor') parser.add_argument('--solP', help='2nd step solver (Poisson)', type=str, default='cg') # choices=['cg', 'gmres', 'richardson', 'tfqmr', ...] parser.add_argument('--Prestart', help='2nd step solver GMRES restart', type=int, default=-1) parser.add_argument('--Vrestart', help='1st step solver GMRES restart', type=int, default=-1) parser.add_argument('--fo', help='Force Neumann outflow boundary for pressure', action='store_true') parser.add_argument('--laplace', help='Use laplace(u) instead of div(symgrad(u))', action='store_true') parser.add_argument( '--bcv', help='Outflow BC for velocity (with stress formulation)', choices=['CDN', 'DDN', 'LAP'], default='CDN') parser.add_argument( '--ema', help='Use EMA conserving scheme for convection term', action='store_true') # described in Charnyi, Heister, Olshanskii, Rebholz: # "On conservation laws of Navier-Stokes Galerkin discretizations" (2016) # it seems that it does not work with projection methods (probably because of dropping div u = 0 constraint) # not documented in readme 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
class Solver(gs.GeneralSolver): def __init__(self, args, tc, metadata): gs.GeneralSolver.__init__(self, args, tc, metadata) self.metadata['hasTentativeV'] = True self.solver_vel_tent = None self.solver_vel_cor = None self.solver_p = None self.solver_rot = None self.null_space = None # input parameters self.forceOutflow = args.fo self.useLaplace = args.laplace self.use_full_SUPG = args.cs self.bcv = 'NOT' if self.useLaplace else args.bcv if self.bcv == 'CDN': info('Using classical do nothing condition (Tn=0).') if self.bcv == 'DDN': info('Using directional do nothing condition (Tn=0.5*negative(u.n)u).') if self.bcv == 'LAP': info('Using laplace neutral condition (grad(u)n=0).') self.stabCoef = args.stab self.stabilize = (args.stab > DOLFIN_EPS) if self.stabilize: if self.use_full_SUPG: info('Used consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info('Used non-consistent streamline-diffusion stabilization with coef.: %f' % args.stab) else: info('No stabilization used.') self.solvers = args.solvers self.useRotationScheme = args.r self.metadata['hasTentativeP'] = self.useRotationScheme def __str__(self): return 'ipcs1 - incremental pressure correction scheme with nonlinearity treated by Adam-Bashword + ' \ 'Crank-Nicolson and viscosity term treated semi-explicitly (Crank-Nicholson)' @staticmethod def setup_parser_options(parser): gs.GeneralSolver.setup_parser_options(parser) parser.add_argument('--stab', help='Use stabilization (positive constant)', type=float, default=0.) parser.add_argument('--cs', help='Use consistent SUPG stabilisation.', action='store_true') parser.add_argument('--cbc_tau', help='Use simpler cbcflow parameter for SUPG', action='store_true') parser.add_argument('-r', help='Use rotation scheme', action='store_true') parser.add_argument('-B', help='Use no BC in correction step', action='store_true') parser.add_argument('-s', '--solvers', help='Solvers', choices=['direct', 'krylov'], default='krylov') parser.add_argument('-b', '--bc', help='Pressure boundary condition mode', choices=['outflow', 'nullspace'], default='outflow') # 'outflow' should be used with direct solvers (cannot LU-decompose singular matrix) parser.add_argument('--prv1', help='relative tentative velocity Krylov solver precision', type=int, default=6) parser.add_argument('--pav1', help='absolute tentative velocity Krylov solver precision', type=int, default=10) parser.add_argument('--pap', help='pressure Krylov solver absolute precision', type=int, default=6) parser.add_argument('--prp', help='pressure Krylov solver relative precision', type=int, default=10) parser.add_argument('--precV', help='Preconditioner for tentative velocity GMRES solver', type=str, default='sor') parser.add_argument('--precVC', help='Preconditioner for corrected velocity CG solver', type=str, default='sor') parser.add_argument('--precP', help='Preconditioner for 2nd step solver (Poisson)', choices=['hypre_amg', 'ilu', 'sor'], default='sor') parser.add_argument('--solP', help='2nd step solver (Poisson)', type=str, default='cg') # choices=['cg', 'gmres', 'richardson', 'tfqmr', ...] parser.add_argument('--Prestart', help='2nd step solver GMRES restart', type=int, default=-1) parser.add_argument('--Vrestart', help='1st step solver GMRES restart', type=int, default=-1) parser.add_argument('--fo', help='Force Neumann outflow boundary for pressure', action='store_true') parser.add_argument('--laplace', help='Use laplace(u) instead of div(symgrad(u))', action='store_true') parser.add_argument('--bcv', help='Outflow BC for velocity (with stress formulation)', choices=['CDN', 'DDN', 'LAP'], default='CDN') parser.add_argument('--ema', help='Use EMA conserving scheme for convection term', action='store_true') # described in Charnyi, Heister, Olshanskii, Rebholz: # "On conservation laws of Navier-Stokes Galerkin discretizations" (2016) # it seems that it does not work with projection methods (probably because of dropping div u = 0 constraint) # not documented in readme 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
class Discretization(object): def __init__(self, problem, verbosity=0): """ :param ProblemData problem: :param int verbosity: :return: """ self.parameters = parameters["discretization"] self.verb = verbosity self.vis_folder = os.path.join(problem.out_folder, "MESH") self.core = problem.core self.G = problem.G if self.verb > 1: print pid+"Loading mesh" t_load = Timer("DD: Data loading") if not problem.mesh_module: if self.verb > 1: print pid + " mesh data" self.mesh = Mesh(problem.mesh_files.mesh) if self.verb > 1: print pid + " physical data" self.cell_regions_fun = MeshFunction("size_t", self.mesh, problem.mesh_files.physical_regions) if self.verb > 1: print pid + " boundary data" self.boundaries = MeshFunction("size_t", self.mesh, problem.mesh_files.facet_regions) else: self.mesh = problem.mesh_module.mesh self.cell_regions_fun = problem.mesh_module.regions try: self.boundaries = problem.mesh_module.boundaries except AttributeError: self.boundaries = None assert self.mesh assert self.boundaries is None or self.boundaries.array().size > 0 if self.verb > 2: print pid+" mesh info: " + str(self.mesh) if self.verb > 1: print0("Defining function spaces" ) self.t_spaces = Timer("DD: Function spaces construction") # Spaces that must be specified by the respective subclasses self.V = None # solution space self.Vphi1 = None # 1-g scalar flux space # XS / TH space self.V0 = FunctionSpace(self.mesh, "DG", 0) self.ndof0 = self.V0.dim() dofmap = self.V0.dofmap() self.local_ndof0 = dofmap.local_dimension("owned") self.cell_regions = self.cell_regions_fun.array() assert self.cell_regions.size == self.local_ndof0 def __create_cell_dof_mapping(self, dofmap): """ Generate cell -> dof mapping for all cells of current partition. Note: in DG(0) space, there is one dof per element and no ghost cells. :param GenericDofMap dofmap: DG(0) dofmap """ if self.verb > 2: print0("Constructing cell -> dof mapping") timer = Timer("DD: Cell->dof construction") code = \ ''' #include <dolfin/mesh/Cell.h> namespace dolfin { void fill_in(Array<int>& local_cell_dof_map, const Mesh& mesh, const GenericDofMap& dofmap) { std::size_t local_dof_range_start = dofmap.ownership_range().first; int* cell_dof_data = local_cell_dof_map.data(); for (CellIterator c(mesh); !c.end(); ++c) *cell_dof_data++ = dofmap.cell_dofs(c->index())[0] - local_dof_range_start; } } ''' cell_mapping_module = compile_extension_module(code) cell_dof_array = IntArray(self.local_ndof0) cell_mapping_module.fill_in(cell_dof_array, self.mesh, dofmap) self._local_cell_dof_map = cell_dof_array.array() timer.stop() def __create_cell_layers_mapping(self): """ Generate a cell -> axial layer mapping for all cells of current partition. Note that keys are ordered by the associated DG(0) dof, not by the cell index in the mesh. """ if self.verb > 2: print0("Constructing cell -> layer mapping") timer = Timer("DD: Cell->layer construction") code = \ ''' #include <dolfin/mesh/Cell.h> namespace dolfin { void fill_in(Array<int>& local_cell_layers, const Mesh& mesh, const Array<int>& cell_dofs, const Array<double>& layer_boundaries) { std::size_t num_layers = layer_boundaries.size() - 1; unsigned int layer; for (CellIterator c(mesh); !c.end(); ++c) { double midz = c->midpoint().z(); for (layer = 0; layer < num_layers; layer++) if (layer_boundaries[layer] <= midz && midz <= layer_boundaries[layer+1]) break; int dof = cell_dofs[c->index()]; local_cell_layers[dof] = layer; } } } ''' cell_mapping_module = compile_extension_module(code) cell_layers_array = IntArray(self.local_ndof0) cell_mapping_module.fill_in(cell_layers_array, self.mesh, self.local_cell_dof_map, self.core.layer_boundaries) self._local_cell_layers = cell_layers_array.array() timer.stop() def __create_cell_vol_mapping(self): """ Generate cell -> volume mapping for all cells of current partition. Note that keys are ordered by the associated DG(0) dof, not by the cell index in the mesh. This map is required for calculating various densities from total region integrals (like cell power densities from cell-integrated powers). """ if self.verb > 2: print0("Constructing cell -> volume mapping") timer = Timer("DD: Cell->vol construction") code = \ ''' #include <dolfin/mesh/Cell.h> namespace dolfin { void fill_in(Array<double>& cell_vols, const Mesh& mesh, const Array<int>& cell_dofs) { for (CellIterator c(mesh); !c.end(); ++c) cell_vols[cell_dofs[c->index()]] = c->volume(); } } ''' cell_mapping_module = compile_extension_module(code) cell_vol_array = DoubleArray(self.local_ndof0) cell_mapping_module.fill_in(cell_vol_array, self.mesh, self.local_cell_dof_map) self._local_cell_volumes = cell_vol_array.array() timer.stop() @property def local_cell_dof_map(self): try: self._local_cell_dof_map except AttributeError: self.__create_cell_dof_mapping(self.V0.dofmap()) return self._local_cell_dof_map @property def local_cell_volumes(self): try: self._local_cell_volumes except AttributeError: self.__create_cell_vol_mapping() return self._local_cell_volumes @property def local_cell_layers(self): try: self._local_cell_layers except AttributeError: self.__create_cell_layers_mapping() return self._local_cell_layers def visualize_mesh_data(self): timer = Timer("DD: Mesh data visualization") if self.verb > 2: print0("Visualizing mesh data") File(os.path.join(self.vis_folder, "mesh.pvd"), "compressed") << self.mesh if self.boundaries: File(os.path.join(self.vis_folder, "boundaries.pvd"), "compressed") << self.boundaries File(os.path.join(self.vis_folder, "mesh_regions.pvd"), "compressed") << self.cell_regions_fun # Create MeshFunction to hold cell process rank processes = CellFunction('size_t', self.mesh, MPI.rank(comm)) File(os.path.join(self.vis_folder, "mesh_partitioning.pvd"), "compressed") << processes def print_diagnostics(self): print "\nDiscretization diagnostics" print MPI.rank(comm), self.mesh.num_entities(self.mesh.topology().dim()) dofmap = self.V0.dofmap() print MPI.rank(comm), dofmap.ownership_range() print MPI.rank(comm), numpy.min(dofmap.collapse(self.mesh)[1].values()), \ numpy.max(dofmap.collapse(self.mesh)[1].values()) print "#Owned by {}: {}".format(MPI.rank(comm), dofmap.local_dimension("owned")) print "#Unowned by {}: {}".format(MPI.rank(comm), dofmap.local_dimension("unowned"))
# BC conditions, nullspace v_in_expr = Constant(v_in) plt = plot(interpolate(v_in_expr, V), range_min=0., range_max=2 * v_in, window_width=width, window_height=height) plt.write_png('%s/correct' % dir) # v_in_expr = Expression('(t<1.0)?t*v:v', v=Constant(v_in), t=0.0) # v_in_expr = Expression('(t<1.0)?(1-cos(pi*t))*v*0.5:v', v=Constant(v_in), t=0.0) bcp = DirichletBC(Q, Constant(0.0), boundary_parts, 2) bcu = DirichletBC(V, v_in_expr, boundary_parts, 1) foo = Function(Q) null_vec = Vector(foo.vector()) Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0 / null_vec.norm('l2') # print(null_vec.array()) null_space = VectorSpaceBasis([null_vec]) ds = Measure("ds", subdomain_data=boundary_parts) # Define forms (dont redefine functions used here) # step 1 u0 = Function(V) u1 = Function(V) p0 = Function(Q) u_tent = TrialFunction(V) v = TestFunction(V) # U_ = 1.5*u0 - 0.5*u1 # nonlinearity = inner(dot(0.5 * (u_tent.dx(0) + u0.dx(0)), U_), v) * dx