def __init__(self, state, V, direction=[1,2], params=None): super(InteriorPenalty, self).__init__(state) dt = state.timestepping.dt kappa = params['kappa'] mu = params['mu'] gamma = TestFunction(V) phi = TrialFunction(V) self.phi1 = Function(V) n = FacetNormal(state.mesh) a = inner(gamma,phi)*dx + dt*inner(grad(gamma), grad(phi)*kappa)*dx 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 if 1 in direction: a += dt*get_flux_form(dS_v, kappa) if 2 in direction: a += dt*get_flux_form(dS_h, kappa) L = inner(gamma,phi)*dx problem = LinearVariationalProblem(a, action(L,self.phi1), self.phi1) self.solver = LinearVariationalSolver(problem)
def solvevdisp(mesh,bdryids,deltah): P1 = FunctionSpace(mesh, "CG", 1) r = TrialFunction(P1) s = TestFunction(P1) a = inner(grad(r), grad(s)) * dx # note natural b.c. on outflow L = inner(Constant(0.0), s) * dx # WARNING: top must go *first* so closed top gets zero; is this documented behavior? bcs = [ DirichletBC(P1, deltah, bdryids['top']), DirichletBC(P1, Constant(0.0), (bdryids['base'],bdryids['inflow'])) ] rsoln = Function(P1) solve(a == L, rsoln, bcs=bcs, options_prefix='vd', solver_parameters={}) return rsoln
def __init__(self, state, V, direction=[], supg_params=None): super(SUPGAdvection, self).__init__(state) dt = state.timestepping.dt params = supg_params.copy() if supg_params else {} params.setdefault('a0', dt/sqrt(15.)) params.setdefault('a1', dt/sqrt(15.)) gamma = TestFunction(V) theta = TrialFunction(V) self.theta0 = Function(V) # make SUPG test function taus = [params["a0"], params["a1"]] for i in direction: taus[i] = 0.0 tau = Constant(((taus[0], 0.), (0., taus[1]))) dgamma = dot(dot(self.ubar, tau), grad(gamma)) gammaSU = gamma + dgamma n = FacetNormal(state.mesh) un = 0.5*(dot(self.ubar, n) + abs(dot(self.ubar, n))) a_mass = gammaSU*theta*dx arhs = a_mass - dt*gammaSU*dot(self.ubar, grad(theta))*dx if 1 in direction: arhs -= ( dt*dot(jump(gammaSU), (un('+')*theta('+') - un('-')*theta('-')))*dS_v - dt*(gammaSU('+')*dot(self.ubar('+'), n('+'))*theta('+') + gammaSU('-')*dot(self.ubar('-'), n('-'))*theta('-'))*dS_v ) if 2 in direction: arhs -= ( dt*dot(jump(gammaSU), (un('+')*theta('+') - un('-')*theta('-')))*dS_h - dt*(gammaSU('+')*dot(self.ubar('+'), n('+'))*theta('+') + gammaSU('-')*dot(self.ubar('-'), n('-'))*theta('-'))*dS_h ) self.theta1 = Function(V) self.dtheta = Function(V) problem = LinearVariationalProblem(a_mass, action(arhs,self.theta1), self.dtheta) self.solver = LinearVariationalSolver(problem, options_prefix='SUPGAdvection')
def test_eigenvalues(): nx, ny = 32, 32 mesh = firedrake.UnitSquareMesh(nx, ny) x, y = firedrake.SpatialCoordinate(mesh) V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=2) u = interpolate(as_vector((x, 0)), V) Q = firedrake.FunctionSpace(mesh, family='DG', degree=2) ε = sym(grad(u)) Λ1, Λ2 = eigenvalues(ε) λ1 = firedrake.project(Λ1, Q) λ2 = firedrake.project(Λ2, Q) assert norm(λ1 - Constant(1)) < norm(u) / (nx * ny) assert norm(λ2) < norm(u) / (nx * ny)
def cell_flux(F, v): r"""Create the weak form of the fluxes through the cell interior 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 """ return -inner(F, grad(v)) * dx
def _ad_dot(self, other, options=None): from firedrake import assemble options = {} if options is None else options riesz_representation = options.get("riesz_representation", "l2") if riesz_representation == "l2": return self.vector().inner(other.vector()) elif riesz_representation == "L2": return assemble(firedrake.inner(self, other) * firedrake.dx) elif riesz_representation == "H1": return assemble( (firedrake.inner(self, other) + firedrake.inner(firedrake.grad(self), other)) * firedrake.dx) else: raise NotImplementedError("Unknown Riesz representation %s" % riesz_representation)
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 prepare_trial(trial, true_sol_name, cl_ctx, queue): tuple_trial = trial_to_tuple(trial) if tuple_trial not in prepared_trials: mesh = trial['mesh'] degree = trial['degree'] kappa = trial['kappa'] function_space = FunctionSpace(mesh, 'CG', degree) vect_function_space = VectorFunctionSpace(mesh, 'CG', degree) true_sol = get_true_sol(function_space, kappa, cl_ctx, queue) true_sol = Function(function_space).interpolate(true_sol) prepared_trials[tuple_trial] = (mesh, function_space, vect_function_space, true_sol, grad(true_sol)) return prepared_trials[tuple_trial]
def gravity(u, h, s): r"""Return the gravitational part of the ice stream action functional The gravitational part of the ice stream action functional is .. math:: E(u) = -\int_\Omega\rho_Igh\nabla s\cdot u\; dx Parameters ---------- u : firedrake.Function ice velocity h : firedrake.Function ice thickness s : firedrake.Function ice surface elevation """ return -ρ_I * g * h * inner(grad(s), u)
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 residual(self, test, trial, trial_lagged, fields, bcs): phi = test n = self.n p = fields['pressure'] # NOTE: we assume p is continuous F = dot(phi, grad(p))*self.dx # do nothing should be zero (normal) stress: F += -dot(phi, n)*p*self.ds # for those boundaries where the normal component of u is specified # we take it out again for id, bc in bcs.items(): if 'u' in bc or 'un' in bc: F += dot(phi, n)*p*self.ds(id) return -F
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 prepare_trial(trial, true_sol_name): tuple_trial = trial_to_tuple(trial) if tuple_trial not in prepared_trials: mesh = trial['mesh'] degree = trial['degree'] function_space = FunctionSpace(mesh, 'CG', degree) vect_function_space = VectorFunctionSpace(mesh, 'CG', degree) true_sol_expr = trial['true_sol_expr'] true_solution = Function(function_space, name=true_sol_name).interpolate( true_sol_expr) true_solution_grad = Function(vect_function_space).interpolate( grad(true_sol_expr)) prepared_trials[tuple_trial] = (mesh, function_space, vect_function_space, true_solution, true_solution_grad) return prepared_trials[tuple_trial]
def __init__(self, state, V, qbar, options=None): super(LinearAdvection_Vt, self).__init__(state) p = TestFunction(V) q = TrialFunction(V) self.dq = Function(V) a = p*q*dx k = state.k # Upward pointing unit vector L = -p*dot(self.ubar,k)*dot(k,grad(qbar))*dx 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='LinearAdvectionVt')
def siahorizontalvelocity(mesh): hbase = surfaceelevation(mesh) if mesh._base_mesh.cell_dimension() == 2: if mesh._base_mesh.ufl_cell() == fd.quadrilateral: Vvectorbase = fd.VectorFunctionSpace(mesh._base_mesh, 'DQ', 0) VvectorR = fd.VectorFunctionSpace(mesh, 'DQ', 0, vfamily='R', vdegree=0, dim=2) else: Vvectorbase = fd.VectorFunctionSpace(mesh._base_mesh, 'DP', 0) VvectorR = fd.VectorFunctionSpace(mesh, 'DP', 0, vfamily='R', vdegree=0, dim=2) gradhbase = fd.project(fd.grad(hbase), Vvectorbase) Vvector = fd.VectorFunctionSpace(mesh, 'DQ', 0, dim=2) elif mesh._base_mesh.cell_dimension() == 1: Vvectorbase = fd.FunctionSpace(mesh._base_mesh, 'DP', 0) gradhbase = fd.project(hbase.dx(0), Vvectorbase) VvectorR = fd.FunctionSpace(mesh, 'DP', 0, vfamily='R', vdegree=0) Vvector = fd.FunctionSpace(mesh, 'DQ', 0) else: raise ValueError('base mesh of extruded input mesh must be 1D or 2D') gradh = fd.Function(VvectorR) gradh.dat.data[:] = gradhbase.dat.data_ro[:] h = extend(mesh, hbase) DQ0 = fd.FunctionSpace(mesh, 'DQ', 0) h0 = fd.project(h, DQ0) x = fd.SpatialCoordinate(mesh) z0 = fd.project(x[mesh._base_mesh.cell_dimension()], DQ0) # FIXME following only valid in flat bed case uvsia = -Gamma * (h0**(n + 1) - (h0 - z0)**(n + 1)) * abs(gradh)**(n - 1) * gradh uv = fd.Function(Vvector).interpolate(uvsia) uv.rename('velocitySIA') return uv
def betaInit(s, h, speed, V, Q, Q1, grounded, inversionParams): """Compute intitial beta using 0.5 taud. Parameters ---------- s : firedrake function model surface elevation h : firedrake function model thickness speed : firedrake function modelled speed V : firedrake vector function space vector function space Q : firedrake function space scalar function space grounded : firedrake function Mask with 1s for grounded 0 for floating. """ # Use a result from prior inversion checkFile = inversionParams['initFile'] Quse = Q if inversionParams['initWithDeg1']: checkFile = f'{inversionParams["inversionResult"]}.deg1' Quse = Q1 if checkFile is not None: betaTemp = mf.getCheckPointVars(checkFile, 'betaInv', Quse)['betaInv'] beta1 = icepack.interpolate(betaTemp, Q) return beta1 # No prior result, so use fraction of taud tauD = firedrake.project(-rhoI * g * h * grad(s), V) # stress = firedrake.sqrt(firedrake.inner(tauD, tauD)) Print('stress', firedrake.assemble(stress * firedrake.dx)) fraction = firedrake.Constant(0.95) U = max_value(speed, 1) C = fraction * stress / U**(1/m) if inversionParams['friction'] == 'schoof': mExp = 1/m + 1 U0 = firedrake.Constant(inversionParams['uThresh']) C = C * (m/(m+1)) * (U0**mExp + U**mExp)**(1/(m+1)) beta = firedrake.interpolate(firedrake.sqrt(C) * grounded, Q) return beta
def gravity(u, h): r"""Return the gravitational part of the ice shelf action functional The gravitational part of the ice shelf action functional is .. math:: E(u) = -\frac{1}{2}\int_\Omega\varrho g\nabla h^2\cdot u\hspace{2pt}dx Parameters ---------- u : firedrake.Function ice velocity h : firedrake.Function ice thickness Returns ------- firedrake.Form """ ρ = ρ_I * (1 - ρ_I / ρ_W) return -0.5 * ρ * g * inner(grad(h**2), u) * dx
def norm(u, norm_type="L2"): r"""Compute the norm of a field Computes one of any number of norms of a scalar or vector field. The available options are: - ``L2``: :math:`\|u\|^2 = \int_\Omega|u|^2dx` - ``H01``: :math:`\|u\|^2 = \int_\Omega|\nabla u|^2dx` - ``H1``: :math:`\|u\|^2 = \int_\Omega\left(|u|^2 + L^2|\nabla u|^2\right)dx` - ``L1``: :math:`\|u\| = \int_\Omega|u|dx` - ``TV``: :math:`\|u\| = \int_\Omega|\nabla u|dx` - ``Linfty``: :math:`\|u\| = \max_{x\in\Omega}|u(x)|` The extra factor :math:`L` in the :math:`H^1` norm is the diameter of the domain in the infinity metric. This extra factor is included to make the norm scale appropriately with the size of the domain. """ if norm_type == "L2": form, p = inner(u, u) * dx, 2 if norm_type == "H01": form, p = inner(grad(u), grad(u)) * dx, 2 if norm_type == "H1": L = utilities.diameter(u.ufl_domain()) form, p = inner(u, u) * dx + L**2 * inner(grad(u), grad(u)) * dx, 2 if norm_type == "L1": form, p = sqrt(inner(u, u)) * dx, 1 if norm_type == "TV": form, p = sqrt(inner(grad(u), grad(u))) * dx, 1 if norm_type == "Linfty": data = u.dat.data_ro if len(data.shape) == 1: local_max = np.max(np.abs(data)) elif len(data.shape) == 2: local_max = np.max(np.sqrt(np.sum(data**2, 1))) return u.comm.allreduce(local_max, op=max) return assemble(form)**(1 / p)
def update_state(self): lam = self.lam c = self.c av = self.bound.vector()[:] self.S.assign(self.T) self.S -= self.iden S = self.S self.gradS.project(fd.grad(S)) lam_c_grad_S = self.lam_c_grad_S lam_c_grad_S.project(lam / c + self.gradS) lam_c_grad_Sv = lam_c_grad_S.vector() B = self.argmin.vector()[:].copy() nucv = self.nuclear_norm.vector()[:].copy() for i in range(len(lam_c_grad_Sv)): W, Sigma, V = svd(lam_c_grad_Sv[i], full_matrices=False) for j in range(self.dim): Sigma[j] = c * max(Sigma[j] - av[i], 0) B[i] = np.dot(W, np.dot(np.diag(Sigma), V)) nucv[i] = av[i] * np.sum(Sigma) self.argmin.vector().set_local(B.flatten()) self.nuclear_norm.vector().set_local(nucv.flatten())
def sources(self, **kwargs): keys = ('damage', 'velocity', 'fluidity') keys_alt = ('D', 'u', 'A') D, u, A = get_kwargs_alt(kwargs, keys, keys_alt) # Increase/decrease damage depending on stress and strain rates ε = sym(grad(u)) ε_1 = eigenvalues(ε)[0] σ = M(ε, A) σ_e = sqrt(inner(σ, σ) - det(σ)) ε_h = firedrake.Constant(self.healing_strain_rate) σ_d = firedrake.Constant(self.damage_stress) γ_h = firedrake.Constant(self.healing_rate) γ_d = firedrake.Constant(self.damage_rate) healing = γ_h * min_value(ε_1 - ε_h, 0) fracture = γ_d * conditional(σ_e - σ_d > 0, ε_1, 0.) * (1 - D) return healing + fracture
def setup(self, state, vorticity_type=None): """Solver for vorticity. :arg state: The state containing model. :arg vorticity_type: must be "relative", "absolute" or "potential" """ if not self._initialised: vorticity_types = ["relative", "absolute", "potential"] if vorticity_type not in vorticity_types: raise ValueError("vorticity type must be one of %s, not %s" % (vorticity_types, vorticity_type)) try: space = state.spaces("CG") except AttributeError: dgspace = state.spaces("DG") cg_degree = dgspace.ufl_element().degree() + 2 space = FunctionSpace(state.mesh, "CG", cg_degree) super().setup(state, space=space) u = state.fields("u") gamma = TestFunction(space) q = TrialFunction(space) if vorticity_type == "potential": D = state.fields("D") a = q*gamma*D*dx else: a = q*gamma*dx if state.on_sphere: cell_normals = CellNormal(state.mesh) gradperp = lambda psi: cross(cell_normals, grad(psi)) L = (- inner(gradperp(gamma), u))*dx else: raise NotImplementedError("The vorticity diagnostics have only been implemented for 2D spherical geometries.") if vorticity_type != "relative": f = state.fields("coriolis") L += gamma*f*dx problem = LinearVariationalProblem(a, L, self.field) self.solver = LinearVariationalSolver(problem, solver_parameters={"ksp_type": "cg"})
def __init__(self, state, V, continuity=False): super(DGAdvection, self).__init__(state) element = V.fiat_element assert element.entity_dofs() == element.entity_closure_dofs(), "Provided space is not discontinuous" dt = state.timestepping.dt if V.extruded: surface_measure = (dS_h + dS_v) else: surface_measure = dS phi = TestFunction(V) D = TrialFunction(V) self.D1 = Function(V) self.dD = Function(V) n = FacetNormal(state.mesh) # ( dot(v, n) + |dot(v, n)| )/2.0 un = 0.5*(dot(self.ubar, n) + abs(dot(self.ubar, n))) a_mass = inner(phi,D)*dx if continuity: a_int = -inner(grad(phi), outer(D, self.ubar))*dx else: a_int = -inner(div(outer(phi,self.ubar)),D)*dx a_flux = (dot(jump(phi), un('+')*D('+') - un('-')*D('-')))*surface_measure arhs = a_mass - dt*(a_int + a_flux) DGproblem = LinearVariationalProblem(a_mass, action(arhs,self.D1), self.dD) self.DGsolver = LinearVariationalSolver(DGproblem, solver_parameters={ 'ksp_type':'preonly', 'pc_type':'bjacobi', 'sub_pc_type': 'ilu'}, options_prefix='DGAdvection')
def gravity(**kwargs): r"""Return the gravitational part of the ice stream action functional The gravitational part of the ice stream action functional is .. math:: E(u) = -\int_\Omega\rho_Igh\nabla s\cdot u\; dx Parameters ---------- u : firedrake.Function ice velocity h : firedrake.Function ice thickness s : firedrake.Function ice surface elevation """ keys = ('velocity', 'thickness', 'surface') keys_alt = ('u', 'h', 's') u, h, s = get_kwargs_alt(kwargs, keys, keys_alt) return -ρ_I * g * h * inner(grad(s), u)
def residual(self, test, trial, trial_lagged, fields, bcs): psi = test n = self.n u = fields['velocity'] # NOTE: we assume psi is continuous # assert is_continuous(psi) F = -dot(grad(psi), u)*self.dx # do nothing should be zero (normal) stress, which means no (Dirichlet condition) # should be imposed on the normal component F += psi*dot(n, u)*self.ds # for those boundaries where the normal component of u is specified # we take it out again and replace with the specified un for id, bc in bcs.items(): if 'u' in bc: F += psi*dot(n, bc['u']-u)*self.ds(id) elif 'un' in bc: F += psi*(bc['un'] - dot(n, u))*self.ds(id) return -F
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 create_dirichlet_bounds(mesh, V, T, v, k, g, boundary=[1, 2, 3, 4, 5, 6]): """ Create the dirichlet boundary conditions: u = g on boundary mesh: Mesh, The mesh to define the bound for V: FunctionSpace, The function space for the boundary T: Function, The function to be calculated v: Function, The test function k: Function, The conductivity of the problem g: float, Used in above formula Returns: bcs, R and b, the defining functions of the bound Type: list<DirichletBC>, Function, Function """ norm = FacetNormal(mesh) bcs = [DirichletBC(V, g, boundary)] R = 0 b = k*v*dot(grad(T), norm)*ds return bcs, R, b
def f_g_scattered_plane_wave(self, d): """Sets f and g to correspond to the scattering of a plane wave by a compactly-supported heterogeneous region. Parameters - d - list of the length of the spatial dimension; the direction in which the plane wave propagates. """ d = fd.as_vector(d) x = fd.SpatialCoordinate(self.V.mesh()) # Incident wave u_I = fd.exp(1j * self._k * fd.dot(x, d)) identity = fd.as_matrix([[1.0, 0.0], [0.0, 1.0]]) f = fd.div(fd.dot((identity-self._A),fd.grad(u_I)))\ + self._k**2.0 * fd.inner((1.0-self._n), u_I) self.set_f(f) self.set_g(0.0)
def gravity(**kwargs): r"""Return the gravitational part of the ice shelf action functional The gravitational part of the ice shelf action functional is .. math:: E(u) = -\frac{1}{2}\int_\Omega\varrho g\nabla h^2\cdot u\; dx Parameters ---------- u : firedrake.Function ice velocity h : firedrake.Function ice thickness Returns ------- firedrake.Form """ u, h = itemgetter("velocity", "thickness")(kwargs) ρ = ρ_I * (1 - ρ_I / ρ_W) return -0.5 * ρ * g * inner(grad(h**2), u)
def __init__(self, mesh, V): """ Initialiser for Problem. Sets up the a and L variables that are used throughout the mixins. Args: mesh (firedrake.Mesh): The mesh that the problem is defined on. V (firedrake.FunctionSpace): The function space to define the solution on. """ self._functions = [] # Store the function space details. self.mesh = mesh self.V = V # Initialise functions for problem. self.v = TestFunction(V) self._add_function('T') self._add_function('S') self._add_function('K') self._add_function('q') self._add_function('v_th') self._add_function('ionisation') self._add_function('atomic_number') self._add_function('a') self._add_function('L') self.q = self.K * grad(self.T) self.v_th = sqrt(3 * e * self.T / m_e) self.ionisation = self._ionisation() self.a = self._A() self.L = self._f()
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 setup(self, state, vorticity_type=None): """Solver for vorticity. :arg state: The state containing model. :arg vorticity_type: must be "relative", "absolute" or "potential" """ if not self._initialised: vorticity_types = ["relative", "absolute", "potential"] if vorticity_type not in vorticity_types: raise ValueError("vorticity type must be one of %s, not %s" % (vorticity_types, vorticity_type)) try: space = state.spaces("CG") except ValueError: dgspace = state.spaces("DG") cg_degree = dgspace.ufl_element().degree() + 2 space = FunctionSpace(state.mesh, "CG", cg_degree) super().setup(state, space=space) u = state.fields("u") gamma = TestFunction(space) q = TrialFunction(space) if vorticity_type == "potential": D = state.fields("D") a = q * gamma * D * dx else: a = q * gamma * dx L = (-inner(state.perp(grad(gamma)), u)) * dx if vorticity_type != "relative": f = state.fields("coriolis") L += gamma * f * dx problem = LinearVariationalProblem(a, L, self.field) self.solver = LinearVariationalSolver( problem, solver_parameters={"ksp_type": "cg"})
def nonlocal_integral_eq( mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=None, solver_parameters=None, fspace=None, vfspace=None, true_sol_grad_expr=None, actx=None, dgfspace=None, dgvfspace=None, meshmode_src_connection=None, qbx_kwargs=None, ): r""" see run_method for descriptions of unlisted args args: gamma and beta are used to precondition with the following equation: \Delta u - \kappa^2 \gamma u = 0 (\partial_n - i\kappa\beta) u |_\Sigma = 0 """ # make sure we get outer bdy id as tuple in case it consists of multiple ids if isinstance(outer_bdy_id, int): outer_bdy_id = [outer_bdy_id] outer_bdy_id = tuple(outer_bdy_id) # away from the excluded region, but firedrake and meshmode point # into pyt_inner_normal_sign = -1 ambient_dim = mesh.geometric_dimension() # {{{ Build src and tgt # build connection meshmode near src boundary -> src boundary inside meshmode from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from meshmode.discretization.connection import make_face_restriction factory = InterpolatoryQuadratureSimplexGroupFactory( dgfspace.finat_element.degree) src_bdy_connection = make_face_restriction(actx, meshmode_src_connection.discr, factory, scatterer_bdy_id) # source is a qbx layer potential from pytential.qbx import QBXLayerPotentialSource disable_refinement = (fspace.mesh().geometric_dimension() == 3) qbx = QBXLayerPotentialSource(src_bdy_connection.to_discr, **qbx_kwargs, _disable_refinement=disable_refinement) # get target indices and point-set target_indices, target = get_target_points_and_indices( fspace, outer_bdy_id) # }}} # build the operations from pytential import bind, sym r""" ..math: x \in \Sigma grad_op(x) = \nabla( \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ grad_op = pyt_inner_normal_sign * sym.grad( ambient_dim, sym.D(HelmholtzKernel(ambient_dim), sym.var("u"), k=sym.var("k"), qbx_forced_limit=None)) r""" ..math: x \in \Sigma op(x) = i \kappa \cdot \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) """ op = pyt_inner_normal_sign * 1j * sym.var("k") * (sym.D( HelmholtzKernel(ambient_dim), sym.var("u"), k=sym.var("k"), qbx_forced_limit=None)) # bind the operations pyt_grad_op = bind((qbx, target), grad_op) pyt_op = bind((qbx, target), op) # }}} class MatrixFreeB(object): def __init__(self, A, pyt_grad_op, pyt_op, actx, kappa): """ :arg kappa: The wave number """ self.actx = actx self.k = kappa self.pyt_op = pyt_op self.pyt_grad_op = pyt_grad_op self.A = A self.meshmode_src_connection = meshmode_src_connection # {{{ Create some functions needed for multing self.x_fntn = Function(fspace) # CG self.potential_int = Function(fspace) self.potential_int.dat.data[:] = 0.0 self.grad_potential_int = Function(vfspace) self.grad_potential_int.dat.data[:] = 0.0 self.pyt_result = Function(fspace) self.n = FacetNormal(mesh) self.v = TestFunction(fspace) # some meshmode ones self.x_mm_fntn = self.meshmode_src_connection.discr.empty( self.actx, dtype='c') # }}} def mult(self, mat, x, y): # Copy function data into the fivredrake function self.x_fntn.dat.data[:] = x[:] # Transfer the function to meshmode self.meshmode_src_connection.from_firedrake(project( self.x_fntn, dgfspace), out=self.x_mm_fntn) # Restrict to boundary x_mm_fntn_on_bdy = src_bdy_connection(self.x_mm_fntn) # Apply the operation potential_int_mm = self.pyt_op(self.actx, u=x_mm_fntn_on_bdy, k=self.k) grad_potential_int_mm = self.pyt_grad_op(self.actx, u=x_mm_fntn_on_bdy, k=self.k) # Store in firedrake self.potential_int.dat.data[target_indices] = potential_int_mm.get( ) for dim in range(grad_potential_int_mm.shape[0]): self.grad_potential_int.dat.data[ target_indices, dim] = grad_potential_int_mm[dim].get() # Integrate the potential r""" Compute the inner products using firedrake. Note this will be subtracted later, hence appears off by a sign. .. math:: \langle n(x) \cdot \nabla( \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ), v \rangle_\Sigma - \langle i \kappa \cdot \int_\Gamma( u(y) \partial_n H_0^{(1)}(\kappa |x - y|) )d\gamma(y), v \rangle_\Sigma """ self.pyt_result = assemble( inner(inner(self.grad_potential_int, self.n), self.v) * ds(outer_bdy_id) - inner(self.potential_int, self.v) * ds(outer_bdy_id)) # y <- Ax - evaluated potential self.A.mult(x, y) with self.pyt_result.dat.vec_ro as ep: y.axpy(-1, ep) # {{{ Compute normal helmholtz operator u = TrialFunction(fspace) v = TestFunction(fspace) r""" .. math:: \langle \nabla u, \nabla v \rangle - \kappa^2 \cdot \langle u, v \rangle - i \kappa \langle u, v \rangle_\Sigma """ a = inner(grad(u), grad(v)) * dx \ - Constant(wave_number**2) * inner(u, v) * dx \ - Constant(1j * wave_number) * inner(u, v) * ds(outer_bdy_id) # get the concrete matrix from a general bilinear form A = assemble(a).M.handle # }}} # {{{ Setup Python matrix B = PETSc.Mat().create() # build matrix context Bctx = MatrixFreeB(A, pyt_grad_op, pyt_op, actx, wave_number) # set up B as same size as A B.setSizes(*A.getSizes()) B.setType(B.Type.PYTHON) B.setPythonContext(Bctx) B.setUp() # }}} # {{{ Create rhs # Remember f is \partial_n(true_sol)|_\Gamma # so we just need to compute \int_\Gamma\partial_n(true_sol) H(x-y) sigma = sym.make_sym_vector("sigma", ambient_dim) r""" ..math: x \in \Sigma grad_op(x) = \nabla( \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ grad_op = pyt_inner_normal_sign * \ sym.grad(ambient_dim, sym.S(HelmholtzKernel(ambient_dim), sym.n_dot(sigma), k=sym.var("k"), qbx_forced_limit=None)) r""" ..math: x \in \Sigma op(x) = i \kappa \cdot \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ) """ op = 1j * sym.var("k") * pyt_inner_normal_sign * \ sym.S(HelmholtzKernel(ambient_dim), sym.n_dot(sigma), k=sym.var("k"), qbx_forced_limit=None) rhs_grad_op = bind((qbx, target), grad_op) rhs_op = bind((qbx, target), op) # Transfer to meshmode metadata = {'quadrature_degree': 2 * fspace.ufl_element().degree()} dg_true_sol_grad = project(true_sol_grad_expr, dgvfspace, form_compiler_parameters=metadata) true_sol_grad_mm = meshmode_src_connection.from_firedrake(dg_true_sol_grad, actx=actx) true_sol_grad_mm = src_bdy_connection(true_sol_grad_mm) # Apply the operations f_grad_convoluted_mm = rhs_grad_op(actx, sigma=true_sol_grad_mm, k=wave_number) f_convoluted_mm = rhs_op(actx, sigma=true_sol_grad_mm, k=wave_number) # Transfer function back to firedrake f_grad_convoluted = Function(vfspace) f_convoluted = Function(fspace) f_grad_convoluted.dat.data[:] = 0.0 f_convoluted.dat.data[:] = 0.0 for dim in range(f_grad_convoluted_mm.shape[0]): f_grad_convoluted.dat.data[target_indices, dim] = f_grad_convoluted_mm[dim].get() f_convoluted.dat.data[target_indices] = f_convoluted_mm.get() r""" \langle f, v \rangle_\Gamma + \langle i \kappa \cdot \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y), v \rangle_\Sigma - \langle n(x) \cdot \nabla( \int_\Gamma( f(y) H_0^{(1)}(\kappa |x - y|) )d\gamma(y) ), v \rangle_\Sigma """ rhs_form = inner(inner(true_sol_grad_expr, FacetNormal(mesh)), v) * ds(scatterer_bdy_id, metadata=metadata) \ + inner(f_convoluted, v) * ds(outer_bdy_id) \ - inner(inner(f_grad_convoluted, FacetNormal(mesh)), v) * ds(outer_bdy_id) rhs = assemble(rhs_form) # {{{ set up a solver: solution = Function(fspace, name="Computed Solution") # {{{ Used for preconditioning if 'gamma' in solver_parameters or 'beta' in solver_parameters: gamma = complex(solver_parameters.pop('gamma', 1.0)) import cmath beta = complex(solver_parameters.pop('beta', cmath.sqrt(gamma))) p = inner(grad(u), grad(v)) * dx \ - Constant(wave_number**2 * gamma) * inner(u, v) * dx \ - Constant(1j * wave_number * beta) * inner(u, v) * ds(outer_bdy_id) P = assemble(p).M.handle else: P = A # }}} # Set up options to contain solver parameters: ksp = PETSc.KSP().create() if solver_parameters['pc_type'] == 'pyamg': del solver_parameters['pc_type'] # We are using the AMG preconditioner pyamg_tol = solver_parameters.get('pyamg_tol', None) if pyamg_tol is not None: pyamg_tol = float(pyamg_tol) pyamg_maxiter = solver_parameters.get('pyamg_maxiter', None) if pyamg_maxiter is not None: pyamg_maxiter = int(pyamg_maxiter) ksp.setOperators(B) ksp.setUp() pc = ksp.pc pc.setType(pc.Type.PYTHON) pc.setPythonContext( AMGTransmissionPreconditioner(wave_number, fspace, A, tol=pyamg_tol, maxiter=pyamg_maxiter, use_plane_waves=True)) # Otherwise use regular preconditioner else: ksp.setOperators(B, P) options_manager = OptionsManager(solver_parameters, options_prefix) options_manager.set_from_options(ksp) import petsc4py.PETSc petsc4py.PETSc.Sys.popErrorHandler() with rhs.dat.vec_ro as b: with solution.dat.vec as x: ksp.solve(b, x) # }}} return ksp, solution
def nabla_phi_bar(phi): return sqrt(inner(grad(phi), grad(phi)))
def solve(self, phi: fd.Function, iters: int = 5) -> fd.Function: marking = fd.Function(self.DG0) marking_bc_nodes = fd.Function(self.V) # Mark cells cut by phi(x) = 0 domain = "{[i, j]: 0 <= i < b.dofs}" instructions = """ <float64> min_value = 1e20 <float64> max_value = -1e20 for i min_value = fmin(min_value, b[i, 0]) max_value = fmax(max_value, b[i, 0]) end a[0, 0] = 1.0 if (min_value < 0 and max_value > 0) else 0.0 """ fd.par_loop( (domain, instructions), dx, { "a": (marking, RW), "b": (phi, READ) }, is_loopy_kernel=True, ) # Mark the nodes in the marked cells fd.par_loop( ("{[i] : 0 <= i < A.dofs}", "A[i, 0] = fmax(A[i, 0], B[0, 0])"), dx, { "A": (marking_bc_nodes, RW), "B": (marking, READ) }, is_loopy_kernel=True, ) # Mark the nodes in the marked cells # Project the gradient of phi on the cut cells self.phi.assign(phi) V = self.V rho, sigma = fd.TrialFunction(V), fd.TestFunction(V) a = rho * sigma * marking * dx L_proj = (self.phi / sqrt(inner(grad(self.phi), grad(self.phi))) * marking * sigma * dx) bc_proj = BCOut(V, fd.Constant(0.0), marking_bc_nodes) self.A_proj = fd.assemble(a, tensor=self.A_proj, bcs=bc_proj) b_proj = fd.assemble(L_proj, bcs=bc_proj) solver_proj = fd.LinearSolver(self.A_proj, solver_parameters=self.solver_parameters) solver_proj.solve(self.phi_int, b_proj) def nabla_phi_bar(phi): return sqrt(inner(grad(phi), grad(phi))) def d1(s): return fd.Constant(1.0) - fd.Constant(1.0) / s def d2(s): return conditional(le(s, fd.Constant(1.0)), s - fd.Constant(1.0), d1(s)) def residual_phi(phi): return fd.norm( fd.assemble( d2(nabla_phi_bar(phi)) * inner(grad(phi), grad(sigma)) * dx)) a = inner(grad(rho), grad(sigma)) * dx L = (inner( (-d2(nabla_phi_bar(self.phi)) + fd.Constant(1.0)) * grad(self.phi), grad(sigma), ) * dx) bc = BCInt(V, self.phi_int, marking_bc_nodes) phi_sol = fd.Function(V) A = fd.assemble(a, bcs=bc) b = fd.assemble(L, bcs=bc) solver = fd.LinearSolver(A, solver_parameters=self.solver_parameters) # Solve the Signed distance equation with Picard iteration bc.apply(phi_sol) init_res = residual_phi(phi_sol) res = 1e10 it = 0 while res > init_res or it < iters: solver.solve(phi_sol, b) self.phi.assign(phi_sol) b = fd.assemble(L, bcs=bc, tensor=b) it += 1 res = residual_phi(phi_sol) if res > init_res: fd.warning( f"Residual in signed distance function increased: {res}, before: {init_res}" ) return self.phi
def residual_phi(phi): return fd.norm( fd.assemble( d2(nabla_phi_bar(phi)) * inner(grad(phi), grad(sigma)) * dx))
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 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
Khet = fd.as_tensor(((1.0 + x, 0, 0), (0, 1.0+y, 0), (0, 0, 1.0+z))) #Khet = fd.as_tensor(((1,0,0),(0,1,0),(0,0,1))) #Khet = fd.as_tensor(((1,0),(0,1))) Khet = fd.Function(W).interpolate(Khet) Khet.rename('K', 'Permeability') # Porosity por = fd.Constant(1.0) # We can now define the bilinear and linear forms for the left and right # hand sides of our equation respectively:: dx = fd.dx a = (fd.dot(Khet * fd.grad(u), fd.grad(v))) * dx m = u * v * por * dx # Defining the eigenvalue problem petsc_a = fd.assemble(a).M.handle petsc_m = fd.assemble(m).M.handle num_eigenvalues = 20 # Set solver options opts = PETSc.Options() opts.setValue("eps_gen_hermitian", None) #opts.setValue("st_pc_factor_shift_type", "NONZERO") opts.setValue("eps_type", "krylovschur") #opts.setValue("eps_smallest_magnitude", None)
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 # Set solver options opts = PETSc.Options() opts.setValue("eps_gen_hermitian", None) #opts.setValue("st_pc_factor_shift_type", "NONZERO") opts.setValue("eps_type", "krylovschur") #opts.setValue("eps_smallest_magnitude", None)
def _setup_solver(self): state = self.state # just cutting down line length a bit dt = state.timestepping.dt beta = dt*state.timestepping.alpha mu = state.mu # Split up the rhs vector (symbolically) u_in, p_in, b_in = split(state.xrhs) # Build the reduced function space for u,p M = MixedFunctionSpace((state.V[0], state.V[1])) w, phi = TestFunctions(M) u, p = TrialFunctions(M) # Get background fields bbar = state.bbar # Analytical (approximate) elimination of theta k = state.k # Upward pointing unit vector b = -dot(k,u)*dot(k,grad(bbar))*beta + b_in # vertical projection def V(u): return k*inner(u,k) eqn = ( inner(w, (u - u_in))*dx - beta*div(w)*p*dx - beta*inner(w,k)*b*dx + phi*div(u)*dx ) 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 p solver self.up = 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")] # preconditioner equation L = self.L Ap = ( inner(w,u) + L*L*div(w)*div(u) + phi*p/L/L )*dx # Solver for u, p up_problem = LinearVariationalProblem( aeqn, Leqn, self.up, bcs=bcs, aP=Ap) nullspace = MixedVectorSpaceBasis(M, [M.sub(0), VectorSpaceBasis(constant=True)]) self.up_solver = LinearVariationalSolver(up_problem, solver_parameters=self.params, nullspace=nullspace) # Reconstruction of b b = TrialFunction(state.V[2]) gamma = TestFunction(state.V[2]) u, p = self.up.split() self.b = Function(state.V[2]) b_eqn = gamma*(b - b_in + dot(k,u)*dot(k,grad(bbar))*beta)*dx b_problem = LinearVariationalProblem(lhs(b_eqn), rhs(b_eqn), self.b) self.b_solver = LinearVariationalSolver(b_problem)
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 initialize(self, pc): from firedrake import TrialFunction, TestFunction, dx, \ assemble, inner, grad, split, Constant, parameters from firedrake.assemble import allocate_matrix, create_assembly_callable prefix = pc.getOptionsPrefix() + "pcd_" # we assume P has things stuffed inside of it _, P = pc.getOperators() context = P.getPythonContext() test, trial = context.a.arguments() if test.function_space() != trial.function_space(): raise ValueError("Pressure space test and trial space differ") Q = test.function_space() p = TrialFunction(Q) q = TestFunction(Q) mass = p*q*dx # Regularisation to avoid having to think about nullspaces. stiffness = inner(grad(p), grad(q))*dx + Constant(1e-6)*p*q*dx opts = PETSc.Options() # we're inverting Mp and Kp, so default them to assembled. # Fp only needs its action, so default it to mat-free. # These can of course be overridden. # only Fp is referred to in update, so that's the only # one we stash. default = parameters["default_matrix_type"] Mp_mat_type = opts.getString(prefix+"Mp_mat_type", default) Kp_mat_type = opts.getString(prefix+"Kp_mat_type", default) self.Fp_mat_type = opts.getString(prefix+"Fp_mat_type", "matfree") Mp = assemble(mass, form_compiler_parameters=context.fc_params, mat_type=Mp_mat_type) Kp = assemble(stiffness, form_compiler_parameters=context.fc_params, mat_type=Kp_mat_type) Mp.force_evaluation() Kp.force_evaluation() # FIXME: Should we transfer nullspaces over. I think not. Mksp = PETSc.KSP().create() Mksp.setOptionsPrefix(prefix + "Mp_") Mksp.setOperators(Mp.petscmat) Mksp.setUp() Mksp.setFromOptions() self.Mksp = Mksp Kksp = PETSc.KSP().create() Kksp.setOptionsPrefix(prefix + "Kp_") Kksp.setOperators(Kp.petscmat) Kksp.setUp() Kksp.setFromOptions() self.Kksp = Kksp state = context.appctx["state"] Re = context.appctx.get("Re", 1.0) velid = context.appctx["velocity_space"] u0 = split(state)[velid] fp = 1.0/Re * inner(grad(p), grad(q))*dx + inner(u0, grad(p))*q*dx self.Re = Re self.Fp = allocate_matrix(fp, form_compiler_parameters=context.fc_params, mat_type=self.Fp_mat_type) self._assemble_Fp = create_assembly_callable(fp, tensor=self.Fp, form_compiler_parameters=context.fc_params, mat_type=self.Fp_mat_type) self._assemble_Fp() self.Fp.force_evaluation() Fpmat = self.Fp.petscmat self.workspace = [Fpmat.createVecLeft() for i in (0, 1)]