def delta_dg(mesh, expr): V = df.FunctionSpace(mesh, "DG", 0) m = df.interpolate(expr, V) n = df.FacetNormal(mesh) h = df.CellSize(mesh) h_avg = (h('+') + h('-')) / 2 alpha = 1.0 gamma = 0.0 u = df.TrialFunction(V) v = df.TestFunction(V) # for DG 0 case, only term contain alpha is nonzero a = df.dot(df.grad(v), df.grad(u))*df.dx \ - df.dot(df.avg(df.grad(v)), df.jump(u, n))*df.dS \ - df.dot(df.jump(v, n), df.avg(df.grad(u)))*df.dS \ + alpha/h_avg*df.dot(df.jump(v, n), df.jump(u, n))*df.dS \ - df.dot(df.grad(v), u*n)*df.ds \ - df.dot(v*n, df.grad(u))*df.ds \ + gamma/h*v*u*df.ds K = df.assemble(a).array() L = df.assemble(v * df.dx).array() h = -np.dot(K, m.vector().array()) / (L) xs = [] for cell in df.cells(mesh): xs.append(cell.midpoint().x()) print len(xs), len(h) return xs, h
def assemble_1d(mesh): DG = df.FunctionSpace(mesh, "DG", 0) n = df.FacetNormal(mesh) h = df.CellSize(mesh) h_avg = (h('+') + h('-')) / 2 u = df.TrialFunction(DG) v = df.TestFunction(DG) a = 1.0 / h_avg * df.dot(df.jump(v, n), df.jump(u, n)) * df.dS K = df.assemble(a) L = df.assemble(v * df.dx).array() return copy_petsc_to_csr(K), L
def _setup_divergence(self, vel, method, name='u'): """ Calculate divergence and element to element velocity flux differences on the same edges """ V = df.FunctionSpace(self.mesh, 'DG', 0) n = df.FacetNormal(self.mesh) u, v = df.TrialFunction(V), df.TestFunction(V) # The difference between the flux on the same facet between two different cells a1 = u * v * dx w = dot(vel('+') - vel('-'), n('+')) if method == 'div0': L1 = w * avg(v) * dS else: L1 = abs(w) * avg(v) * dS # The divergence internally in the cell a2 = u * v * dx if method in ('div', 'div0'): L2 = abs(df.div(vel)) * v * dx elif method == 'gradq_avg': L2 = dot(avg(vel), n('+')) * jump(v) * dS - dot(vel, grad(v)) * dx else: raise ValueError('Divergence type %r not supported' % method) # Store for usage in projection storage = self._div[name] = {} storage['dS_solver'] = df.LocalSolver(a1, L1) storage['dx_solver'] = df.LocalSolver(a2, L2) storage['div_dS'] = df.Function(V) storage['div_dx'] = df.Function(V) # Pre-factorize matrices storage['dS_solver'].factorize() storage['dx_solver'].factorize() storage['div_dS'].rename('Divergence_%s_dS' % name, 'Divergence_%s_dS' % name) storage['div_dx'].rename('Divergence_%s_dx' % name, 'Divergence_%s_dx' % name)
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 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 solve(self): """ Solves the stokes equation with the current multimesh """ (u, p) = TrialFunctions(self.VQ) (v, q) = TestFunctions(self.VQ) n = FacetNormal(self.multimesh) h = 2.0 * Circumradius(self.multimesh) alpha = Constant(6.0) tensor_jump = lambda u: outer(u("+"), n("+")) + outer(u("-"), n("-")) a_s = inner(grad(u), grad(v)) * dX a_IP = - inner(avg(grad(u)), tensor_jump(v))*dI\ - inner(avg(grad(v)), tensor_jump(u))*dI\ + alpha/avg(h) * inner(jump(u), jump(v))*dI a_O = inner(jump(grad(u)), jump(grad(v))) * dO b_s = -div(u) * q * dX - div(v) * p * dX b_IP = jump(u, n) * avg(q) * dI + jump(v, n) * avg(p) * dI l_s = inner(self.f, v) * dX s_C = h*h*inner(-div(grad(u)) + grad(p), -div(grad(v)) - grad(q))*dC\ + h("+")*h("+")*inner(-div(grad(u("+"))) + grad(p("+")), -div(grad(v("+"))) + grad(q("+")))*dO l_C = h*h*inner(self.f, -div(grad(v)) - grad(q))*dC\ + h("+")*h("+")*inner(self.f("+"), -div(grad(v("+"))) - grad(q("+")))*dO a = a_s + a_IP + a_O + b_s + b_IP + s_C l = l_s + l_C A = assemble_multimesh(a) L = assemble_multimesh(l) [bc.apply(A, L) for bc in self.bcs] self.VQ.lock_inactive_dofs(A, L) solve(A, self.w.vector(), L, "mumps") self.splitMMF()
def define_pressure_equation(self): """ Setup the pressure Poisson equation This implementation assembles the full LHS and RHS each time they are needed """ sim = self.simulation Vp = sim.data['Vp'] p_star = sim.data['p'] u_star = sim.data['u'] # Trial and test functions p = dolfin.TrialFunction(Vp) q = dolfin.TestFunction(Vp) c1 = sim.data['time_coeffs'][0] dt = sim.data['dt'] mesh = sim.data['mesh'] n = dolfin.FacetNormal(mesh) # Fluid properties mpm = sim.multi_phase_model mu = mpm.get_laminar_dynamic_viscosity(0) rho = sim.data['rho'] # Lagrange multiplicator to remove the pressure null space # ∫ p dx = 0 assert not self.use_lagrange_multiplicator, 'NOT IMPLEMENTED YET' # Weak form of the Poisson eq. with discontinuous elements # -∇⋅∇p = - γ_1/Δt ρ ∇⋅u^* K = 1.0 / rho a = K * dot(grad(p), grad(q)) * dx L = K * dot(grad(p_star), grad(q)) * dx # RHS, ∇⋅u^* if self.incompressibility_flux_type == 'central': u_flux = avg(u_star) elif self.incompressibility_flux_type == 'upwind': switch = dolfin.conditional( dolfin.gt(abs(dot(u_star, n))('+'), 0.0), 1.0, 0.0 ) u_flux = switch * u_star('+') + (1 - switch) * u_star('-') L += c1 / dt * dot(u_star, grad(q)) * dx L -= c1 / dt * dot(u_flux, n('+')) * jump(q) * dS # Symmetric Interior Penalty method for -∇⋅∇p a -= dot(n('+'), avg(K * grad(p))) * jump(q) * dS a -= dot(n('+'), avg(K * grad(q))) * jump(p) * dS # Symmetric Interior Penalty method for -∇⋅∇p^* L -= dot(n('+'), avg(K * grad(p_star))) * jump(q) * dS L -= dot(n('+'), avg(K * grad(q))) * jump(p_star) * dS # Weak continuity penalty_dS, penalty_ds = self.calculate_penalties() # Symmetric Interior Penalty coercivity term a += penalty_dS * jump(p) * jump(q) * dS # L += penalty_dS*jump(p_star)*jump(q)*dS # Collect Dirichlet and outlet boundary values dirichlet_vals_and_ds = [] for dbc in sim.data['dirichlet_bcs'].get('p', []): dirichlet_vals_and_ds.append((dbc.func(), dbc.ds())) for obc in sim.data['outlet_bcs']: p_ = mu * dot(dot(grad(u_star), n), n) dirichlet_vals_and_ds.append((p_, obc.ds())) # Apply Dirichlet boundary conditions for p_bc, dds in dirichlet_vals_and_ds: # SIPG for -∇⋅∇p a -= dot(n, K * grad(p)) * q * dds a -= dot(n, K * grad(q)) * p * dds L -= dot(n, K * grad(q)) * p_bc * dds # SIPG for -∇⋅∇p^* L -= dot(n, K * grad(p_star)) * q * dds L -= dot(n, K * grad(q)) * p_star * dds # Weak Dirichlet a += penalty_ds * p * q * dds L += penalty_ds * p_bc * q * dds # Weak Dirichlet for p^* # L += penalty_ds*p_star*q*dds # L -= penalty_ds*p_bc*q*dds # Neumann boundary conditions neumann_bcs = sim.data['neumann_bcs'].get('p', []) for nbc in neumann_bcs: # Neumann boundary conditions on p and p_star cancel # L += (nbc.func() - dot(n, grad(p_star)))*q*nbc.ds() pass # Use boundary conditions for the velocity for the # term from integration by parts of div(u_star) for d in range(sim.ndim): dirichlet_bcs = sim.data['dirichlet_bcs'].get('u%d' % d, []) neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, []) for dbc in dirichlet_bcs: u_bc = dbc.func() L -= c1 / dt * u_bc * n[d] * q * dbc.ds() for nbc in neumann_bcs: L -= c1 / dt * u_star[d] * n[d] * q * nbc.ds() # ALE mesh velocities if sim.mesh_morpher.active: cvol_new = dolfin.CellVolume(mesh) cvol_old = sim.data['cvolp'] # Divergence of u should balance expansion/contraction of the cell K # ∇⋅u = -∂K/∂t (See below for definition of the ∇⋅u term) L -= (cvol_new - cvol_old) / dt * q * dx self.form_lhs = a self.form_rhs = L
def _setup_dg1_projection_2D(self, w, incompressibility_flux_type, D12, use_bcs): """ Implement the projection where the result is BDM embeded in a DG1 function """ sim = self.simulation k = 1 gdim = 2 mesh = w[0].function_space().mesh() V = VectorFunctionSpace(mesh, 'DG', k) W = FunctionSpace(mesh, 'DGT', k) n = FacetNormal(mesh) v1 = TestFunction(W) u = TrialFunction(V) # The same fluxes that are used in the incompressibility equation if incompressibility_flux_type == 'central': u_hat_dS = dolfin.avg(w) elif incompressibility_flux_type == 'upwind': w_nU = (dot(w, n) + abs(dot(w, n))) / 2.0 switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0) u_hat_dS = switch * w('+') + (1 - switch) * w('-') if D12 is not None: u_hat_dS += dolfin.Constant([D12, D12]) * dolfin.jump(w, n) # Equation 1 - flux through the sides a = L = 0 for R in '+-': a += dot(u(R), n(R)) * v1(R) * dS L += dot(u_hat_dS, n(R)) * v1(R) * dS # Eq. 1 cont. - flux through external boundaries a += dot(u, n) * v1 * ds if use_bcs: for d in range(gdim): dirichlet_bcs = sim.data['dirichlet_bcs']['u%d' % d] neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, []) robin_bcs = sim.data['robin_bcs'].get('u%d' % d, []) outlet_bcs = sim.data['outlet_bcs'] for dbc in dirichlet_bcs: u_bc = dbc.func() L += u_bc * n[d] * v1 * dbc.ds() for nbc in neumann_bcs + robin_bcs + outlet_bcs: if nbc.enforce_zero_flux: pass # L += 0 else: L += w[d] * n[d] * v1 * nbc.ds() for sbc in sim.data['slip_bcs'].get('u', []): pass # L += 0 else: L += dot(w, n) * v1 * ds # Equation 2 - internal shape : empty for DG1 # Equation 3 - BDM Phi : empty for DG1 return a, L, V
def _setup_dg2_projection_2D(self, w, incompressibility_flux_type, D12, use_bcs): """ Implement the projection where the result is BDM embeded in a DG2 function """ sim = self.simulation k = 2 gdim = 2 mesh = w[0].function_space().mesh() V = VectorFunctionSpace(mesh, 'DG', k) n = FacetNormal(mesh) # The mixed function space of the projection test functions e1 = FiniteElement('DGT', mesh.ufl_cell(), k) e2 = VectorElement('DG', mesh.ufl_cell(), k - 2) e3 = FiniteElement('Bubble', mesh.ufl_cell(), 3) em = MixedElement([e1, e2, e3]) W = FunctionSpace(mesh, em) v1, v2, v3b = TestFunctions(W) u = TrialFunction(V) # The same fluxes that are used in the incompressibility equation if incompressibility_flux_type == 'central': u_hat_dS = dolfin.avg(w) elif incompressibility_flux_type == 'upwind': w_nU = (dot(w, n) + abs(dot(w, n))) / 2.0 switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0) u_hat_dS = switch * w('+') + (1 - switch) * w('-') if D12 is not None: u_hat_dS += dolfin.Constant([D12, D12]) * dolfin.jump(w, n) # Equation 1 - flux through the sides a = L = 0 for R in '+-': a += dot(u(R), n(R)) * v1(R) * dS L += dot(u_hat_dS, n(R)) * v1(R) * dS # Eq. 1 cont. - flux through external boundaries a += dot(u, n) * v1 * ds if use_bcs: for d in range(gdim): dirichlet_bcs = sim.data['dirichlet_bcs']['u%d' % d] neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, []) robin_bcs = sim.data['robin_bcs'].get('u%d' % d, []) outlet_bcs = sim.data['outlet_bcs'] for dbc in dirichlet_bcs: u_bc = dbc.func() L += u_bc * n[d] * v1 * dbc.ds() for nbc in neumann_bcs + robin_bcs + outlet_bcs: if nbc.enforce_zero_flux: pass # L += 0 else: L += w[d] * n[d] * v1 * nbc.ds() for sbc in sim.data['slip_bcs'].get('u', []): pass # L += 0 else: L += dot(w, n) * v1 * ds # Equation 2 - internal shape a += dot(u, v2) * dx L += dot(w, v2) * dx # Equation 3 - BDM Phi v3 = as_vector([v3b.dx(1), -v3b.dx(0)]) # Curl of [0, 0, v3b] a += dot(u, v3) * dx L += dot(w, v3) * dx return a, L, V
def apply_stabilization(self): df.dS = self.dS #Important <---- h_msh = df.CellDiameter(self.mesh) h_avg = (h_msh("+") + h_msh("-")) / 2.0 #Output local_stab = 0.0 if self.stab_type == 'cip': if self.moment_order == 3 or self.moment_order == 'ns': local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[0]),self.nv)\ * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for temp if self.moment_order == 6: local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\ * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\ * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\ * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y if self.moment_order == 13: local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[3]),self.nv)\ * df.jump(df.grad(self.v[3]),self.nv) * df.dS #cip for temp local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\ * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\ * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\ * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y if self.moment_order == 'grad13': local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[3]),self.nv)\ * df.jump(df.grad(self.v[6]),self.nv) * df.dS #cip for temp local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\ * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\ * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\ * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y elif self.stab_type == 'gls': local_stab = (0.0001* h_msh * df.inner(self.SA_x * df.Dx(self.u,0)\ + self.SA_y * df.Dx(self.u,1) + self.SP_Coeff * self.u, self.SA_x * df.Dx(self.v,0)\ + self.SA_y * df.Dx(self.v,1) + self.SP_Coeff * self.v ) * df.dx) return local_stab
phi = TrialFunction(Vx) psi = TestFunction(Vx) v = Function(Vx) gamma = 1.0 # Jump penalty term stab1 = 2. nE = FacetNormal(mx) hE = FacetArea(mx) test = div(grad(psi)) S_ = gamma * inner(ax, grad(grad(phi))) * test * dx(mx) \ + stab1 * avg(hE)**(-1) * inner(jump(grad(phi), nE), jump(grad(psi), nE)) * dS(mx) \ + gamma * inner(bx, grad(phi)) * test * dx(mx) \ + 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) bc_Vx = DirichletBC(Vx, g, 'on_boundary') S = assemble(S_) M = assemble(M_) # Prepare special treatment of deterministic part and time-derivative. xdofs = Vx.tabulate_dof_coordinates().flatten() ix = np.argsort(xdofs)
def delta_dg(mesh,expr): V = df.FunctionSpace(mesh, "DG", 1) m = df.interpolate(expr, V) n = df.FacetNormal(mesh) h = df.CellSize(mesh) h_avg = (h('+') + h('-'))/2.0 alpha = 1.0 gamma = 0 u = df.TrialFunction(V) v = df.TestFunction(V) a = df.dot(df.grad(v), df.grad(u))*df.dx \ - df.dot(df.avg(df.grad(v)), df.jump(u, n))*df.dS \ - df.dot(df.jump(v, n), df.avg(df.grad(u)))*df.dS \ + alpha/h_avg*df.dot(df.jump(v, n), df.jump(u, n))*df.dS #- df.dot(df.grad(v), u*n)*df.ds \ #- df.dot(v*n, df.grad(u))*df.ds \ #+ gamma/h*v*u*df.ds #a = 1.0/h_avg*df.dot(df.jump(v, n), df.jump(u, n))*df.dS #- df.dot(df.grad(v), u*n)*df.ds \ #- df.dot(v*n, df.grad(u))*df.ds \ #+ gamma/h*v*u*df.ds """ a1 = df.dot(df.grad(v), df.grad(u))*df.dx a2 = df.dot(df.avg(df.grad(v)), df.jump(u, n))*df.dS a3 = df.dot(df.jump(v, n), df.avg(df.grad(u)))*df.dS a4 = alpha/h_avg*df.dot(df.jump(v, n), df.jump(u, n))*df.dS a5 = df.dot(df.grad(v), u*n)*df.ds a6 = df.dot(v*n, df.grad(u))*df.ds a7 = alpha/h*v*u*df.ds printaa(a1,'a1') printaa(a2,'a2') printaa(a3,'a3') printaa(a4,'a4') printaa(a5,'a5') printaa(a6,'a6') printaa(a7,'a7') """ K = df.assemble(a).array() L = df.assemble(v * df.dx).array() h = -np.dot(K,m.vector().array())/L fun = df.Function(V) fun.vector().set_local(h) DG1 = df.FunctionSpace(mesh, "DG", 1) CG1 = df.FunctionSpace(mesh, "CG", 1) #fun = df.interpolate(fun, DG1) fun = df.project(fun, CG1) dim = mesh.topology().dim() res = [] if dim == 1: for x in xs: res.append(fun(x)) elif dim == 2: df.plot(fun) df.interactive() for x in xs: res.append(fun(x,0.5)) elif dim == 3: for x in xs: res.append(fun(x,0.5,0.5)) return res
# Term stemming from grad(T) da1_top -= inner(dot(grad(s_top), grad(T)), grad(lmb)) * dX # Term stemming from grad(lmb) da1_top -= inner(grad(T), dot(grad(s_top), grad(lmb))) * dX # Classic shape derivative term bottom mesh da1_bottom = div(s_bottom) * inner(grad(T), grad(lmb)) * dX # Term stemming from grad(T) da1_bottom += inner(grad(dot(s_bottom, grad(T))), grad(lmb)) * dX # Term stemming from grad(lmb) da1_bottom += inner(grad(T), grad(dot(s_bottom, grad(lmb)))) * dX # Material derivative of background T da1_bottom -= inner(dot(nabla_grad(s_bottom), grad(T)), grad(lmb)) * dX # Material derivative of background lmb da1_bottom -= inner(grad(T), dot(nabla_grad(s_bottom), grad(lmb))) * dX #---------------------------------------------------------------------------- a2 = -dot(avg(grad(T)), jump(lmb, n)) * dI # Classic shape derivative at interface da2 = -0.5*tan_div(s_top("-"), n("-"))*inner(n("-"),grad(T("-"))+grad(T("+")))\ *(lmb("-")-lmb("+"))*dI # Due to normal variation da2 -= 0.5*inner(dn_mat(s_top("-"), n("-")), grad(T("-")) + grad(T("+")))*\ (lmb("-")-lmb("+"))*dI # Due to grad(T) da2 += 0.5 * inner( n("-"), dot(nabla_grad(s_top("-")), nabla_grad(T("-")) + nabla_grad(T("+"))) * (lmb("-") - lmb("+"))) * dI # Material derivative of background grad(T) da2 -= 0.5 * inner(n("-"), grad(dot(s_top("-"), grad( T("+"))))) * (lmb("-") - lmb("+")) * dI # Material derivative of background lmb
def define_advection_equation(self): """ Setup the advection equation for the colour function This implementation assembles the full LHS and RHS each time they are needed """ sim = self.simulation mesh = sim.data['mesh'] n = dolfin.FacetNormal(mesh) dS, dx = dolfin.dS(mesh), dolfin.dx(mesh) # Trial and test functions Vc = self.Vc c = dolfin.TrialFunction(Vc) d = dolfin.TestFunction(Vc) c1, c2, c3 = self.time_coeffs dt = self.dt u_conv = self.u_conv if not self.colour_is_discontinuous: # Continous Galerkin implementation of the advection equation # FIXME: add stabilization eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx + div( c * u_conv) * d * dx elif self.velocity_is_trace: # Upstream and downstream normal velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2 if self.beta is not None: # Define the blended flux # The blending factor beta is not DG, so beta('+') == beta('-') b = self.beta('+') flux = (1 - b) * jump(c * w_nU) + b * jump(c * w_nD) else: flux = jump(c * w_nU) # Discontinuous Galerkin implementation of the advection equation eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx + flux * jump(d) * dS # On each facet either w_nD or w_nU will be 0, the other is multiplied # with the appropriate flux, either the value c going out of the domain # or the Dirichlet value coming into the domain for dbc in self.dirichlet_bcs: eq += w_nD * dbc.func() * d * dbc.ds() eq += w_nU * c * d * dbc.ds() elif self.beta is not None: # Upstream and downstream normal velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2 if self.beta is not None: # Define the blended flux # The blending factor beta is not DG, so beta('+') == beta('-') b = self.beta('+') flux = (1 - b) * jump(c * w_nU) + b * jump(c * w_nD) else: flux = jump(c * w_nU) # Discontinuous Galerkin implementation of the advection equation eq = ((c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx - dot(c * u_conv, grad(d)) * dx + flux * jump(d) * dS) # Enforce Dirichlet BCs weakly for dbc in self.dirichlet_bcs: eq += w_nD * dbc.func() * d * dbc.ds() eq += w_nU * c * d * dbc.ds() else: # Downstream normal velocities w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2 # Discontinuous Galerkin implementation of the advection equation eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx # Convection integrated by parts two times to bring back the original # div form (this means we must subtract and add all fluxes) eq += div(c * u_conv) * d * dx # Replace downwind flux with upwind flux on downwind internal facets eq -= jump(w_nD * d) * jump(c) * dS # Replace downwind flux with upwind BC flux on downwind external facets for dbc in self.dirichlet_bcs: # Subtract the "normal" downwind flux eq -= w_nD * c * d * dbc.ds() # Add the boundary value upwind flux eq += w_nD * dbc.func() * d * dbc.ds() # Penalty forcing zones for fz in self.forcing_zones: eq += fz.penalty * fz.beta * (c - fz.target) * d * dx a, L = dolfin.system(eq) self.form_lhs = dolfin.Form(a) self.form_rhs = dolfin.Form(L) self.tensor_lhs = None self.tensor_rhs = None
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
def transport_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, \ d_0, d_1, d_t, vel_c, p_con, A_0, Temp, c_extrapolate): # 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) C_cg = FiniteElement("CG", mesh.ufl_cell(), 1) C_dg = FiniteElement("DG", mesh.ufl_cell(), 0) mini = C_cg + C_dg C = FunctionSpace(mesh, mini) C = BlockFunctionSpace([C]) 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)) penalty1 = Constant(1.0) tau = Function(PM) tau = tau_cal(tau, phi, -0.5) tuning_para = 0.25 vel_norm = (dot(vel_c, n) + abs(dot(vel_c, n))) / 2.0 cell_size = CellDiameter(mesh) vnorm = sqrt(dot(vel_c, vel_c)) I = Identity(mesh.topology().dim()) d_eff = Function(TM) d_eff = diff_coeff_cal_rev(d_eff, d_0, tau, phi) + tuning_para * cell_size * vnorm * I monitor_dt = dt # Define variational problem dc, = BlockTrialFunction(C) dc_dot, = BlockTrialFunction(C) psic, = BlockTestFunction(C) block_c = BlockFunction(C) c, = block_split(block_c) block_c_dot = BlockFunction(C) c_dot, = block_split(block_c_dot) theta = -1.0 a_time = phi * rho * inner(c_dot, psic) * dx a_dif = dot(rho*d_eff*grad(c),grad(psic))*dx \ - dot(avg_w(rho*d_eff*grad(c),weight_e(rho*d_eff,n)), jump(psic, n))*dS \ + theta*dot(avg_w(rho*d_eff*grad(psic),weight_e(rho*d_eff,n)), jump(c, n))*dS \ + penalty1/h_avg*k_e(rho*d_eff,n)*dot(jump(c, n), jump(psic, n))*dS a_adv = -dot(rho*vel_c*c,grad(psic))*dx \ + dot(jump(psic), rho('+')*vel_norm('+')*c('+') - rho('-')*vel_norm('-')*c('-') )*dS \ + dot(psic, rho*vel_norm*c)*ds(3) R_c = R_c_cal(c_extrapolate, p_con, Temp) c_D1 = Constant(0.5) rhs_c = R_c * A_s_cal(phi, phi_0, A_0) * psic * dx - dot( rho * phi * vel_c, n) * c_D1 * psic * ds(1) r_u = [a_dif + a_adv] j_u = block_derivative(r_u, [c], [dc]) r_u_dot = [a_time] j_u_dot = block_derivative(r_u_dot, [c_dot], [dc_dot]) r = [r_u_dot[0] + r_u[0] - rhs_c] # this part is not applied. exact_solution_expression1 = Expression("1.0", t=0, element=C[0].ufl_element()) def bc(t): p5 = DirichletBC(C.sub(0), exact_solution_expression1, boundaries, 1, method="geometric") return BlockDirichletBC([p5]) # Define problem wrapper class ProblemWrapper(object): def set_time(self, t): pass # Residual and jacobian functions def residual_eval(self, t, solution, solution_dot): 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] ]] # Define boundary condition def bc_eval(self, t): pass # Define initial condition def ic_eval(self): return solution0 # Define custom monitor to plot the solution def monitor(self, t, solution, solution_dot): pass problem_wrapper = ProblemWrapper() (solution, solution_dot) = (block_c, block_c_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
def on_simulation_start(self): """ This runs when the simulation starts. It does not run in __init__ since the N-S solver needs the density and viscosity we define, and we need the velocity that is defined by the solver """ if self.use_analytical_solution: return sim = self.simulation dirichlet_bcs = sim.data['dirichlet_bcs'].get('rho', []) if self.use_rk_method: V = self.simulation.data['Vrho'] if not V.ufl_element().family() == 'Discontinuous Lagrange': ocellaris_error( 'VariableDensity timestepping error', 'Can only use explicit SSP Runge-Kutta method with DG space for rho', ) Vu = sim.data['Vu'] u_conv, self.funcs_to_extrapolate = [], [] for d in range(sim.ndim): ux = Function(Vu) up = sim.data['up_conv%d' % d] upp = sim.data['upp_conv%d' % d] self.funcs_to_extrapolate.append((ux, up, upp)) u_conv.append(ux) u_conv = dolfin.as_vector(u_conv) from dolfin import dot, div, jump, dS mesh = self.simulation.data['mesh'] re = self.rho_explicit = dolfin.Function(V) c, d = dolfin.TrialFunction(V), dolfin.TestFunction(V) n = dolfin.FacetNormal(mesh) w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2 dx = dolfin.dx(domain=mesh) eq = c * d * dx # Convection integrated by parts two times to bring back the original # div form (this means we must subtract and add all fluxes) eq += div(re * u_conv) * d * dx # Replace downwind flux with upwind flux on downwind internal facets eq -= jump(w_nD * d) * jump(re) * dS # Replace downwind flux with upwind BC flux on downwind external facets for dbc in dirichlet_bcs: # Subtract the "normal" downwind flux eq -= w_nD * re * d * dbc.ds() # Add the boundary value upwind flux eq += w_nD * dbc.func() * d * dbc.ds() a, L = dolfin.system(eq) self.rk = RungeKuttaDGTimestepping( self.simulation, a, L, self.rho, self.rho_explicit, 'rho', order=None, explicit_funcs=self.funcs_to_extrapolate, bcs=dirichlet_bcs, ) else: # Use backward Euler (BDF1) for timestep 1 self.time_coeffs = Constant([1, -1, 0]) if dolfin.norm(self.rho_pp.vector()) > 0: # Use BDF2 from the start self.time_coeffs.assign(Constant([3 / 2, -2, 1 / 2])) self.simulation.log.info( 'Using second order timestepping from the start in VariableDensity' ) # Define equation for advection of the density # ∂ρ/∂t + ∇⋅(ρ u) = 0 beta = None u_conv = sim.data['u_conv'] forcing_zones = sim.data['forcing_zones'].get('rho', []) self.eq = AdvectionEquation( sim, sim.data['Vrho'], self.rho_p, self.rho_pp, u_conv, beta, self.time_coeffs, dirichlet_bcs, forcing_zones, ) self.solver = linear_solver_from_input(sim, 'solver/rho', SOLVER, PRECONDITIONER, None, KRYLOV_PARAMETERS) self.slope_limiter = SlopeLimiter(sim, 'rho', self.rho) self.slope_limiter.set_phi_old(self.rho_p) # Make sure the initial value is included in XDMF results from timestep 0 self.rho.assign(self.rho_p)
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 _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
R_c = (H_c - Hmid) * xsi_c * df.dx #################################################################### ########################## MASS BALANCE ########################## #################################################################### # Solve the transport equation using DG0 finite elements which are # first order accurate but unconditionally TVD and positivity-preserving, # and also conserves mass perfectly. # Grid velocity v = dLdt * x_spatial[0] # Inter element flux (upwind) uH = df.avg((ubar - v)) * df.avg(Hmid * width) + 0.5 * abs( df.avg(width * (ubar - v))) * df.jump(Hmid * width) # Residual of the transport equation with a zero-flux upstream boundary. R_mass = (Lmid * width * dHdt * xsi + Lmid * Hmid * dWdt * xsi + Hmid * width * dLdt * xsi - xsi.dx(0) * (ubar - v) * width * Hmid - Lmid * width * adot * xsi) * df.dx + uH * df.jump(xsi) * df.dS + ( U[0] - v) * Hmid * width * xsi * df.ds(1) #################################################################### ######################### LENGTH MODEL ########################### #################################################################### # Calving thickness (when thickness is less than this, calving begins) # This is imposed strongly when using the local length evolution equation H_calving = (1 + df.Constant(q)) * D * rho_w / rho # Calving velocity proportional to the degree to which the calving thickness
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 define_coupled_equation(self): """ Setup the coupled Navier-Stokes equation This implementation assembles the full LHS and RHS each time they are needed """ sim = self.simulation mpm = sim.multi_phase_model mesh = sim.data['mesh'] Vcoupled = sim.data['Vcoupled'] u_conv = sim.data['u_conv'] # Unpack the coupled trial and test functions uc = dolfin.TrialFunction(Vcoupled) vc = dolfin.TestFunction(Vcoupled) ulist = [] vlist = [] ndim = self.simulation.ndim sigma, tau = [], [] for d in range(ndim): ulist.append(uc[d]) vlist.append(vc[d]) indices = list(range(1 + ndim * (d + 1), 1 + ndim * (d + 2))) sigma.append(dolfin.as_vector([uc[i] for i in indices])) tau.append(dolfin.as_vector([vc[i] for i in indices])) u = dolfin.as_vector(ulist) v = dolfin.as_vector(vlist) p = uc[ndim] q = vc[ndim] c1, c2, c3 = sim.data['time_coeffs'] dt = sim.data['dt'] g = sim.data['g'] n = dolfin.FacetNormal(mesh) # Fluid properties rho = mpm.get_density(0) mu = mpm.get_laminar_dynamic_viscosity(0) # Upwind and downwind velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2.0 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2.0 # LDG penalties C11 = Constant(1.0 + sim.ndim) switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), n('+'), n('-')) C12 = 0.5 * switch # Start building the coupled equations eq = 0 # Momentum equations for d in range(sim.ndim): up = sim.data['up%d' % d] upp = sim.data['upp%d' % d] # Divergence free criterion # ∇⋅u = 0 u_hat_p = avg(u[d]) if self.use_grad_q_form: eq -= u[d] * q.dx(d) * dx eq += u_hat_p * jump(q) * n[d]('+') * dS else: eq += q * u[d].dx(d) * dx eq -= avg(q) * jump(u[d]) * n[d]('+') * dS # Time derivative # ∂(ρu)/∂t eq += rho * (c1 * u[d] + c2 * up + c3 * upp) / dt * v[d] * dx # Convection: # -w⋅∇(ρu) flux_nU = u[d] * w_nU flux = jump(flux_nU) eq -= u[d] * dot(grad(rho * v[d]), u_conv) * dx eq += flux * jump(rho * v[d]) * dS # Diffusion: # -∇⋅∇u u_hat_dS = avg(u[d]) - dot(C12, jump(u[d], n)) sigma_hat_dS = avg( sigma[d]) - C11 * jump(u[d], n) + C12 * jump(sigma[d], n) eq += dot(sigma[d], tau[d]) * dx eq += u[d] * div(mu * tau[d]) * dx eq -= u_hat_dS * jump(mu * tau[d], n) * dS eq += dot(sigma[d], grad(v[d])) * dx eq -= dot(sigma_hat_dS, jump(v[d], n)) * dS # Pressure # ∇p if self.use_grad_p_form: eq += v[d] * p.dx(d) * dx eq -= avg(v[d]) * jump(p) * n[d]('+') * dS else: eq -= p * v[d].dx(d) * dx eq += avg(p) * jump(v[d]) * n[d]('+') * dS # Body force (gravity) # ρ g eq -= rho * g[d] * v[d] * dx # Other sources for f in sim.data['momentum_sources']: eq -= f[d] * v[d] * dx # Dirichlet boundary dirichlet_bcs = sim.data['dirichlet_bcs'].get('u%d' % d, []) for dbc in dirichlet_bcs: u_bc = dbc.func() # Divergence free criterion if self.use_grad_q_form: eq += q * u_bc * n[d] * dbc.ds() else: eq -= q * u[d] * n[d] * dbc.ds() eq += q * u_bc * n[d] * dbc.ds() # Convection eq += rho * u[d] * w_nU * v[d] * dbc.ds() eq += rho * u_bc * w_nD * v[d] * dbc.ds() # Diffusion u_hat_ds = u_bc sigma_hat_ds = sigma[d] - C11 * (u[d] - u_bc) * n eq -= u_hat_ds * mu * dot(tau[d], n) * dbc.ds() eq -= dot(sigma_hat_ds, n) * v[d] * dbc.ds() # Pressure if not self.use_grad_p_form: eq += p * v[d] * n[d] * dbc.ds() # Neumann boundary neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, []) for nbc in neumann_bcs: # Divergence free criterion if self.use_grad_q_form: eq += q * u[d] * n[d] * nbc.ds() else: eq -= q * u[d] * n[d] * nbc.ds() # Convection eq += rho * u[d] * w_nU * v[d] * nbc.ds() # Diffusion u_hat_ds = u[d] sigma_hat_ds = nbc.func() / mu * n eq -= u_hat_ds * mu * dot(tau[d], n) * nbc.ds() eq -= dot(sigma_hat_ds, n) * v[d] * nbc.ds() # Pressure if not self.use_grad_p_form: eq += p * v[d] * n[d] * nbc.ds() a, L = dolfin.system(eq) self.form_lhs = a self.form_rhs = L self.tensor_lhs = None self.tensor_rhs = None
def define_dg_equations( u, v, p, q, lm_trial, lm_test, simulation, include_hydrostatic_pressure, incompressibility_flux_type, use_grad_q_form, use_grad_p_form, use_stress_divergence_form, velocity_continuity_factor_D12=0, pressure_continuity_factor=0, ): """ Define the coupled equations. Also used by the SIMPLE and IPCS-A solvers Weak form of the Navier-Stokes eq. with discontinuous elements :type simulation: ocellaris.Simulation """ sim = simulation show = sim.log.info show(' Creating DG weak form with BCs') show(' include_hydrostatic_pressure = %r' % include_hydrostatic_pressure) show(' incompressibility_flux_type = %s' % incompressibility_flux_type) show(' use_grad_q_form = %r' % use_grad_q_form) show(' use_grad_p_form = %r' % use_grad_p_form) show(' use_stress_divergence_form = %r' % use_stress_divergence_form) show(' velocity_continuity_factor_D12 = %r' % velocity_continuity_factor_D12) show(' pressure_continuity_factor = %r' % pressure_continuity_factor) mpm = sim.multi_phase_model mesh = sim.data['mesh'] u_conv = sim.data['u_conv'] dx = dolfin.dx(domain=mesh) dS = dolfin.dS(domain=mesh) c1, c2, c3 = sim.data['time_coeffs'] dt = sim.data['dt'] g = sim.data['g'] n = dolfin.FacetNormal(mesh) x = dolfin.SpatialCoordinate(mesh) # Fluid properties rho = mpm.get_density(0) nu = mpm.get_laminar_kinematic_viscosity(0) mu = mpm.get_laminar_dynamic_viscosity(0) # Hydrostatic pressure correction if include_hydrostatic_pressure: p += sim.data['p_hydrostatic'] # Start building the coupled equations eq = 0 # ALE mesh velocities if sim.mesh_morpher.active: u_mesh = sim.data['u_mesh'] # Either modify the convective velocity or just include the mesh # velocity on cell integral form. Only activate one of the lines below # PS: currently the UFL form splitter (used in e.g. IPCS-A) has a # problem with line number 2, but the Coupled solver handles # both options with approximately the same resulting convergence # errors on the Taylor-Green test case (TODO: fix form splitter) u_conv -= u_mesh # eq -= dot(div(rho * dolfin.outer(u, u_mesh)), v) * dx # Divergence of u should balance expansion/contraction of the cell K # ∇⋅u = -∂x/∂t (See below for definition of the ∇⋅u term) # THIS IS SOMEWHAT EXPERIMENTAL cvol_new = dolfin.CellVolume(mesh) cvol_old = sim.data['cvolp'] eq += (cvol_new - cvol_old) / dt * q * dx # Elliptic penalties penalty_dS, penalty_ds, D11, D12 = navier_stokes_stabilization_penalties( sim, nu, velocity_continuity_factor_D12, pressure_continuity_factor) yh = 1 / penalty_ds # Upwind and downwind velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2.0 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2.0 # Lagrange multiplicator to remove the pressure null space # ∫ p dx = 0 if lm_trial is not None: eq = (p * lm_test + q * lm_trial) * dx # Momentum equations for d in range(sim.ndim): up = sim.data['up%d' % d] upp = sim.data['upp%d' % d] # Divergence free criterion # ∇⋅u = 0 if incompressibility_flux_type == 'central': u_hat_p = avg(u[d]) elif incompressibility_flux_type == 'upwind': assert use_grad_q_form, 'Upwind only implemented for grad_q_form' switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0) u_hat_p = switch * u[d]('+') + (1 - switch) * u[d]('-') if use_grad_q_form: eq -= u[d] * q.dx(d) * dx eq += (u_hat_p + D12[d] * jump(u, n)) * jump(q) * n[d]('+') * dS else: eq += q * u[d].dx(d) * dx eq -= (avg(q) - dot(D12, jump(q, n))) * jump(u[d]) * n[d]('+') * dS # Time derivative # ∂(ρu)/∂t eq += rho * (c1 * u[d] + c2 * up + c3 * upp) / dt * v[d] * dx # Convection: # -w⋅∇(ρu) flux_nU = u[d] * w_nU flux = jump(flux_nU) eq -= u[d] * dot(grad(rho * v[d]), u_conv) * dx eq += flux * jump(rho * v[d]) * dS # Stabilizing term when w is not divergence free eq += 1 / 2 * div(u_conv) * u[d] * v[d] * dx # Diffusion: # -∇⋅μ∇u eq += mu * dot(grad(u[d]), grad(v[d])) * dx # Symmetric Interior Penalty method for -∇⋅μ∇u eq -= avg(mu) * dot(n('+'), avg(grad(u[d]))) * jump(v[d]) * dS eq -= avg(mu) * dot(n('+'), avg(grad(v[d]))) * jump(u[d]) * dS # Symmetric Interior Penalty coercivity term eq += penalty_dS * jump(u[d]) * jump(v[d]) * dS # -∇⋅μ(∇u)^T if use_stress_divergence_form: eq += mu * dot(u.dx(d), grad(v[d])) * dx eq -= avg(mu) * dot(n('+'), avg(u.dx(d))) * jump(v[d]) * dS eq -= avg(mu) * dot(n('+'), avg(v.dx(d))) * jump(u[d]) * dS # Pressure # ∇p if use_grad_p_form: eq += v[d] * p.dx(d) * dx eq -= (avg(v[d]) + D12[d] * jump(v, n)) * jump(p) * n[d]('+') * dS else: eq -= p * v[d].dx(d) * dx eq += (avg(p) - dot(D12, jump(p, n))) * jump(v[d]) * n[d]('+') * dS # Pressure continuity stabilization. Needed for equal order discretization if D11 is not None: eq += D11 * dot(jump(p, n), jump(q, n)) * dS # Body force (gravity) # ρ g eq -= rho * g[d] * v[d] * dx # Other sources for f in sim.data['momentum_sources']: eq -= f[d] * v[d] * dx # Penalty forcing zones for fz in sim.data['forcing_zones'].get('u', []): eq += fz.penalty * fz.beta * (u[d] - fz.target[d]) * v[d] * dx # Boundary conditions that do not couple the velocity components # The BCs are imposed for each velocity component u[d] separately eq += add_dirichlet_bcs(sim, d, u, p, v, q, rho, mu, n, w_nU, w_nD, penalty_ds, use_grad_q_form, use_grad_p_form) eq += add_neumann_bcs(sim, d, u, p, v, q, rho, mu, n, w_nU, w_nD, penalty_ds, use_grad_q_form, use_grad_p_form) eq += add_robin_bcs(sim, d, u, p, v, q, rho, mu, n, w_nU, w_nD, yh, use_grad_q_form, use_grad_p_form) eq += add_outlet_bcs(sim, d, u, p, v, q, rho, mu, n, w_nU, w_nD, g, x, use_grad_q_form, use_grad_p_form) # Boundary conditions that couple the velocity components # Decomposing the velocity into wall normal and parallel parts eq += add_slip_bcs(sim, u, p, v, q, rho, mu, n, w_nU, w_nD, penalty_ds, use_grad_q_form, use_grad_p_form) return eq
def define_coupled_equation(self): """ Setup the coupled Navier-Stokes equation This implementation assembles the full LHS and RHS each time they are needed """ sim = self.simulation mpm = sim.multi_phase_model mesh = sim.data['mesh'] Vcoupled = sim.data['Vcoupled'] u_conv = sim.data['u_conv'] # Unpack the coupled trial and test functions uc = dolfin.TrialFunction(Vcoupled) vc = dolfin.TestFunction(Vcoupled) ulist = [] vlist = [] sigmas, taus = [], [] for d in range(sim.ndim): ulist.append(uc[d]) vlist.append(vc[d]) indices = list( range(1 + sim.ndim * (d + 1), 1 + sim.ndim * (d + 2))) sigmas.append([uc[i] for i in indices]) taus.append([vc[i] for i in indices]) u = dolfin.as_vector(ulist) v = dolfin.as_vector(vlist) p = uc[sim.ndim] q = vc[sim.ndim] sigma = dolfin.as_tensor(sigmas) tau = dolfin.as_tensor(taus) c1, c2, c3 = sim.data['time_coeffs'] dt = sim.data['dt'] g = sim.data['g'] n = dolfin.FacetNormal(mesh) h = dolfin.FacetArea(mesh) # Fluid properties rho = mpm.get_density(0) mu = mpm.get_laminar_dynamic_viscosity(0) # Upwind and downwind velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2.0 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2.0 u_uw_s = dolfin.conditional(dolfin.gt(dot(u_conv, n), 0.0), 1.0, 0.0)('+') u_uw = u_uw_s * u('+') + (1 - u_uw_s) * u('-') # LDG penalties # kappa_0 = Constant(4.0) # kappa = mu*kappa_0/h C11 = avg(mu / h) D11 = avg(h / mu) C12 = 0.2 * n('+') D12 = 0.2 * n('+') def ojump(v, n): return outer(v, n)('+') + outer(v, n)('-') # Interior facet fluxes # u_hat_dS = avg(u) # sigma_hat_dS = avg(sigma) - avg(kappa)*ojump(u, n) # p_hat_dS = avg(p) u_hat_s_dS = avg(u) + dot(ojump(u, n), C12) u_hat_p_dS = avg(u) + D11 * jump(p, n) + D12 * jump(u, n) sigma_hat_dS = avg(sigma) - C11 * ojump(u, n) - outer( jump(sigma, n), C12) p_hat_dS = avg(p) - dot(D12, jump(p, n)) # Time derivative up = sim.data['up'] upp = sim.data['upp'] eq = rho * dot(c1 * u + c2 * up + c3 * upp, v) / dt * dx # LDG equation 1 eq += inner(sigma, tau) * dx eq += dot(u, div(mu * tau)) * dx eq -= dot(u_hat_s_dS, jump(mu * tau, n)) * dS # LDG equation 2 eq += (inner(sigma, grad(v)) - p * div(v)) * dx eq -= (inner(sigma_hat_dS, ojump(v, n)) - p_hat_dS * jump(v, n)) * dS eq -= dot(u, div(outer(v, rho * u_conv))) * dx eq += rho('+') * dot(u_conv('+'), n('+')) * dot(u_uw, v('+')) * dS eq += rho('-') * dot(u_conv('-'), n('-')) * dot(u_uw, v('-')) * dS momentum_sources = sim.data['momentum_sources'] + [rho * g] eq -= dot(sum(momentum_sources), v) * dx # LDG equation 3 eq -= dot(u, grad(q)) * dx eq += dot(u_hat_p_dS, jump(q, n)) * dS # Dirichlet boundary dirichlet_bcs = get_collected_velocity_bcs(sim, 'dirichlet_bcs') for ds, u_bc in dirichlet_bcs.items(): # sigma_hat_ds = sigma - kappa*outer(u, n) sigma_hat_ds = sigma - C11 * outer(u - u_bc, n) u_hat_ds = u_bc p_hat_ds = p # LDG equation 1 eq -= dot(u_hat_ds, dot(mu * tau, n)) * ds # LDG equation 2 eq -= (inner(sigma_hat_ds, outer(v, n)) - p_hat_ds * dot(v, n)) * ds eq += rho * w_nU * dot(u, v) * ds eq += rho * w_nD * dot(u_bc, v) * ds # LDG equation 3 eq += dot(u_hat_ds, q * n) * ds # Neumann boundary neumann_bcs = get_collected_velocity_bcs(sim, 'neumann_bcs') assert not neumann_bcs for ds, du_bc in neumann_bcs.items(): # Divergence free criterion if self.use_grad_q_form: eq += q * dot(u, n) * ds else: eq -= q * dot(u, n) * ds # Convection eq += rho * w_nU * dot(u, v) * ds # Diffusion u_hat_ds = u sigma_hat_ds = outer(du_bc, n) / mu eq -= dot(u_hat_ds, dot(mu * tau, n)) * ds eq -= inner(sigma_hat_ds, outer(v, n)) * ds # Pressure if not self.use_grad_p_form: eq += p * dot(v, n) * ds a, L = dolfin.system(eq) self.form_lhs = a self.form_rhs = L self.tensor_lhs = None self.tensor_rhs = None