def adaptive(self, mesh, eigv, eigf): """Refine mesh based on residual errors.""" fraction = 0.1 C = FunctionSpace(mesh, "DG", 0) # constants on triangles w = TestFunction(C) h = CellSize(mesh) n = FacetNormal(mesh) marker = CellFunction("bool", mesh) print len(marker) indicators = np.zeros(len(marker)) for e, u in zip(eigv, eigf): errform = avg(h) * jump(grad(u), n) ** 2 * avg(w) * dS \ + h * (inner(grad(u), n) - Constant(e) * u) ** 2 * w * ds if self.degree > 1: errform += h ** 2 * div(grad(u)) ** 2 * w * dx indicators[:] += assemble(errform).array() # errors for each cell print "Residual error: ", sqrt(sum(indicators) / len(eigv)) cutoff = sorted( indicators, reverse=True)[ int(len(indicators) * fraction) - 1] marker.array()[:] = indicators > cutoff # mark worst errors mesh = refine(mesh, marker) return mesh
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 _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)
def solverNeilanSalgadoZhang(P, opt): ''' This function implements the method presented in Neilan, Salgado, Zhang (2017), Chapter 4 Main characteristics: - test with piecewise second derivative of test_u - no discrete Hessian - first-order stabilization necessary ''' assert opt["HessianSpace"] == "CG", 'opt["HessianSpace"] has to be "CG"' assert opt[ "stabilityConstant1"], 'opt["stabilityConstant1"] has to be positive' gamma = P.normalizeSystem(opt) if isinstance(P.g, list): bc_V = [] for fun, dom in P.g: bc_V.append(DirichletBC(P.V, fun, dom)) else: if isinstance(P.g, Function): bc_V = DirichletBC(P.V, P.g, 'on_boundary') else: g = project(P.g, P.V) bc_V = DirichletBC(P.V, g, 'on_boundary') trial_u = TrialFunction(P.V) test_u = TestFunction(P.V) # Assemble right-hand side in each case f_h = gamma * P.f * div(grad(test_u)) * dx # Adjust load vector in case of time-dependent problem if P.isTimeDependant: f_h = gamma * P.u_np1 * div(grad(test_u)) * dx - P.dt * f_h rhs = assemble(f_h) if isinstance(bc_V, list): for bc_v in bc_V: bc_v.apply(rhs) else: bc_V.apply(rhs) repeat_setup = False if hasattr(P, 'timeDependentCoefs'): if P.timeDependentCoefs: repeat_setup = True if 'HJB' in str(type(P)): repeat_setup = True if (not P.isTimeDependant) or (P.iter == 0) or repeat_setup: print('Setup bilinear form') # Define bilinear form a_h = gamma * inner(P.a, grad(grad(trial_u))) * div(grad(test_u)) * dx \ + opt["stabilityConstant1"] * avg(P.hE)**(-1) * inner( jump(grad(trial_u), P.nE), jump(grad(test_u), P.nE)) * dS if P.hasDrift: a_h += gamma * inner(P.b, grad(trial_u)) * div(grad(test_u)) * dx if P.hasPotential: a_h += gamma * P.c * trial_u * div(grad(test_u)) * dx # Adjust system matrix in case of time-dependent problem if P.isTimeDependant: a_h = gamma * trial_u * div(grad(test_u)) * dx - P.dt * a_h S = assemble(a_h) if isinstance(bc_V, list): for bc_v in bc_V: bc_v.apply(S) else: bc_V.apply(S) P.S = S if opt['time_check']: t1 = time() solve(P.S, P.u.vector(), rhs) # tmp = assemble(inner(P.a,grad(grad(trial_u))) * div(grad(test_u)) * dx) # A = assemble(a_h) # print('Row sum: ', sum(A.array(),1).round(4)) # print('Col sum: ', sum(A.array(),0).round(4)) # print('Total sum: ', sum(sum(A.array())).round(4)) # ipdb.set_trace() # solve(a_h == f_h, P.u, bc_V, solver_parameters={'linear_solver': 'mumps'}) if opt['time_check']: print("Solve linear equation system ... %.2fs" % (time() - t1)) sys.stdout.flush() N_iter = 1 return N_iter
# Crank Nicholson parameter cn = 1.0 Hm = cn * H + (1 - cn) * H0 # Jump and average for DG elements H_avg = 0.5 * (Hm('+') + Hm('-')) H_jump = Hm('+') * nhat('+') + Hm('-') * nhat('-') xsi_avg = 0.5 * (xsi('+') + xsi('-')) xsi_jump = xsi('+') * nhat('+') + xsi('-') * nhat('-') uvec = df.as_vector([u, v]) unorm = df.dot(uvec, uvec)**0.5 #Upwind uH = df.avg(uvec) * H_avg + 0.5 * df.avg(unorm) * H_jump beff = df.Function(Q_dg) beff.interpolate(adot()) R_H = ((H - H0) / dt - beff) * xsi * df.dx - df.dot( df.grad(xsi), uvec * Hm) * df.dx + df.dot( uH, xsi_jump) * df.dS # + xsi*H*df.dot(uvec,nhat)*df.ds # Calving relations k_calving = df.Constant(0.5) H_calving = (1 + df.Constant(.0)) * d * rho_w / rho f = Min((H_calving / H), 5) ucalv = f * uvec # ALE level set velocity unorm_c = df.dot(uvec - ucalv, uvec - ucalv)**0.5
def setup_scalar_equation(self): sim = self.simulation V = sim.data['Vphi'] mesh = V.mesh() P = V.ufl_element().degree() # Source term source_cpp = sim.input.get_value('solver/source', '0', 'string') f = dolfin.Expression(source_cpp, degree=P) # Create the solution function sim.data['phi'] = dolfin.Function(V) # DG elliptic penalty penalty = define_penalty(mesh, P, k_min=1.0, k_max=1.0) penalty_dS = dolfin.Constant(penalty) penalty_ds = dolfin.Constant(penalty * 2) yh = dolfin.Constant(1 / (penalty * 2)) # Define weak form u, v = dolfin.TrialFunction(V), dolfin.TestFunction(V) a = dot(grad(u), grad(v)) * dx L = f * v * dx # Symmetric Interior Penalty method for -∇⋅∇φ n = dolfin.FacetNormal(mesh) a -= dot(n('+'), avg(grad(u))) * jump(v) * dS a -= dot(n('+'), avg(grad(v))) * jump(u) * dS # Symmetric Interior Penalty coercivity term a += penalty_dS * jump(u) * jump(v) * dS # Dirichlet boundary conditions # Nitsche's (1971) method, see e.g. Epshteyn and Rivière (2007) dirichlet_bcs = sim.data['dirichlet_bcs'].get('phi', []) for dbc in dirichlet_bcs: bcval, dds = dbc.func(), dbc.ds() # SIPG for -∇⋅∇φ a -= dot(n, grad(u)) * v * dds a -= dot(n, grad(v)) * u * dds L -= dot(n, grad(v)) * bcval * dds # Weak Dirichlet a += penalty_ds * u * v * dds L += penalty_ds * bcval * v * dds # Neumann boundary conditions neumann_bcs = sim.data['neumann_bcs'].get('phi', []) for nbc in neumann_bcs: L += nbc.func() * v * nbc.ds() # Robin boundary conditions # See Juntunen and Stenberg (2009) # n⋅∇φ = (φ0 - φ)/b + g robin_bcs = sim.data['robin_bcs'].get('phi', []) for rbc in robin_bcs: b, rds = rbc.blend(), rbc.ds() dval, nval = rbc.dfunc(), rbc.nfunc() # From IBP of the main equation a -= dot(n, grad(u)) * v * rds # Test functions for the Robin BC z1 = 1 / (b + yh) * v z2 = -yh / (b + yh) * dot(n, grad(v)) # Robin BC added twice with different test functions for z in [z1, z2]: a += b * dot(n, grad(u)) * z * rds a += u * z * rds L += dval * z * rds L += b * nval * z * rds # Does the system have a null-space? self.has_null_space = len(dirichlet_bcs) + len(robin_bcs) == 0 self.form_lhs = a self.form_rhs = L
def h_linear(integrator_type, mesh, subdomains, boundaries, t_start, dt, T, solution0, \ alpha_0, K_0, mu_l_0, lmbda_l_0, Ks_0, \ alpha_1, K_1, mu_l_1, lmbda_l_1, Ks_1, \ alpha, K, mu_l, lmbda_l, Ks, \ cf_0, phi_0, rho_0, mu_0, k_0,\ cf_1, phi_1, rho_1, mu_1, k_1,\ cf, phi, rho, mu, k, \ sigma_v_freeze, dphi_c_dt): # Create mesh and define function space parameters["ghost_mode"] = "shared_facet" # required by dS dx = Measure('dx', domain=mesh, subdomain_data=subdomains) ds = Measure('ds', domain=mesh, subdomain_data=boundaries) dS = Measure('dS', domain=mesh, subdomain_data=boundaries) BDM = FiniteElement("BDM", mesh.ufl_cell(), 1) PDG = FiniteElement("DG", mesh.ufl_cell(), 0) BDM_F = FunctionSpace(mesh, BDM) PDG_F = FunctionSpace(mesh, PDG) W = BlockFunctionSpace([BDM_F, PDG_F], restrict=[None, None]) TM = TensorFunctionSpace(mesh, 'DG', 0) PM = FunctionSpace(mesh, 'DG', 0) n = FacetNormal(mesh) vc = CellVolume(mesh) fc = FacetArea(mesh) h = vc / fc h_avg = (vc('+') + vc('-')) / (2 * avg(fc)) I = Identity(mesh.topology().dim()) monitor_dt = dt p_outlet = 0.1e6 p_inlet = 1000.0 M_inv = phi_0 * cf + (alpha - phi_0) / Ks # Define variational problem trial = BlockTrialFunction(W) dv, dp = block_split(trial) trial_dot = BlockTrialFunction(W) dv_dot, dp_dot = block_split(trial_dot) test = BlockTestFunction(W) psiv, psip = block_split(test) block_w = BlockFunction(W) v, p = block_split(block_w) block_w_dot = BlockFunction(W) v_dot, p_dot = block_split(block_w_dot) a_time = Constant(0.0) * inner(v_dot, psiv) * dx #quasi static # k is a function of phi #k = perm_update_rutqvist_newton(p,p0,phi0,phi,coeff) lhs_a = inner(dot(v, mu * inv(k)), psiv) * dx - p * div( psiv ) * dx #+ 6.0*inner(psiv,n)*ds(2) # - inner(gravity*(rho-rho0), psiv)*dx b_time = (M_inv + pow(alpha, 2.) / K) * p_dot * psip * dx lhs_b = div(v) * psip * dx #div(rho*v)*psip*dx #TODO rho rhs_v = -p_outlet * inner(psiv, n) * ds(3) rhs_p = -alpha / K * sigma_v_freeze * psip * dx - dphi_c_dt * psip * dx r_u = [lhs_a, lhs_b] j_u = block_derivative(r_u, block_w, trial) r_u_dot = [a_time, b_time] j_u_dot = block_derivative(r_u_dot, block_w_dot, trial_dot) r = [r_u_dot[0] + r_u[0] - rhs_v, \ r_u_dot[1] + r_u[1] - rhs_p] def bc(t): #bc_v = [DirichletBC(W.sub(0), (.0, .0), boundaries, 4)] v1 = DirichletBC(W.sub(0), (1.e-4 * 2.0, 0.0), boundaries, 1) v2 = DirichletBC(W.sub(0), (0.0, 0.0), boundaries, 2) v4 = DirichletBC(W.sub(0), (0.0, 0.0), boundaries, 4) bc_v = [v1, v2, v4] return BlockDirichletBC([bc_v, None]) # Define problem wrapper class ProblemWrapper(object): def set_time(self, t): pass #g.t = t # Residual and jacobian functions def residual_eval(self, t, solution, solution_dot): #print(as_backend_type(assemble(p_time - p_time_error)).vec().norm()) #print("gravity effect", as_backend_type(assemble(inner(gravity*(rho-rho0), psiv)*dx)).vec().norm()) return r def jacobian_eval(self, t, solution, solution_dot, solution_dot_coefficient): return [[Constant(solution_dot_coefficient)*j_u_dot[0, 0] + j_u[0, 0], \ Constant(solution_dot_coefficient)*j_u_dot[0, 1] + j_u[0, 1]], \ [Constant(solution_dot_coefficient)*j_u_dot[1, 0] + j_u[1, 0], \ Constant(solution_dot_coefficient)*j_u_dot[1, 1] + j_u[1, 1]]] # Define boundary condition def bc_eval(self, t): return bc(t) # Define initial condition def ic_eval(self): return solution0 # Define custom monitor to plot the solution def monitor(self, t, solution, solution_dot): pass # Solve the time dependent problem problem_wrapper = ProblemWrapper() (solution, solution_dot) = (block_w, block_w_dot) solver = TimeStepping(problem_wrapper, solution, solution_dot) solver.set_parameters({ "initial_time": t_start, "time_step_size": dt, "monitor": { "time_step_size": monitor_dt, }, "final_time": T, "exact_final_time": "stepover", "integrator_type": integrator_type, "problem_type": "linear", "linear_solver": "mumps", "report": True }) export_solution = solver.solve() return export_solution, T
Vx = FunctionSpace(mx, 'CG', 2) Vy = FunctionSpace(my, 'CG', 1) phi = TrialFunction(Vx) psi = TestFunction(Vx) v = Function(Vx) # We avoid the normalization gamma = 1.0 # Jump penalty term stab1 = 2. nE = FacetNormal(mx) hE = FacetArea(mx) test = div(grad(psi)) S_xx = gamma * inner(ax, grad(grad(phi))) * test * dx(mx) \ + stab1 * avg(hE)**(-1) * inner(jump(grad(phi), nE), jump(grad(psi), nE)) * dS(mx) S_x = gamma * inner(bx, grad(phi)) * test * dx(mx) S_0 = gamma * c * phi * test * dx(mx) # This matrix also changes since we are testing the whole equation # with div(grad(psi)) instead of psi M_ = gamma * phi * test * dx(mx) M = assemble(M_) # Prepare special treatment of deterministic part and time-derivative. # Probably, I'll need here the dof coordinates and the dof list xdofs = Vx.tabulate_dof_coordinates().flatten() ix = np.argsort(xdofs)
def navier_stokes_stabilization_penalties( simulation, nu, velocity_continuity_factor_D12=0, pressure_continuity_factor=0, no_coeff=False, ): """ Calculate the stabilization parameters needed in the DG scheme """ ndim = simulation.ndim mpm = simulation.multi_phase_model mesh = simulation.data['mesh'] use_const = simulation.input.get_value('solver/spatially_constant_penalty', False, 'bool') if no_coeff: mu_min = mu_max = 1.0 else: mu_min, mu_max = mpm.get_laminar_dynamic_viscosity_range() P = simulation.data['Vu'].ufl_element().degree() if use_const: simulation.log.info(' Using spatially constant elliptic penalty') penalty_dS = define_penalty(mesh, P, mu_min, mu_max, boost_factor=3, exponent=1.0) penalty_ds = penalty_dS * 2 simulation.log.info(' DG SIP penalty: dS %.1f ds %.1f' % (penalty_dS, penalty_ds)) penalty_dS = Constant(penalty_dS) penalty_ds = Constant(penalty_ds) else: simulation.log.info(' Using spatially varying elliptic penalties') penalty_dS = define_spatially_varying_penalty(simulation, P, mu_min, mu_max, boost_factor=3, exponent=1.0) penalty_ds = penalty_dS * 2 penalty_dS = dolfin.conditional( dolfin.lt(penalty_dS('+'), penalty_dS('-')), penalty_dS('-'), penalty_dS('+'), ) if velocity_continuity_factor_D12: D12 = Constant([velocity_continuity_factor_D12] * ndim) else: D12 = Constant([0] * ndim) if pressure_continuity_factor: h = simulation.data['h'] h = Constant(1.0) D11 = avg(h / nu) * Constant(pressure_continuity_factor) else: D11 = None return penalty_dS, penalty_ds, D11, D12
degree = 2 V = MultiMeshFunctionSpace(multimesh, "CG", degree) # Assign a function to each mesh, such that T and lmb are discontinuous at the # interface Gamma T = MultiMeshFunction(V) lmb = MultiMeshFunction(V) # Terms for variational form alpha = 4 beta = 4 h = 2 * Circumradius(multimesh) h = 0.5 * (h("+") + h("-")) a1 = inner(grad(T), grad(lmb)) * dX a2 = -dot(avg(grad(T)), jump(lmb, n)) * dI a3 = -dot(avg(grad(lmb)), jump(T, n)) * dI # NOTE: Only adding h to a4 reduced convergence rate a4 = alpha / h * inner(jump(T), jump(lmb)) * dI # NOTE: Adding overlap stabilization increases convergence rate to two as it enforces smoothness a5 = beta * dot(jump(grad(T)), jump(grad(lmb))) * dO J1 = inner(T, T) * dX def solve_state(): a = a1 + a2 + a3 + a4 + a5 a = replace(a, {lmb: TestFunction(V), T: TrialFunction(V)}) l = Constant(0) * TestFunction(V) * dX a = assemble_multimesh(a) L = assemble_multimesh(l) bcs = [
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_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 _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