def setup(self, state): if not self._initialised: space = state.spaces("Vv") super().setup(state, space=space) rho = state.fields("rho") rhobar = state.fields("rhobar") theta = state.fields("theta") thetabar = state.fields("thetabar") pi = thermodynamics.pi(state.parameters, rho, theta) pibar = thermodynamics.pi(state.parameters, rhobar, thetabar) cp = Constant(state.parameters.cp) n = FacetNormal(state.mesh) F = TrialFunction(space) w = TestFunction(space) a = inner(w, F)*dx L = (- cp*div((theta-thetabar)*w)*pibar*dx + cp*jump((theta-thetabar)*w, n)*avg(pibar)*dS_v - cp*div(thetabar*w)*(pi-pibar)*dx + cp*jump(thetabar*w, n)*avg(pi-pibar)*dS_v) bcs = [DirichletBC(space, 0.0, "bottom"), DirichletBC(space, 0.0, "top")] imbalanceproblem = LinearVariationalProblem(a, L, self.field, bcs=bcs) self.imbalance_solver = LinearVariationalSolver(imbalanceproblem)
def form(u, v): return inner(div(grad(u)), div(grad(v)))*dx \ - inner(avg(div(grad(u))), jump(grad(v), n))*dS \ - inner(jump(grad(u), n), avg(div(grad(v))))*dS \ + alpha/h*inner(jump(grad(u), n), jump(grad(v), n))*dS \ - inner(div(grad(u)), inner(grad(v), n))*ds \ - inner(inner(grad(u), n), div(grad(v)))*ds \ + alpha/h*inner(grad(u), grad(v))*ds
def dgls_form(self, problem, mesh, bcs_p): rho = problem.rho mu = problem.mu k = problem.k f = problem.f q, p = fire.TrialFunctions(self._W) w, v = fire.TestFunctions(self._W) n = fire.FacetNormal(mesh) h = fire.CellDiameter(mesh) # Stabilizing parameters has_mesh_characteristic_length = True delta_0 = fire.Constant(1) delta_1 = fire.Constant(-1 / 2) delta_2 = fire.Constant(1 / 2) delta_3 = fire.Constant(1 / 2) eta_p = fire.Constant(100) eta_q = fire.Constant(100) h_avg = (h("+") + h("-")) / 2.0 if has_mesh_characteristic_length: delta_2 = delta_2 * h * h delta_3 = delta_3 * h * h kappa = rho * k / mu inv_kappa = 1.0 / kappa # Classical mixed terms a = (dot(inv_kappa * q, w) - div(w) * p - delta_0 * v * div(q)) * dx L = -delta_0 * f * v * dx # DG terms a += jump(w, n) * avg(p) * dS - avg(v) * jump(q, n) * dS # Edge stabilizing terms a += (eta_q * h_avg) * avg(inv_kappa) * ( jump(q, n) * jump(w, n)) * dS + (eta_p / h_avg) * avg(kappa) * dot( jump(v, n), jump(p, n)) * dS # Add the contributions of the pressure boundary conditions to L for pboundary, iboundary in bcs_p: L -= pboundary * dot(w, n) * ds(iboundary) # Stabilizing terms a += (delta_1 * inner(kappa * (inv_kappa * q + grad(p)), delta_0 * inv_kappa * w + grad(v)) * dx) a += delta_2 * inv_kappa * div(q) * div(w) * dx a += delta_3 * inner(kappa * curl(inv_kappa * q), curl( inv_kappa * w)) * dx L += delta_2 * inv_kappa * f * div(w) * dx return a, L
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.u0 = Function(self.V) self.u1 = Function(self.V) self.sigma0 = Function(self.S) self.sigma1 = Function(self.S) theta = conditions.theta uh = (1-theta) * self.u0 + theta * self.u1 a = Function(self.U) h = Function(self.U) p = TestFunction(self.V) q = TestFunction(self.S) self.initial_condition((self.u0, conditions.ic['u']), (a, conditions.ic['a']), (h, conditions.ic['h'])) ep_dot = self.strain(grad(uh)) zeta = self.zeta(h, a, self.delta(uh)) eta = zeta * params.e ** (-2) rheology = 2 * eta * ep_dot + (zeta - eta) * tr(ep_dot) * Identity(2) - 0.5 * self.Ice_Strength(h, a) * Identity(2) self.initial_condition((self.sigma0, rheology),(self.sigma1, self.sigma0)) def sigma_next(timestep, zeta, ep_dot, sigma, P): A = 1 + 0.25 * (timestep * params.e ** 2) / params.T B = timestep * 0.125 * (1 - params.e ** 2) / params.T rhs = (1 - (timestep * params.e ** 2) / (4 * params.T)) * sigma - timestep / params.T * ( 0.125 * (1 - params.e ** 2) * tr(sigma) * Identity(2) - 0.25 * P * Identity(2) + zeta * ep_dot) C = (rhs[0, 0] - rhs[1, 1]) / A D = (rhs[0, 0] + rhs[1, 1]) / (A + 2 * B) sigma00 = 0.5 * (C + D) sigma11 = 0.5 * (D - C) sigma01 = rhs[0, 1] sigma = as_matrix([[sigma00, sigma01], [sigma01, sigma11]]) return sigma s = sigma_next(self.timestep, zeta, ep_dot, self.sigma0, self.Ice_Strength(h, a)) sh = (1-theta) * s + theta * self.sigma0 eqn = self.momentum_equation(h, self.u1, self.u0, p, sh, params.rho, uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep, ind=self.ind) tensor_eqn = inner(self.sigma1-s, q) * dx if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] eqn += stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=uh, test=p) bcs = DirichletBC(self.V, conditions.bc['u'], "on_boundary") uprob = NonlinearVariationalProblem(eqn, self.u1, bcs) self.usolver = NonlinearVariationalSolver(uprob, solver_parameters=solver_params.bt_params) sprob = NonlinearVariationalProblem(tensor_eqn, self.sigma1) self.ssolver = NonlinearVariationalSolver(sprob, solver_parameters=solver_params.bt_params)
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.u0 = Function(self.V) self.u1 = Function(self.V) self.h = Function(self.U) self.a = Function(self.U) self.p = TestFunction(self.V) theta = conditions.theta self.uh = (1-theta) * self.u0 + theta * self.u1 ep_dot = self.strain(grad(self.uh)) self.initial_condition((self.u0, conditions.ic['u']),(self.u1, self.u0), (self.a, conditions.ic['a']),(self.h, conditions.ic['h'])) zeta = self.zeta(self.h, self.a, self.delta(self.uh)) eta = zeta * params.e ** -2 sigma = 2 * eta * ep_dot + (zeta - eta) * tr(ep_dot) * Identity(2) - 0.5 * self.Ice_Strength(self.h,self.a) * Identity(2) self.eqn = self.momentum_equation(self.h, self.u1, self.u0, self.p, sigma, params.rho, self.uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep) if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] self.eqn += self.stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=self.uh, test=self.p) self.bcs = DirichletBC(self.V, conditions.bc['u'], "on_boundary")
def advection_term(self, q): if self.state.mesh.topological_dimension() == 3: # <w,curl(u) cross ubar + grad( u.ubar)> # =<curl(u),ubar cross w> - <div(w), u.ubar> # =<u,curl(ubar cross w)> - # <<u_upwind, [[n cross(ubar cross w)cross]]>> both = lambda u: 2 * avg(u) L = (inner(q, curl(cross(self.ubar, self.test))) * dx - inner(both(self.Upwind * q), both(cross(self.n, cross(self.ubar, self.test)))) * self.dS) else: if self.ibp == "once": L = (-inner( self.gradperp(inner(self.test, self.perp(self.ubar))), q) * dx - inner( jump(inner(self.test, self.perp(self.ubar)), self.n), self.perp_u_upwind(q)) * self.dS) else: L = ( (-inner(self.test, div(self.perp(q)) * self.perp(self.ubar))) * dx - inner(jump(inner(self.test, self.perp(self.ubar)), self.n), self.perp_u_upwind(q)) * self.dS + jump( inner(self.test, self.perp(self.ubar)) * self.perp(q), self.n) * self.dS) L -= 0.5 * div(self.test) * inner(q, self.ubar) * dx return L
def residual(self, test, trial, trial_lagged, fields, bcs): u_adv = trial_lagged phi = test n = self.n u = trial F = -dot(u, div(outer(phi, u_adv)))*self.dx for id, bc in bcs.items(): if 'u' in bc: u_in = bc['u'] elif 'un' in bc: u_in = bc['un'] * n # this implies u_t=0 on the inflow else: u_in = zero(self.dim) F += conditional(dot(u_adv, n) < 0, dot(phi, u_in)*dot(u_adv, n), dot(phi, u)*dot(u_adv, n)) * self.ds(id) if not (is_continuous(self.trial_space) and normal_is_continuous(u_adv)): # s=0: u.n(-)<0 => flow goes from '+' to '-' => '+' is upwind # s=1: u.n(-)>0 => flow goes from '-' to '+' => '-' is upwind s = 0.5*(sign(dot(avg(u), n('-'))) + 1.0) u_up = u('-')*s + u('+')*(1-s) F += dot(u_up, (dot(u_adv('+'), n('+'))*phi('+') + dot(u_adv('-'), n('-'))*phi('-'))) * self.dS return -F
def advection_term(self, q): if self.continuity: n = FacetNormal(self.state.mesh) L = (-dot(grad(self.test), self.ubar) * self.qbar * dx + jump(self.ubar * self.test, n) * avg(self.qbar) * self.dS) else: L = self.test * dot(self.ubar, self.state.k) * dot( self.state.k, grad(self.qbar)) * dx return L
def get_laplace(q, phi): h = fd.avg(fd.CellVolume(mesh)) / fd.FacetArea(mesh) mu = eta / h n = fd.FacetNormal(mesh) ad = (-fd.inner(2 * fd.avg(phi * n), fd.avg(fd.grad(q))) - fd.inner(fd.avg(fd.grad(phi)), 2 * fd.avg(q * n)) + mu * fd.inner(2 * fd.avg(phi * n), 2 * fd.avg(q * n))) * fd.dS ad += fd.inner(fd.grad(q), fd.grad(phi)) * fd.dx return ad
def __init__(self, dq1_space): v = TestFunction(dq1_space) # nodes on squeezed facets are not included in dS_v or ds_v # and all other nodes are (for DQ1), so this step gives nonzero for these other nodes self.marker = assemble(avg(v) * dS_v + v * ds_v) # flip this: 1 for squeezed nodes, and 0 for all others self.marker.assign(conditional(self.marker > 1e-12, 0, 1)) self.P0 = FunctionSpace(dq1_space.mesh(), "DG", 0) self.u0 = Function(self.P0, name='averaged squeezed values') self.u1 = Function(dq1_space, name='aux. squeezed values')
def _build_forcing_solver(self, linear): """ Only put forcing terms into the u equation. """ state = self.state self.scaling = Constant(1.0) Vu = state.V[0] W = state.W self.x0 = Function(W) # copy x to here u0, rho0, theta0 = split(self.x0) F = TrialFunction(Vu) w = TestFunction(Vu) self.uF = Function(Vu) Omega = state.Omega cp = state.parameters.cp mu = state.mu n = FacetNormal(state.mesh) pi = exner(theta0, rho0, state) a = inner(w, F) * dx L = self.scaling * ( +cp * div(theta0 * w) * pi * dx # pressure gradient [volume] - cp * jump(w * theta0, n) * avg(pi) * dS_v # pressure gradient [surface] ) if state.parameters.geopotential: Phi = state.Phi L += self.scaling * div(w) * Phi * dx # gravity term else: g = state.parameters.g L -= self.scaling * g * inner(w, state.k) * dx # gravity term if not linear: L -= self.scaling * 0.5 * div(w) * inner(u0, u0) * dx if Omega is not None: L -= self.scaling * inner(w, cross(2 * Omega, u0)) * dx # Coriolis term if mu is not None: self.mu_scaling = Constant(1.0) L -= self.mu_scaling * mu * inner(w, state.k) * inner(u0, state.k) * dx bcs = [DirichletBC(Vu, 0.0, "bottom"), DirichletBC(Vu, 0.0, "top")] u_forcing_problem = LinearVariationalProblem(a, L, self.uF, bcs=bcs) self.u_forcing_solver = LinearVariationalSolver(u_forcing_problem)
def get_flux_form(dS, M): fluxes = (-inner(2 * avg(outer(phi, n)), avg(grad(gamma) * M)) - inner(avg(grad(phi) * M), 2 * avg(outer(gamma, n))) + mu * inner(2 * avg(outer(phi, n)), 2 * avg(outer(gamma, n) * kappa))) * dS return fluxes
def get_flux_form(dS, M): fluxes = ( -inner(2*avg(outer(q, n)), avg(grad(test)*M)) - inner(avg(grad(q)*M), 2*avg(outer(test, n))) + mu*inner(2*avg(outer(q, n)), 2*avg(outer(test, n)*kappa)) )*dS return fluxes
def vector_invariant_form(state, test, q, ibp=IntegrateByParts.ONCE): Vu = state.spaces("HDiv") dS_ = (dS_v + dS_h) if Vu.extruded else dS ubar = Function(Vu) n = FacetNormal(state.mesh) Upwind = 0.5 * (sign(dot(ubar, n)) + 1) if state.mesh.topological_dimension() == 3: if ibp != IntegrateByParts.ONCE: raise NotImplementedError # <w,curl(u) cross ubar + grad( u.ubar)> # =<curl(u),ubar cross w> - <div(w), u.ubar> # =<u,curl(ubar cross w)> - # <<u_upwind, [[n cross(ubar cross w)cross]]>> both = lambda u: 2 * avg(u) L = (inner(q, curl(cross(ubar, test))) * dx - inner(both(Upwind * q), both(cross(n, cross(ubar, test)))) * dS_) else: perp = state.perp if state.on_sphere: outward_normals = CellNormal(state.mesh) perp_u_upwind = lambda q: Upwind('+') * cross( outward_normals('+'), q('+')) + Upwind('-') * cross( outward_normals('-'), q('-')) else: perp_u_upwind = lambda q: Upwind('+') * perp(q('+')) + Upwind( '-') * perp(q('-')) if ibp == IntegrateByParts.ONCE: L = (-inner(perp(grad(inner(test, perp(ubar)))), q) * dx - inner(jump(inner(test, perp(ubar)), n), perp_u_upwind(q)) * dS_) else: L = ((-inner(test, div(perp(q)) * perp(ubar))) * dx - inner(jump(inner(test, perp(ubar)), n), perp_u_upwind(q)) * dS_ + jump(inner(test, perp(ubar)) * perp(q), n) * dS_) L -= 0.5 * div(test) * inner(q, ubar) * dx form = transporting_velocity(L, ubar) return transport(form, TransportEquationType.vector_invariant)
def linear_continuity_form(state, test, qbar, facet_term=False): Vu = state.spaces("HDiv") ubar = Function(Vu) L = qbar * test * div(ubar) * dx if facet_term: n = FacetNormal(state.mesh) Vu = state.spaces("HDiv") dS_ = (dS_v + dS_h) if Vu.extruded else dS L += jump(ubar * test, n) * avg(qbar) * dS_ form = transporting_velocity(L, ubar) return transport(form, TransportEquationType.conservative)
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.w0 = Function(self.W3) self.w1 = Function(self.W3) u0, s0, h0, a0 = self.w0.split() p, q, r, m = TestFunctions(self.W3) self.initial_condition((u0, conditions.ic['u']), (s0, conditions.ic['s']), (a0, conditions.ic['a']), (h0, conditions.ic['h'])) self.w1.assign(self.w0) u1, s1, h1, a1 = split(self.w1) u0, s0, h0, a0 = split(self.w0) theta = conditions.theta uh = (1-theta) * u0 + theta * u1 sh = (1-theta) * s0 + theta * s1 hh = (1-theta) * h0 + theta * h1 ah = (1-theta) * a0 + theta * a1 ep_dot = self.strain(grad(uh)) zeta = self.zeta(hh, ah, self.delta(uh)) rheology = params.e ** 2 * sh + Identity(2) * 0.5 * ((1 - params.e ** 2) * tr(sh) + self.Ice_Strength(hh, ah)) eqn = self.momentum_equation(hh, u1, u0, p, sh, params.rho, uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep, ind=self.ind) eqn += self.transport_equation(uh, hh, ah, h1, h0, a1, a0, r, m, self.n, self.timestep) eqn += inner(self.ind * (s1 - s0) + 0.5 * self.timestep * rheology / params.T, q) * dx eqn -= inner(q * zeta * self.timestep / params.T, ep_dot) * dx if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] eqn += self.stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=uh, test=p) bcs = DirichletBC(self.W3.sub(0), conditions.bc['u'], "on_boundary") uprob = NonlinearVariationalProblem(eqn, self.w1, bcs) self.usolver = NonlinearVariationalSolver(uprob, solver_parameters=solver_params.bt_params) self.u1, self.s0, self.h1, self.a1 = self.w1.split()
def advection_term(self, q): n = FacetNormal(self.state.mesh) Upwind = 0.5 * (sign(dot(self.ubar, n)) + 1) if self.state.mesh.topological_dimension() == 3: # <w,curl(u) cross ubar + grad( u.ubar)> # =<curl(u),ubar cross w> - <div(w), u.ubar> # =<u,curl(ubar cross w)> - # <<u_upwind, [[n cross(ubar cross w)cross]]>> both = lambda u: 2 * avg(u) L = (inner(q, curl(cross(self.ubar, self.test))) * dx - inner(both(Upwind * q), both(cross(n, cross(self.ubar, self.test)))) * self.dS) else: perp = self.state.perp if self.state.on_sphere: outward_normals = CellNormal(self.state.mesh) perp_u_upwind = lambda q: Upwind('+') * cross( outward_normals('+'), q('+')) + Upwind('-') * cross( outward_normals('-'), q('-')) else: perp_u_upwind = lambda q: Upwind('+') * perp(q('+')) + Upwind( '-') * perp(q('-')) if self.ibp == IntegrateByParts.ONCE: L = (-inner(perp(grad(inner(self.test, perp(self.ubar)))), q) * dx - inner(jump(inner(self.test, perp(self.ubar)), n), perp_u_upwind(q)) * self.dS) else: L = ((-inner(self.test, div(perp(q)) * perp(self.ubar))) * dx - inner(jump(inner(self.test, perp(self.ubar)), n), perp_u_upwind(q)) * self.dS + jump(inner(self.test, perp(self.ubar)) * perp(q), n) * self.dS) L -= 0.5 * div(self.test) * inner(q, self.ubar) * dx return L
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.w0 = Function(self.W2) self.w1 = Function(self.W2) u0, h0, a0 = self.w0.split() p, q, r = TestFunctions(self.W2) self.initial_condition((u0, conditions.ic['u']), (h0, conditions.ic['h']), (a0, conditions.ic['a'])) self.w1.assign(self.w0) u1, h1, a1 = split(self.w1) u0, h0, a0 = split(self.w0) theta = conditions.theta uh = (1-theta) * u0 + theta * u1 ah = (1-theta) * a0 + theta * a1 hh = (1-theta) * h0 + theta * h1 ep_dot = self.strain(grad(uh)) zeta = self.zeta(hh, ah, self.delta(uh)) eta = zeta * params.e ** (-2) sigma = 2 * eta * ep_dot + (zeta - eta) * tr(ep_dot) * Identity(2) - self.Ice_Strength(hh, ah) * 0.5 * Identity( 2) eqn = self.momentum_equation(hh, u1, u0, p, sigma, params.rho, uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep) eqn += self.transport_equation(uh, hh, ah, h1, h0, a1, a0, q, r, self.n, self.timestep) if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] eqn += self.stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=uh, test=p) bcs = DirichletBC(self.W2.sub(0), conditions.bc['u'], "on_boundary") uprob = NonlinearVariationalProblem(eqn, self.w1, bcs) self.usolver = NonlinearVariationalSolver(uprob, solver_parameters=solver_params.bt_params) self.u1, self.h1, self.a1 = self.w1.split()
def lax_friedrichs_facet_flux(s, c, v): r"""Create the Lax-Friedrichs numerical flux through cell facets This is a diffusive numerical flux that can correct for the instability of the central facet flux. You can think of it as being like upwinding but for systems of PDE. Parameters ---------- s : firedrake.Function The state variable being solved for c : ufl.Expr An expression for the maximum outward wave speed through a facet Returns ------- f : firedrake.Form A 1-form that discretizes the residual of the flux """ return avg(c) * inner(s('+') - s('-'), v('+') - v('-')) * dS
def central_facet_flux(F, v): r"""Create the weak form of the central numerical flux through cell facets This numerical flux, by itself, is unstable. The full right-hand side of the problem requires an additional facet flux term for stability. Parameters ---------- F : ufl.Expr A symbolic expression for the flux v : firedrake.TestFunction A test function from the state space Returns ------- f : firedrake.Form A 1-form that discretizes the residual of the flux """ mesh = v.ufl_domain() n = FacetNormal(mesh) return inner(avg(F), outer(v('+'), n('+')) + outer(v('-'), n('-'))) * dS
def __init__(self, state, V, qbar, options=None): super(LinearAdvection_V3, self).__init__(state) p = TestFunction(V) q = TrialFunction(V) self.dq = Function(V) n = FacetNormal(state.mesh) a = p*q*dx L = (dot(grad(p), self.ubar)*qbar*dx - jump(self.ubar*p, n)*avg(qbar)*(dS_v + dS_h)) aProblem = LinearVariationalProblem(a,L,self.dq) if options is None: options = {'ksp_type':'cg', 'pc_type':'bjacobi', 'sub_pc_type':'ilu'} self.solver = LinearVariationalSolver(aProblem, solver_parameters=options, options_prefix='LinearAdvectionV3')
def pressure_gradient_term(self): u0, rho0, theta0 = split(self.x0) cp = self.state.parameters.cp n = FacetNormal(self.state.mesh) Vtheta = self.state.spaces("HDiv_v") # introduce new theta so it can be changed by moisture theta = theta0 # add effect of density of water upon theta if self.moisture is not None: water_t = Function(Vtheta).assign(0.0) for water in self.moisture: water_t += self.state.fields(water) theta = theta / (1 + water_t) pi = thermodynamics.pi(self.state.parameters, rho0, theta0) L = (+cp * div(theta * self.test) * pi * dx - cp * jump(self.test * theta, n) * avg(pi) * dS_v) return L
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.w0 = Function(self.W1) self.w1 = Function(self.W1) self.a = Function(self.U) self.h = Function(self.U) self.u0, self.s0 = self.w0.split() self.p, self.q = TestFunctions(self.W1) self.initial_condition((self.u0, conditions.ic['u']), (self.s0, conditions.ic['s']), (self.a, conditions.ic['a']), (self.h, conditions.ic['h'])) self.w1.assign(self.w0) u1, s1 = split(self.w1) u0, s0 = split(self.w0) theta = conditions.theta uh = (1-theta) * u0 + theta * u1 sh = (1-theta) * s0 + theta * s1 self.ep_dot = self.strain(grad(uh)) zeta = self.zeta(self.h, self.a, self.delta(uh)) self.rheology = params.e ** 2 * sh + Identity(2) * 0.5 * ((1 - params.e ** 2) * tr(sh) + self.Ice_Strength(self.h, self.a)) self.eqn = self.momentum_equation(self.h, u1, u0, self.p, sh, params.rho, uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep, ind=self.ind) self.eqn += inner(self.ind * (s1 - s0) + 0.5 * self.timestep * self.rheology / params.T, self.q) * dx self.eqn -= inner(self.q * zeta * self.timestep / params.T, self.ep_dot) * dx if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] self.eqn += self.stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=uh, test=self.p) self.bcs = DirichletBC(self.W1.sub(0), conditions.bc['u'], "on_boundary")
def L_tr(f): return _dl('+') * avg(f) * ( dS_vp + dS_hp) + _dl * f * ds_vp + _dl * f * ds_tbp
def get_flux_form(dS, M): fluxes = (-inner(2*avg(outer(phi, n)), avg(grad(gamma)*M)) - inner(avg(grad(phi)*M), 2*avg(outer(gamma, n))) + mu*inner(2*avg(outer(phi, n)), 2*avg(outer(gamma, n)*kappa)))*dS return fluxes
Kz = fd.Function(V) phi = fd.Constat(0.2) coords2ijk = np.vectorize(coords2ijk, excluded=['data_array', 'Delta']) Kx.dat.data[...] = coords2ijk(coords[:, 0], coords[:, 1], coords[:, 2], Delta=Delta, data_array=kx_array) Ky.dat.data[...] = coords2ijk(coords[:, 0], coords[:, 1], coords[:, 2], Delta=Delta, data_array=ky_array) Kz.dat.data[...] = coords2ijk(coords[:, 0], coords[:, 1], coords[:, 2], Delta=Delta, data_array=kz_array) print("END: Read in reservoir fields") # Permeability field harmonic interpolation to facets Kx_facet = fd.conditional(fd.gt(fd.avg(Kx), 0.0), Kx('+')*Kx('-') / fd.avg(Kx), 0.0) Ky_facet = fd.conditional(fd.gt(fd.avg(Ky), 0.0), Ky('+')*Ky('-') / fd.avg(Ky), 0.0) Kz_facet = fd.conditional(fd.gt(fd.avg(Kz), 0.0), Kz('+')*Kz('-') / fd.avg(Kz), 0.0) # We can now define the bilinear and linear forms for the left and right dx = fd.dx KdivU = fd.as_vector((Kx_facet*u.dx(0), Ky_facet*u.dx(1), Kz_facet*u.dx(2))) a = (fd.dot(KdivU, fd.grad(v))) * dx m = u * v * phi * dx # Defining the eigenvalue problem petsc_a = fd.assemble(a).M.handle petsc_m = fd.assemble(m).M.handle num_eigenvalues = 3
def _setup_solver(self): state = self.state # just cutting down line length a bit Dt = state.timestepping.dt beta_ = Dt*state.timestepping.alpha cp = state.parameters.cp mu = state.mu Vu = state.spaces("HDiv") Vtheta = state.spaces("HDiv_v") Vrho = state.spaces("DG") # Store time-stepping coefficients as UFL Constants dt = Constant(Dt) beta = Constant(beta_) beta_cp = Constant(beta_ * cp) # Split up the rhs vector (symbolically) u_in, rho_in, theta_in = split(state.xrhs) # Build the reduced function space for u,rho M = MixedFunctionSpace((Vu, Vrho)) w, phi = TestFunctions(M) u, rho = TrialFunctions(M) n = FacetNormal(state.mesh) # Get background fields thetabar = state.fields("thetabar") rhobar = state.fields("rhobar") pibar = thermodynamics.pi(state.parameters, rhobar, thetabar) pibar_rho = thermodynamics.pi_rho(state.parameters, rhobar, thetabar) pibar_theta = thermodynamics.pi_theta(state.parameters, rhobar, thetabar) # Analytical (approximate) elimination of theta k = state.k # Upward pointing unit vector theta = -dot(k, u)*dot(k, grad(thetabar))*beta + theta_in # Only include theta' (rather than pi') in the vertical # component of the gradient # the pi prime term (here, bars are for mean and no bars are # for linear perturbations) pi = pibar_theta*theta + pibar_rho*rho # vertical projection def V(u): return k*inner(u, k) # specify degree for some terms as estimated degree is too large dxp = dx(degree=(self.quadrature_degree)) dS_vp = dS_v(degree=(self.quadrature_degree)) # add effect of density of water upon theta if self.moisture is not None: water_t = Function(Vtheta).assign(0.0) for water in self.moisture: water_t += self.state.fields(water) theta_w = theta / (1 + water_t) thetabar_w = thetabar / (1 + water_t) else: theta_w = theta thetabar_w = thetabar eqn = ( inner(w, (state.h_project(u) - u_in))*dx - beta_cp*div(theta_w*V(w))*pibar*dxp # following does nothing but is preserved in the comments # to remind us why (because V(w) is purely vertical). # + beta_cp*jump(theta*V(w), n)*avg(pibar)*dS_v - beta_cp*div(thetabar_w*w)*pi*dxp + beta_cp*jump(thetabar_w*w, n)*avg(pi)*dS_vp + (phi*(rho - rho_in) - beta*inner(grad(phi), u)*rhobar)*dx + beta*jump(phi*u, n)*avg(rhobar)*(dS_v + dS_h) ) if mu is not None: eqn += dt*mu*inner(w, k)*inner(u, k)*dx aeqn = lhs(eqn) Leqn = rhs(eqn) # Place to put result of u rho solver self.urho = Function(M) # Boundary conditions (assumes extruded mesh) bcs = [DirichletBC(M.sub(0), 0.0, "bottom"), DirichletBC(M.sub(0), 0.0, "top")] # Solver for u, rho urho_problem = LinearVariationalProblem( aeqn, Leqn, self.urho, bcs=bcs) self.urho_solver = LinearVariationalSolver(urho_problem, solver_parameters=self.solver_parameters, options_prefix='ImplicitSolver') # Reconstruction of theta theta = TrialFunction(Vtheta) gamma = TestFunction(Vtheta) u, rho = self.urho.split() self.theta = Function(Vtheta) theta_eqn = gamma*(theta - theta_in + dot(k, u)*dot(k, grad(thetabar))*beta)*dx theta_problem = LinearVariationalProblem(lhs(theta_eqn), rhs(theta_eqn), self.theta) self.theta_solver = LinearVariationalSolver(theta_problem, options_prefix='thetabacksubstitution')
def stabilisation_term(self, alpha, zeta, mesh, v, test): e = avg(CellVolume(mesh)) / FacetArea(mesh) return 2 * alpha * zeta / e * (dot(jump(v), jump(test))) * dS
# numerical flux chat = lmbd_h ########################################### qhat = qh + tau_d * (ch - chat) * n + velocity * chat + tau_a * (ch - chat) * n # qhat = qh + tau*(ch - chat)*n + velocity * chat + tau_a*(ch - chat)*n # qhat_n = fd.dot(qh, n) + tau*(ch - chat) + chat*vn # ########################################### # # Lax-Friedrichs/Roe form # ch_a = 0.5*(ch('+') + ch('-')) # qhat = qh + tau_d*(ch - chat)*n + velocity * ch_a + tau_a*(ch - ch_a)*n a_u = ( fd.inner(fd.inv(Diff) * qh, vh) * fd.dx - ch * fd.div(vh) * fd.dx + # internal faces 2 * fd.avg(lmbd_h * fd.dot(vh, n)) * fd.dS + # other faces lmbd_h * fd.inner(vh, n) * fd.ds) # Dirichlet faces L_u = 0 a_c = (wh * (ch - c0) / dtc * fd.dx - fd.inner(fd.grad(wh), qh + ch * velocity) * fd.dx + 2 * fd.avg(wh * fd.dot(qhat, n)) * fd.dS + wh * fd.dot(qhat, n) * fd.ds) L_c = 0 # transmission boundary condition F_q = 2*fd.avg(mu_h*fd.dot(qhat, n))*fd.dS + \ mu_h*fd.inner(qhat, n)*fd.ds(outflow) + \
def __init__(self, state, V): super(EulerPoincareForm, self).__init__(state) dt = state.timestepping.dt w = TestFunction(V) u = TrialFunction(V) self.u0 = Function(V) ustar = 0.5*(self.u0 + u) n = FacetNormal(state.mesh) Upwind = 0.5*(sign(dot(self.ubar, n))+1) if state.mesh.geometric_dimension() == 3: if V.extruded: surface_measure = (dS_h + dS_v) else: surface_measure = dS # <w,curl(u) cross ubar + grad( u.ubar)> # =<curl(u),ubar cross w> - <div(w), u.ubar> # =<u,curl(ubar cross w)> - # <<u_upwind, [[n cross(ubar cross w)cross]]>> both = lambda u: 2*avg(u) Eqn = ( inner(w, u-self.u0)*dx + dt*inner(ustar, curl(cross(self.ubar, w)))*dx - dt*inner(both(Upwind*ustar), both(cross(n, cross(self.ubar, w))))*surface_measure - dt*div(w)*inner(ustar, self.ubar)*dx ) # define surface measure and terms involving perp differently # for slice (i.e. if V.extruded is True) and shallow water # (V.extruded is False) else: if V.extruded: surface_measure = (dS_h + dS_v) perp = lambda u: as_vector([-u[1], u[0]]) perp_u_upwind = Upwind('+')*perp(ustar('+')) + Upwind('-')*perp(ustar('-')) else: surface_measure = dS outward_normals = CellNormal(state.mesh) perp = lambda u: cross(outward_normals, u) perp_u_upwind = Upwind('+')*cross(outward_normals('+'),ustar('+')) + Upwind('-')*cross(outward_normals('-'),ustar('-')) Eqn = ( (inner(w, u-self.u0) - dt*inner(w, div(perp(ustar))*perp(self.ubar)) - dt*div(w)*inner(ustar, self.ubar))*dx - dt*inner(jump(inner(w, perp(self.ubar)), n), perp_u_upwind)*surface_measure + dt*jump(inner(w, perp(self.ubar))*perp(ustar), n)*surface_measure ) a = lhs(Eqn) L = rhs(Eqn) self.u1 = Function(V) uproblem = LinearVariationalProblem(a, L, self.u1) self.usolver = LinearVariationalSolver(uproblem, options_prefix='EPAdvection')
def setup(self, state): space = state.spaces("HDiv") super(SawyerEliassenU, self).setup(state, space=space) u = state.fields("u") b = state.fields("b") v = inner(u, as_vector([0., 1., 0.])) # spaces V0 = FunctionSpace(state.mesh, "CG", 2) Vu = u.function_space() # project b to V0 self.b_v0 = Function(V0) btri = TrialFunction(V0) btes = TestFunction(V0) a = inner(btes, btri) * dx L = inner(btes, b) * dx projectbproblem = LinearVariationalProblem(a, L, self.b_v0) self.project_b_solver = LinearVariationalSolver( projectbproblem, solver_parameters={'ksp_type': 'cg'}) # project v to V0 self.v_v0 = Function(V0) vtri = TrialFunction(V0) vtes = TestFunction(V0) a = inner(vtes, vtri) * dx L = inner(vtes, v) * dx projectvproblem = LinearVariationalProblem(a, L, self.v_v0) self.project_v_solver = LinearVariationalSolver( projectvproblem, solver_parameters={'ksp_type': 'cg'}) # stm/psi is a stream function self.stm = Function(V0) psi = TrialFunction(V0) xsi = TestFunction(V0) f = state.parameters.f H = state.parameters.H L = state.parameters.L dbdy = state.parameters.dbdy x, y, z = SpatialCoordinate(state.mesh) bcs = [DirichletBC(V0, 0., "bottom"), DirichletBC(V0, 0., "top")] Mat = as_matrix([[b.dx(2), 0., -f * self.v_v0.dx(2)], [0., 0., 0.], [-self.b_v0.dx(0), 0., f**2 + f * self.v_v0.dx(0)]]) Equ = (inner(grad(xsi), dot(Mat, grad(psi))) - dbdy * inner(grad(xsi), as_vector([-v, 0., f * (z - H / 2)]))) * dx # fourth-order terms if state.parameters.fourthorder: eps = Constant(0.0001) brennersigma = Constant(10.0) n = FacetNormal(state.mesh) deltax = Constant(state.parameters.deltax) deltaz = Constant(state.parameters.deltaz) nn = as_matrix([[sqrt(brennersigma / Constant(deltax)), 0., 0.], [0., 0., 0.], [0., 0., sqrt(brennersigma / Constant(deltaz))]]) mu = as_matrix([[1., 0., 0.], [0., 0., 0.], [0., 0., H / L]]) # anisotropic form Equ += eps * ( div(dot(mu, grad(psi))) * div(dot(mu, grad(xsi))) * dx - (avg(dot(dot(grad(grad(psi)), n), n)) * jump(grad(xsi), n=n) + avg(dot(dot(grad(grad(xsi)), n), n)) * jump(grad(psi), n=n) - jump(nn * grad(psi), n=n) * jump(nn * grad(xsi), n=n)) * (dS_h + dS_v)) Au = lhs(Equ) Lu = rhs(Equ) stmproblem = LinearVariationalProblem(Au, Lu, self.stm, bcs=bcs) self.stream_function_solver = LinearVariationalSolver( stmproblem, solver_parameters={'ksp_type': 'cg'}) # solve for sawyer_eliassen u self.u = Function(Vu) utrial = TrialFunction(Vu) w = TestFunction(Vu) a = inner(w, utrial) * dx L = (w[0] * (-self.stm.dx(2)) + w[2] * (self.stm.dx(0))) * dx ugproblem = LinearVariationalProblem(a, L, self.u) self.sawyer_eliassen_u_solver = LinearVariationalSolver( ugproblem, solver_parameters={'ksp_type': 'cg'})
I.dat.data[...] = np.floor(coords[:, 0] / Delta[0]).astype(int) J.dat.data[...] = np.floor(coords[:, 1] / Delta[1]).astype(int) K.dat.data[...] = np.floor(coords[:, 2] / Delta[2]).astype(int) Tx = Kx / mu * to_days Ty = Ky / mu * to_days Tz = Kz / mu * to_days w = phi * ct print("END: Read in reservoir fields") fd.File("../../output/spatial_homog.pvd").write(Kx, Ky, Kz, phi) # Permeability field harmonic interpolation to facets n = fd.FacetNormal(mesh) Tx_facet = fd.conditional(fd.gt(fd.avg(Tx), 0.0), Tx('+') * Tx('-') / fd.avg(Tx), 0.0) Ty_facet = fd.conditional(fd.gt(fd.avg(Ty), 0.0), Ty('+') * Ty('-') / fd.avg(Ty), 0.0) Tz_facet = fd.conditional(fd.gt(fd.avg(Tz), 0.0), Tz('+') * Tz('-') / fd.avg(Tz), 0.0) T_facet = (Tx_facet * (abs(n[0]('+')) + abs(n[0]('-'))) / 2 + Ty_facet * (abs(n[1]('+')) + abs(n[1]('-'))) / 2 + Tz_facet * (abs(n[2]('+')) + abs(n[2]('-'))) / 2) # We can now define the bilinear and linear forms for the left and right x, y, z = mesh.coordinates x_func = fd.interpolate(x, V) y_func = fd.interpolate(y, V)
def heat_exchanger_optimization(mu=0.03, n_iters=1000): output_dir = "2D/" path = os.path.abspath(__file__) dir_path = os.path.dirname(path) mesh = fd.Mesh(f"{dir_path}/2D_mesh.msh") # Perturb the mesh coordinates. Necessary to calculate shape derivatives S = fd.VectorFunctionSpace(mesh, "CG", 1) s = fd.Function(S, name="deform") mesh.coordinates.assign(mesh.coordinates + s) # Initial level set function x, y = fd.SpatialCoordinate(mesh) PHI = fd.FunctionSpace(mesh, "CG", 1) phi_expr = sin(y * pi / 0.2) * cos(x * pi / 0.2) - fd.Constant(0.8) # Avoid recording the operation interpolate into the tape. # Otherwise, the shape derivatives will not be correct with fda.stop_annotating(): phi = fd.interpolate(phi_expr, PHI) phi.rename("LevelSet") fd.File(output_dir + "phi_initial.pvd").write(phi) # Physics mu = fd.Constant(mu) # viscosity alphamin = 1e-12 alphamax = 2.5 / (2e-4) parameters = { "mat_type": "aij", "ksp_type": "preonly", "ksp_converged_reason": None, "pc_type": "lu", "pc_factor_mat_solver_type": "mumps", } stokes_parameters = parameters temperature_parameters = parameters u_inflow = 2e-3 tin1 = fd.Constant(10.0) tin2 = fd.Constant(100.0) P2 = fd.VectorElement("CG", mesh.ufl_cell(), 2) P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1) TH = P2 * P1 W = fd.FunctionSpace(mesh, TH) U = fd.TrialFunction(W) u, p = fd.split(U) V = fd.TestFunction(W) v, q = fd.split(V) epsilon = fd.Constant(10000.0) def hs(phi, epsilon): return fd.Constant(alphamax) * fd.Constant(1.0) / ( fd.Constant(1.0) + exp(-epsilon * phi)) + fd.Constant(alphamin) def stokes(phi, BLOCK_INLET_MOUTH, BLOCK_OUTLET_MOUTH): a_fluid = mu * inner(grad(u), grad(v)) - div(v) * p - q * div(u) darcy_term = inner(u, v) return (a_fluid * dx + hs(phi, epsilon) * darcy_term * dx(0) + alphamax * darcy_term * (dx(BLOCK_INLET_MOUTH) + dx(BLOCK_OUTLET_MOUTH))) # Dirichlet boundary conditions inflow1 = fd.as_vector([ u_inflow * sin( ((y - (line_sep - (dist_center + inlet_width))) * pi) / inlet_width), 0.0, ]) inflow2 = fd.as_vector([ u_inflow * sin(((y - (line_sep + dist_center)) * pi) / inlet_width), 0.0, ]) noslip = fd.Constant((0.0, 0.0)) # Stokes 1 bcs1_1 = fd.DirichletBC(W.sub(0), noslip, WALLS) bcs1_2 = fd.DirichletBC(W.sub(0), inflow1, INLET1) bcs1_3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), OUTLET1) bcs1_4 = fd.DirichletBC(W.sub(0), noslip, INLET2) bcs1_5 = fd.DirichletBC(W.sub(0), noslip, OUTLET2) bcs1 = [bcs1_1, bcs1_2, bcs1_3, bcs1_4, bcs1_5] # Stokes 2 bcs2_1 = fd.DirichletBC(W.sub(0), noslip, WALLS) bcs2_2 = fd.DirichletBC(W.sub(0), inflow2, INLET2) bcs2_3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), OUTLET2) bcs2_4 = fd.DirichletBC(W.sub(0), noslip, INLET1) bcs2_5 = fd.DirichletBC(W.sub(0), noslip, OUTLET1) bcs2 = [bcs2_1, bcs2_2, bcs2_3, bcs2_4, bcs2_5] # Forward problems U1, U2 = fd.Function(W), fd.Function(W) L = inner(fd.Constant((0.0, 0.0, 0.0)), V) * dx problem = fd.LinearVariationalProblem(stokes(-phi, INMOUTH2, OUTMOUTH2), L, U1, bcs=bcs1) solver_stokes1 = fd.LinearVariationalSolver( problem, solver_parameters=stokes_parameters, options_prefix="stokes_1") solver_stokes1.solve() problem = fd.LinearVariationalProblem(stokes(phi, INMOUTH1, OUTMOUTH1), L, U2, bcs=bcs2) solver_stokes2 = fd.LinearVariationalSolver( problem, solver_parameters=stokes_parameters, options_prefix="stokes_2") solver_stokes2.solve() # Convection difussion equation ks = fd.Constant(1e0) cp_value = 5.0e5 cp = fd.Constant(cp_value) T = fd.FunctionSpace(mesh, "DG", 1) t = fd.Function(T, name="Temperature") w = fd.TestFunction(T) # Mesh-related functions n = fd.FacetNormal(mesh) h = fd.CellDiameter(mesh) u1, p1 = fd.split(U1) u2, p2 = fd.split(U2) def upwind(u): return (dot(u, n) + abs(dot(u, n))) / 2.0 u1n = upwind(u1) u2n = upwind(u2) # Penalty term alpha = fd.Constant(500.0) # Bilinear form a_int = dot(grad(w), ks * grad(t) - cp * (u1 + u2) * t) * dx a_fac = (fd.Constant(-1.0) * ks * dot(avg(grad(w)), jump(t, n)) * dS + fd.Constant(-1.0) * ks * dot(jump(w, n), avg(grad(t))) * dS + ks("+") * (alpha("+") / avg(h)) * dot(jump(w, n), jump(t, n)) * dS) a_vel = (dot( jump(w), cp * (u1n("+") + u2n("+")) * t("+") - cp * (u1n("-") + u2n("-")) * t("-"), ) * dS + dot(w, cp * (u1n + u2n) * t) * ds) a_bnd = (dot(w, cp * dot(u1 + u2, n) * t) * (ds(INLET1) + ds(INLET2)) + w * t * (ds(INLET1) + ds(INLET2)) - w * tin1 * ds(INLET1) - w * tin2 * ds(INLET2) + alpha / h * ks * w * t * (ds(INLET1) + ds(INLET2)) - ks * dot(grad(w), t * n) * (ds(INLET1) + ds(INLET2)) - ks * dot(grad(t), w * n) * (ds(INLET1) + ds(INLET2))) aT = a_int + a_fac + a_vel + a_bnd LT_bnd = (alpha / h * ks * tin1 * w * ds(INLET1) + alpha / h * ks * tin2 * w * ds(INLET2) - tin1 * ks * dot(grad(w), n) * ds(INLET1) - tin2 * ks * dot(grad(w), n) * ds(INLET2)) problem = fd.LinearVariationalProblem(derivative(aT, t), LT_bnd, t) solver_temp = fd.LinearVariationalSolver( problem, solver_parameters=temperature_parameters, options_prefix="temperature", ) solver_temp.solve() # fd.solve(eT == 0, t, solver_parameters=temperature_parameters) # Cost function: Flux at the cold outlet scale_factor = 4e-4 Jform = fd.assemble( fd.Constant(-scale_factor * cp_value) * inner(t * u1, n) * ds(OUTLET1)) # Constraints: Pressure drop on each fluid power_drop = 1e-2 Power1 = fd.assemble(p1 / power_drop * ds(INLET1)) Power2 = fd.assemble(p2 / power_drop * ds(INLET2)) phi_pvd = fd.File("phi_evolution.pvd") def deriv_cb(phi): with stop_annotating(): phi_pvd.write(phi[0]) c = fda.Control(s) # Reduced Functionals Jhat = LevelSetFunctional(Jform, c, phi, derivative_cb_pre=deriv_cb) P1hat = LevelSetFunctional(Power1, c, phi) P1control = fda.Control(Power1) P2hat = LevelSetFunctional(Power2, c, phi) P2control = fda.Control(Power2) Jhat_v = Jhat(phi) print("Initial cost function value {:.5f}".format(Jhat_v), flush=True) print("Power drop 1 {:.5f}".format(Power1), flush=True) print("Power drop 2 {:.5f}".format(Power2), flush=True) beta_param = 0.08 # Regularize the shape derivatives only in the domain marked with 0 reg_solver = RegularizationSolver(S, mesh, beta=beta_param, gamma=1e5, dx=dx, design_domain=0) tol = 1e-5 dt = 0.05 params = { "alphaC": 1.0, "debug": 5, "alphaJ": 1.0, "dt": dt, "K": 1e-3, "maxit": n_iters, "maxtrials": 5, "itnormalisation": 10, "tol_merit": 5e-3, # new merit can be within 0.5% of the previous merit # "normalize_tol" : -1, "tol": tol, } solver_parameters = { "reinit_solver": { "h_factor": 2.0, } } # Optimization problem problem = InfDimProblem( Jhat, reg_solver, ineqconstraints=[ Constraint(P1hat, 1.0, P1control), Constraint(P2hat, 1.0, P2control), ], solver_parameters=solver_parameters, ) results = nlspace_solve(problem, params) return results
def __init__(self, mesh: object, kappa: float, comm_space: MPI.Comm, mu: float = 5., *args, **kwargs): """ Constructor :param mesh: spatial domain :param kappa: diffusion coefficient :param mu: penalty weighting function """ super(Diffusion2D, self).__init__(*args, **kwargs) # Spatial domain and function space self.mesh = mesh V = FunctionSpace(self.mesh, "DG", 1) self.function_space = V self.comm_space = comm_space # Placeholder for time step - will be updated in the update method self.dt = Constant(0.) # Things we need for the form gamma = TestFunction(V) phi = TrialFunction(V) self.f = Function(V) n = FacetNormal(mesh) # Set up the rhs and bilinear form of the equation a = (inner(gamma, phi) * dx + self.dt * (inner(grad(gamma), grad(phi) * kappa) * dx - inner(2 * avg(outer(phi, n)), avg(grad(gamma) * kappa)) * dS - inner(avg(grad(phi) * kappa), 2 * avg(outer(gamma, n))) * dS + mu * inner(2 * avg(outer(phi, n)), 2 * avg(outer(gamma, n) * kappa)) * dS)) rhs = inner(gamma, self.f) * dx # Function to hold the solution self.soln = Function(V) # Setup problem and solver prob = LinearVariationalProblem(a, rhs, self.soln) self.solver = NonlinearVariationalSolver(prob) # Set the data structure for any user-defined time point self.vector_template = VectorDiffusion2D(size=len(self.function_space), comm_space=self.comm_space) # Set initial condition: # Setting up a Gaussian blob in the centre of the domain. self.vector_t_start = VectorDiffusion2D(size=len(self.function_space), comm_space=self.comm_space) x = SpatialCoordinate(self.mesh) initial_tracer = exp(-((x[0] - 5)**2 + (x[1] - 5)**2)) tmp = Function(self.function_space) tmp.interpolate(initial_tracer) self.vector_t_start.set_values(np.copy(tmp.dat.data))
def _setup_solver(self): state = self.state # just cutting down line length a bit dt = state.timestepping.dt beta = dt*state.timestepping.alpha cp = state.parameters.cp mu = state.mu # Split up the rhs vector (symbolically) u_in, rho_in, theta_in = split(state.xrhs) # Build the reduced function space for u,rho M = MixedFunctionSpace((state.V[0], state.V[1])) w, phi = TestFunctions(M) u, rho = TrialFunctions(M) n = FacetNormal(state.mesh) # Get background fields thetabar = state.thetabar rhobar = state.rhobar pibar = exner(thetabar, rhobar, state) pibar_rho = exner_rho(thetabar, rhobar, state) pibar_theta = exner_theta(thetabar, rhobar, state) # Analytical (approximate) elimination of theta k = state.k # Upward pointing unit vector theta = -dot(k,u)*dot(k,grad(thetabar))*beta + theta_in # Only include theta' (rather than pi') in the vertical # component of the gradient # the pi prime term (here, bars are for mean and no bars are # for linear perturbations) pi = pibar_theta*theta + pibar_rho*rho # vertical projection def V(u): return k*inner(u,k) eqn = ( inner(w, (u - u_in))*dx - beta*cp*div(theta*V(w))*pibar*dx # following does nothing but is preserved in the comments # to remind us why (because V(w) is purely vertical. # + beta*cp*jump(theta*V(w),n)*avg(pibar)*dS_v - beta*cp*div(thetabar*w)*pi*dx + beta*cp*jump(thetabar*w,n)*avg(pi)*dS_v + (phi*(rho - rho_in) - beta*inner(grad(phi), u)*rhobar)*dx + beta*jump(phi*u, n)*avg(rhobar)*(dS_v + dS_h) ) if mu is not None: eqn += dt*mu*inner(w,k)*inner(u,k)*dx aeqn = lhs(eqn) Leqn = rhs(eqn) # Place to put result of u rho solver self.urho = Function(M) # Boundary conditions (assumes extruded mesh) dim = M.sub(0).ufl_element().value_shape()[0] bc = ("0.0",)*dim bcs = [DirichletBC(M.sub(0), Expression(bc), "bottom"), DirichletBC(M.sub(0), Expression(bc), "top")] # Solver for u, rho urho_problem = LinearVariationalProblem( aeqn, Leqn, self.urho, bcs=bcs) self.urho_solver = LinearVariationalSolver(urho_problem, solver_parameters=self.params, options_prefix='ImplicitSolver') # Reconstruction of theta theta = TrialFunction(state.V[2]) gamma = TestFunction(state.V[2]) u, rho = self.urho.split() self.theta = Function(state.V[2]) theta_eqn = gamma*(theta - theta_in + dot(k,u)*dot(k,grad(thetabar))*beta)*dx theta_problem = LinearVariationalProblem(lhs(theta_eqn), rhs(theta_eqn), self.theta) self.theta_solver = LinearVariationalSolver(theta_problem, options_prefix='thetabacksubstitution')
def both(u): return 2 * fd.avg(u)