def get_strandberg_I(self): u = self.unit_registry h = u('1 planck_constant') c = u('1 speed_of_light') kT = self.pdd.kT mu = self.pdd.mesh_util n_r = 1 K = (8 * dolfin.pi * n_r **2) / (h**3 * c**2) E_L, E_U = self.get_absorption_bounds() # equivalent to flipping integration bounds if E_U < E_L sign = dolfin.conditional(dolfin.gt( E_U.magnitude, E_L.m_as(E_U.units)), +1, -1) # antiderivative of `E^2 exp(E/kT)` def F(E): return -kT*(2*kT**2 + 2*E*kT + E**2) * mu.exp(-E/kT) I = F(E_U) - F(E_L) ans = K * I * sign # to evaluate integrals # ans = (ans.m((0.0, 0.0)) * ans.units).to("1 / centimeter ** 2 / second") # print(self.src_band.key, self.dst_band.key, ans) return ans
def nu_PWC_arit(phi, cc): A = df.conditional(df.gt(phi, 0.975), 1.0, 0.0) B = df.conditional(df.lt(phi, 0.025), 1.0, 0.0) nu = A * cc[r"\nu_1"]\ + B * cc[r"\nu_2"]\ + (1.0 - A - B) * 0.5 * (cc[r"\nu_1"] + cc[r"\nu_2"]) return nu
def nu_PWC_harm(phi, cc): A = df.conditional(df.gt(phi, 0.975), 1.0, 0.0) B = df.conditional(df.lt(phi, 0.025), 1.0, 0.0) nu = A * cc[r"\nu_1"]\ + B * cc[r"\nu_2"]\ + (1.0 - A - B) * 2.0 / (1.0 / cc[r"\nu_1"] + 1.0 / cc[r"\nu_2"]) return nu
def main(): fsr = FunctionSubspaceRegistry() deg = 2 mesh = dolfin.UnitSquareMesh(100, 3) muc = mesh.ufl_cell() el_w = dolfin.FiniteElement('DG', muc, deg - 1) el_j = dolfin.FiniteElement('BDM', muc, deg) el_DG0 = dolfin.FiniteElement('DG', muc, 0) el = dolfin.MixedElement([el_w, el_j]) space = dolfin.FunctionSpace(mesh, el) DG0 = dolfin.FunctionSpace(mesh, el_DG0) fsr.register(space) facet_normal = dolfin.FacetNormal(mesh) xyz = dolfin.SpatialCoordinate(mesh) trial = dolfin.Function(space) test = dolfin.TestFunction(space) w, c = dolfin.split(trial) v, phi = dolfin.split(test) sympy_exprs = derive_exprs() exprs = { k: sympy_dolfin_printer.to_ufl(sympy_exprs['R'], mesh, v) for k, v in sympy_exprs['quantities'].items() } f = exprs['f'] w0 = dolfin.project(dolfin.conditional(dolfin.gt(xyz[0], 0.5), 1.0, 0.3), DG0) w_BC = exprs['w'] dx = dolfin.dx() form = (+v * dolfin.div(c) * dx - v * f * dx + dolfin.exp(w + w0) * dolfin.dot(phi, c) * dx + dolfin.div(phi) * w * dx - (w_BC - w0) * dolfin.dot(phi, facet_normal) * dolfin.ds() - (w0('-') - w0('+')) * dolfin.dot(phi('+'), facet_normal('+')) * dolfin.dS()) solver = NewtonSolver(form, trial, [], parameters=dict(relaxation_parameter=1.0, maximum_iterations=15, extra_iterations=10, relative_tolerance=1e-6, absolute_tolerance=1e-7)) solver.solve() with closing(XdmfPlot("out/qflop_test.xdmf", fsr)) as X: CG1 = dolfin.FunctionSpace(mesh, dolfin.FiniteElement('CG', muc, 1)) X.add('w0', 1, w0, CG1) X.add('w_c', 1, w + w0, CG1) X.add('w_e', 1, exprs['w'], CG1) X.add('f', 1, f, CG1) X.add('cx_c', 1, c[0], CG1) X.add('cx_e', 1, exprs['c'][0], CG1)
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 _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
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 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
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
hdf.read(h_eff0, "h_eff") assigner_s.assign(T, [B0, Qs0, h_s0, h_s_0, h_eff0]) assigner_g.assign(U, [ubar0, udef0, H0, H0_]) # Save the time series hdf = HDF5File(mesh.mpi_comm(), out_file + ".h5", "w") hdf.write(mesh, "mesh") t = t_start # Loop over time while t < t_end: ax[0].set_title(t) try: # If the solvers don't converge, reduce the time step and try again. bmelt = -20.0 bdot = df.conditional(df.gt(H, np.abs(bmelt)), bmelt, -H) * (1 - grounded) P = None if climate in "ltop": P = get_adot_from_orog_precip(ltop_constants) adot.vector().set_local(P) print(t, dt_float, H0.vector().max(), df.assemble(h_s0 * df.dx)) assigner_s.assign(T, [B0, Qs0, h_s0, h_s_0, h_eff0]) assigner_g.assign(U, [ubar0, udef0, H0, H0_]) # Solve for water flux df.solve(A_Qw == b_Qw, Qw) # Solve for sediment variables