def get_dwr_indicator(F, adjoint_error, test_space=None): """ Generate a dual weighted residual (DWR) error indicator, given a form and an approximation of the error in the adjoint solution. :arg F: the form :arg adjoint_error: the approximation to the adjoint error, either as a single :class:`Function`, or in a dictionary :kwarg test_space: the :class:`FunctionSpace` that the test function lives in, or an appropriate dictionary """ mapping = {} if isinstance(adjoint_error, firedrake.Function): fs = test_space or adjoint_error.function_space() if F.ufl_domain() != fs.mesh(): raise ValueError( "Meshes underlying the form and adjoint error do not match.") mapping[firedrake.TestFunction(fs)] = adjoint_error else: if test_space is None: test_space = { key: firedrake.TestFunction(err.function_space()) for key, err in adjoint_error.items() } for key, err in adjoint_error.items(): if F.ufl_domain() != test_space[key].mesh(): raise ValueError( "Meshes underlying the form and adjoint error do not match." ) mapping[firedrake.TestFunction(test_space[key])] = err return form2indicator(ufl.replace(F, mapping))
def __init__(self, mesh_r): # Create mesh_r and V_r self.mesh_r = mesh_r element = self.mesh_r.coordinates.function_space().ufl_element() self.V_r = fd.FunctionSpace(self.mesh_r, element) # Create self.id and self.T, self.mesh_m, and self.V_m. X = fd.SpatialCoordinate(self.mesh_r) self.id = fd.interpolate(X, self.V_r) self.T = fd.Function(self.V_r, name="T") self.T.assign(self.id) self.mesh_m = fd.Mesh(self.T) self.V_m = fd.FunctionSpace(self.mesh_m, element) self.is_DG = False """ ControlSpace for discontinuous coordinate fields (e.g. periodic domains) In Firedrake, periodic meshes are implemented using a discontinuous field. This implies that self.V_r contains discontinuous functions. To ensure domain updates do not create holes in the domain, use a continuous subspace self.V_c of self.V_r as control space. """ if element.family() == 'Discontinuous Lagrange': self.is_DG = True self.V_c = fd.VectorFunctionSpace(self.mesh_r, "CG", element._degree) self.Ip = fd.Interpolator(fd.TestFunction(self.V_c), self.V_r).callable().handle
def __init__(self, function_space, model_parameters): super(AllenCahnHeatModel, self).__init__() self._solution_function = fd.Function(function_space) self._solutiondot_function = fd.Function(function_space) test_function = fd.TestFunction(function_space) # Split mixed FE function into separate functions and put them into namedtuple FiniteElementFunction = namedtuple("FiniteElementFunction", ["phi", "T"]) split_solution_function = FiniteElementFunction( *ufl.split(self._solution_function)) split_solutiondot_function = FiniteElementFunction( *ufl.split(self._solutiondot_function)) split_test_function = FiniteElementFunction(*ufl.split(test_function)) finite_element_functions = ( split_solution_function, split_solutiondot_function, split_test_function, ) F_AC = get_allen_cahn_weak_form(*finite_element_functions, model_parameters) F_heat = get_heat_weak_form(*finite_element_functions, model_parameters) F = F_AC + F_heat self._residual = F
def __init__(self, equation, solution, fields, dt, solver_parameters={}): """ :arg equation: the equation to solve :type equation: :class:`BaseEquation` object :arg solution: :class:`Function` where solution will be stored :arg fields: Dictionary of fields that are passed to the equation :type fields: dict of :class:`Function` or :class:`Constant` objects :arg float dt: time step in seconds :kwarg dict solver_parameters: PETSc solver options """ super(TimeIntegrator, self).__init__() self.equation = equation self.test = firedrake.TestFunction(solution.function_space()) self.solution = solution self.fields = fields self.dt = dt self.dt_const = firedrake.Constant(dt) # unique identifier used in solver self.name = '-'.join( [self.__class__.__name__, self.equation.__class__.__name__]) self.solver_parameters = {} self.solver_parameters.update(solver_parameters)
def get_form(self, V): u = fd.TrialFunction(V) v = fd.TestFunction(V) base = self.base_form.get_form(V) n = fd.FacetNormal(V.ufl_domain()) dim = V.mesh().topological_dimension() def h(domain): if dim == 3: return (fd.FacetArea(domain) * 2)**(1. / 2) else: return fd.FacetArea(domain) h = h(V.mesh()) alpha = fd.Constant(10) from firedrake import div, grad, dS, dx, inner, jump, avg, ds 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 form = base + self.mu * (sum(form(u[i], v[i]) for i in range(dim))) return form
def _advect(self, dt, E, u, w, h, s, E_inflow, E_surface): Q = E.function_space() φ, ψ = firedrake.TrialFunction(Q), firedrake.TestFunction(Q) U = firedrake.as_vector((u[0], u[1], w)) flux_cells = -φ * inner(U, grad(ψ)) * h * dx mesh = Q.mesh() ν = facet_normal_2(mesh) outflow = firedrake.max_value(inner(u, ν), 0) inflow = firedrake.min_value(inner(u, ν), 0) flux_outflow = φ * ψ * outflow * h * ds_v + \ φ * ψ * firedrake.max_value(-w, 0) * h * ds_b + \ φ * ψ * firedrake.max_value(+w, 0) * h * ds_t F = φ * ψ * h * dx + dt * (flux_cells + flux_outflow) flux_inflow = -E_inflow * ψ * inflow * h * ds_v \ -E_surface * ψ * firedrake.min_value(-w, 0) * h * ds_b \ -E_surface * ψ * firedrake.min_value(+w, 0) * h * ds_t A = E * ψ * h * dx + dt * flux_inflow solver_parameters = {'ksp_type': 'preonly', 'pc_type': 'lu'} degree_E = E.ufl_element().degree() degree_u = u.ufl_element().degree() degree = (3 * degree_E[0] + degree_u[0], 2 * degree_E[1] + degree_u[1]) form_compiler_parameters = {'quadrature_degree': degree} firedrake.solve(F == A, E, solver_parameters=solver_parameters, form_compiler_parameters=form_compiler_parameters)
def advective_flux(self, **kwargs): keys = ('energy', 'velocity', 'vertical_velocity', 'thickness', 'energy_inflow', 'energy_surface') keys_alt = ('E', 'u', 'w', 'h', 'E_inflow', 'E_surface') E, u, w, h, E_inflow, E_surface = get_kwargs_alt( kwargs, keys, keys_alt) Q = E.function_space() ψ = firedrake.TestFunction(Q) U = firedrake.as_vector((u[0], u[1], w)) flux_cells = -E * inner(U, grad(ψ)) * h * dx mesh = Q.mesh() ν = facet_normal_2(mesh) outflow = firedrake.max_value(inner(u, ν), 0) inflow = firedrake.min_value(inner(u, ν), 0) flux_outflow = (E * outflow * ψ * h * ds_v + E * firedrake.max_value(-w, 0) * ψ * h * ds_b + E * firedrake.max_value(+w, 0) * ψ * h * ds_t) flux_inflow = (E_inflow * inflow * ψ * h * ds_v + E_surface * firedrake.min_value(-w, 0) * ψ * h * ds_b + E_surface * firedrake.min_value(+w, 0) * ψ * h * ds_t) return flux_cells + flux_outflow + flux_inflow
def get_facet_areas(mesh): """ Compute area of each facet of `mesh`. The facet areas are stored as a HDiv trace field. Note that the plus sign is arbitrary and could equally well be chosen as minus. :arg mesh: the input mesh to do computations on :rtype: firedrake.function.Function facet_areas with facet area data """ HDivTrace = firedrake.FunctionSpace(mesh, "HDiv Trace", 0) v = firedrake.TestFunction(HDivTrace) u = firedrake.TrialFunction(HDivTrace) facet_areas = firedrake.Function(HDivTrace, name="Facet areas") mass_term = v("+") * u("+") * ufl.dS + v * u * ufl.ds rhs = v("+") * ufl.FacetArea(mesh) * ufl.dS + v * ufl.FacetArea(mesh) * ufl.ds sp = { "snes_type": "ksponly", "ksp_type": "preonly", "pc_type": "jacobi", } firedrake.solve(mass_term == rhs, facet_areas, solver_parameters=sp) return facet_areas
def advective_flux(self, **kwargs): keys = ( "energy", "velocity", "vertical_velocity", "thickness", "energy_inflow", "energy_surface", ) E, u, w, h, E_inflow, E_surface = itemgetter(*keys)(kwargs) Q = E.function_space() ψ = firedrake.TestFunction(Q) # NOTE: Be careful here going to xz! You might have to separate this into # the sum of a horizontal and vertical flux if we're shadowing Firedrake's # grad operator with out own specialized one. U = firedrake.as_vector((u[0], u[1], w)) flux_cells = -E * inner(U, grad(ψ)) * h * dx ν = FacetNormal(Q.mesh()) outflow = firedrake.max_value(inner(u, ν), 0) inflow = firedrake.min_value(inner(u, ν), 0) flux_outflow = (E * outflow * ψ * h * ds_v + E * firedrake.max_value(-w, 0) * ψ * h * ds_b + E * firedrake.max_value(+w, 0) * ψ * h * ds_t) flux_inflow = (E_inflow * inflow * ψ * h * ds_v + E_surface * firedrake.min_value(-w, 0) * ψ * h * ds_b + E_surface * firedrake.min_value(+w, 0) * ψ * h * ds_t) return flux_cells + flux_outflow + flux_inflow
def AdvectionDiffusionGLS( V: fd.FunctionSpace, theta: fd.Function, phi: fd.Function, PeInv: float = 1e-4, phi_t: fd.Function = None, ): PeInv_ct = fd.Constant(PeInv) rho = fd.TestFunction(V) F = (inner(theta, grad(phi)) * rho + PeInv_ct * inner(grad(phi), grad(rho))) * dx if phi_t: F += phi_t * rho * dx h = fd.CellDiameter(V.ufl_domain()) R_U = dot(theta, grad(phi)) - PeInv_ct * div(grad(phi)) if phi_t: R_U += phi_t beta_gls = 0.9 tau_gls = beta_gls * ((4.0 * dot(theta, theta) / h**2) + 9.0 * (4.0 * PeInv_ct / h**2)**2)**(-0.5) theta_U = dot(theta, grad(rho)) - PeInv_ct * div(grad(rho)) F += tau_gls * inner(R_U, theta_U) * dx() return F
def _initialise_problem(self): """ Set up the Firedrake machinery for solving the problem. """ # Define trial and test functions on the space self._u = fd.TrialFunction(self.V) self._v = fd.TestFunction(self.V) # Define sesquilinear form and antilinear functional self._a = self._define_form(self._A, self._n) self._set_L() self._set_pre() # Define problem and solver (following code courtesy of Lawrence # Mitchell, via Slack) problem = fd.LinearVariationalProblem(self._a, self._L, self.u_h, aP=self._a_pre, constant_jacobian=False) self._solver = fd.LinearVariationalSolver( problem, solver_parameters=self._solver_parameters) self._initialised = True
def create_form(form_string, family, degree, dimension, operation): from firedrake import dx, inner, grad import firedrake as f f.parameters['coffee']['optlevel'] = 'O3' f.parameters['pyop2_options']['opt_level'] = 'O3' f.parameters['pyop2_options']['simd_isa'] = 'avx' f.parameters["pyop2_options"]["lazy_evaluation"] = False m = f.UnitSquareMesh(2, 2, quadrilateral=True) if dimension == 3: m = f.ExtrudedMesh(m, 2) fs = f.FunctionSpace(m, family, degree) v = f.TestFunction(fs) if operation == "matrix": u = f.TrialFunction(fs) elif operation == "action": u = f.Function(fs) else: raise ValueError("Unknown operation: %s" % operation) if form_string == "u * v * dx": return u * v * dx elif form_string == "inner(grad(u), grad(v)) * dx": return inner(grad(u), grad(v)) * dx
def make_kernels(self, Vf, Vc): """ Interpolation and restriction kernels between arbitrary elements. This is temporary while we wait for structure-preserving tfsc kernels. """ self.prolong_kernel = self.prolongation_transfer_kernel_action( Vf, self.uc) matrix_kernel = self.prolongation_transfer_kernel_action( Vf, firedrake.TestFunction(Vc)) # The way we transpose the prolongation kernel is suboptimal. # A local matrix is generated each time the kernel is executed. element_kernel = loopy.generate_code_v2( matrix_kernel.code).device_code() element_kernel = element_kernel.replace( "void expression_kernel", "static void expression_kernel") dimc = Vc.finat_element.space_dimension() * Vc.value_size dimf = Vf.finat_element.space_dimension() * Vf.value_size restrict_code = f""" {element_kernel} void restriction({ScalarType_c} *restrict Rc, const {ScalarType_c} *restrict Rf, const {ScalarType_c} *restrict w) {{ {ScalarType_c} Afc[{dimf}*{dimc}] = {{0}}; expression_kernel(Afc); for ({IntType_c} i = 0; i < {dimf}; i++) for ({IntType_c} j = 0; j < {dimc}; j++) Rc[j] += Afc[i*{dimc} + j] * Rf[i] * w[i]; }} """ self.restrict_kernel = op2.Kernel(restrict_code, "restriction")
def define_functions(self): self.X = fd.Function(self.V, name="X") # displacement, not position self.U = fd.Function(self.V, name="U") # velocity self.trial = fd.TrialFunction(self.V) # self.phi_vect = fd.Function(self.V, name="Surface forcing") #Fgr = Function(V, name="Gravity") self.v = fd.TestFunction(self.V)
def __init__(self, mesh_m): super().__init__() # Setup problem V = fd.FunctionSpace(mesh_m, "CG", 1) # Weak form of Poisson problem u = fda.Function(V, name="State") v = fd.TestFunction(V) f = fda.Constant(4.) F = (fd.inner(fd.grad(u), fd.grad(v)) - f * v) * fd.dx bcs = fda.DirichletBC(V, 0., "on_boundary") # PDE-solver parameters params = { "ksp_type": "cg", "mat_type": "aij", "pc_type": "hypre", "pc_factor_mat_solver_package": "boomerang", "ksp_rtol": 1e-11, "ksp_atol": 1e-11, "ksp_stol": 1e-15, } self.solution = u problem = fda.NonlinearVariationalProblem(F, self.solution, bcs=bcs) self.solver = fda.NonlinearVariationalSolver(problem, solver_parameters=params)
def __init__(self, problem, callback=(lambda s: None), memory=5): self._setup(problem, callback) self.update_state() self.update_adjoint_state() Q = self.parameter.function_space() self._memory = memory q, dJ = self.search_direction, self.gradient M = firedrake.TrialFunction(Q) * firedrake.TestFunction(Q) * dx f = firedrake.Function(Q) problem = firedrake.LinearVariationalProblem( M, dJ, f, form_compiler_parameters=self._fc_params) self._search_direction_solver = firedrake.LinearVariationalSolver( problem, solver_parameters=self._solver_params) self._search_direction_solver.solve() q.assign(-f) self._f = f self._rho = [] self._ps = [self.parameter.copy(deepcopy=True)] self._fs = [q.copy(deepcopy=True)] self._fs[-1] *= -1 self._callback(self)
def setup(self, **kwargs): r"""Create the internal data structures that help reuse information from past prognostic solves""" for name, field in kwargs.items(): if name in self._fields.keys(): self._fields[name].assign(field) else: if isinstance(field, firedrake.Constant): self._fields[name] = firedrake.Constant(field) elif isinstance(field, firedrake.Function): self._fields[name] = field.copy(deepcopy=True) else: raise TypeError( 'Input fields must be Constant or Function!') dt = firedrake.Constant(1.) dh_dt = self._continuity(dt, **self._fields) h = self._fields.get('thickness', self._fields.get('h')) h_0 = h.copy(deepcopy=True) q = firedrake.TestFunction(h.function_space()) F = (h - h_0) * q * dx - dt * dh_dt problem = firedrake.NonlinearVariationalProblem(F, h) self._solver = firedrake.NonlinearVariationalSolver( problem, solver_parameters=self._solver_parameters) self._thickness_old = h_0 self._timestep = dt
def __init__(self, V, fixed_dims=[], direct_solve=False): if isinstance(fixed_dims, int): fixed_dims = [fixed_dims] self.V = V self.fixed_dims = fixed_dims self.direct_solve = direct_solve self.zero = fd.Constant(V.mesh().topological_dimension() * (0, )) u = fd.TrialFunction(V) v = fd.TestFunction(V) self.zero_fun = fd.Function(V) self.a = 1e-2 * \ fd.inner(u, v) * fd.dx + fd.inner(fd.sym(fd.grad(u)), fd.sym(fd.grad(v))) * fd.dx self.bc_fun = fd.Function(V) if len(self.fixed_dims) == 0: bcs = [fd.DirichletBC(self.V, self.bc_fun, "on_boundary")] else: bcs = [] for i in range(self.V.mesh().topological_dimension()): if i in self.fixed_dims: bcs.append(fd.DirichletBC(self.V.sub(i), 0, "on_boundary")) else: bcs.append( fd.DirichletBC(self.V.sub(i), self.bc_fun.sub(i), "on_boundary")) self.A_ext = fd.assemble(self.a, bcs=bcs, mat_type="aij") self.ls_ext = fd.LinearSolver(self.A_ext, solver_parameters=self.get_params()) self.A_adj = fd.assemble(self.a, bcs=fd.DirichletBC(self.V, self.zero, "on_boundary"), mat_type="aij") self.ls_adj = fd.LinearSolver(self.A_adj, solver_parameters=self.get_params())
def _setup(self, **kwargs): for name, field in kwargs.items(): if name in self._fields.keys(): self._fields[name].assign(field) else: if isinstance(field, firedrake.Constant): self._fields[name] = firedrake.Constant(field) elif isinstance(field, firedrake.Function): self._fields[name] = field.copy(deepcopy=True) else: raise TypeError( "Input %s field has type %s, must be Constant or Function!" % (name, type(field))) dt = Constant(1.0) aflux = self.model.advective_flux(**self.fields) dflux = self.model.diffusive_flux(**self.fields) sources = self.model.sources(**self.fields) dE_dt = sources - aflux - dflux E, h = itemgetter("energy", "thickness")(self.fields) E_0 = E.copy(deepcopy=True) ψ = firedrake.TestFunction(E.function_space()) F = (E - E_0) * ψ * h * dx - dt * dE_dt degree = E.ufl_element().degree() fc_params = {"quadrature_degree": (3 * degree[0], 2 * degree[1])} problem = firedrake.NonlinearVariationalProblem( F, E, form_compiler_parameters=fc_params) self._solver = firedrake.NonlinearVariationalSolver( problem, solver_parameters=self._solver_parameters) self._energy_old = E_0 self._timestep = dt
def _setup(self, **kwargs): for name, field in kwargs.items(): if name in self.fields.keys(): self.fields[name].assign(field) else: self.fields[name] = utilities.copy(field) dt = Constant(1.) aflux = self.model.advective_flux(**self.fields) dflux = self.model.diffusive_flux(**self.fields) sources = self.model.sources(**self.fields) dE_dt = sources - aflux - dflux E = self.fields.get('energy', self.fields.get('E')) h = self.fields.get('thickness', self.fields.get('h')) E_0 = E.copy(deepcopy=True) ψ = firedrake.TestFunction(E.function_space()) F = (E - E_0) * ψ * h * dx - dt * dE_dt degree = E.ufl_element().degree() fc_params = {'quadrature_degree': (3 * degree[0], 2 * degree[1])} problem = firedrake.NonlinearVariationalProblem( F, E, form_compiler_parameters=fc_params ) self._solver = firedrake.NonlinearVariationalSolver( problem, solver_parameters=self._solver_parameters ) self._energy_old = E_0 self._timestep = dt
def __init__(self, mesh_m): super().__init__() self.mesh_m = mesh_m # Setup problem self.V = fd.FunctionSpace(self.mesh_m, "CG", 1) # Preallocate solution variables for state and adjoint equations self.solution = fd.Function(self.V, name="State") # Weak form of Poisson problem u = self.solution v = fd.TestFunction(self.V) self.f = fd.Constant(4.) self.F = (fd.inner(fd.grad(u), fd.grad(v)) - self.f * v) * fd.dx self.bcs = fd.DirichletBC(self.V, 0., "on_boundary") # PDE-solver parameters self.params = { "ksp_type": "cg", "mat_type": "aij", "pc_type": "hypre", "pc_factor_mat_solver_package": "boomerang", "ksp_rtol": 1e-11, "ksp_atol": 1e-11, "ksp_stol": 1e-15, } stateproblem = fd.NonlinearVariationalProblem(self.F, self.solution, bcs=self.bcs) self.solver = fd.NonlinearVariationalSolver( stateproblem, solver_parameters=self.params)
def A(self): u = firedrake.TrialFunction(self.target.function_space()) v = firedrake.TestFunction(self.target.function_space()) a = firedrake.inner(u, v)*firedrake.dx if self.use_slate_for_inverse: a = firedrake.Tensor(a).inv A = firedrake.assemble(a, bcs=self.bcs, form_compiler_parameters=self.form_compiler_parameters) return A
def V_approx_inv_mass(self, V, DG): """ Approximate inverse mass. Computes (cellwise) (V, V)^{-1} (V, DG). :arg V: a function space :arg DG: the DG space :returns: A PETSc Mat mapping from V -> DG. """ key = V.dim() try: return self._V_approx_inv_mass[key] except KeyError: a = firedrake.Tensor(firedrake.inner(firedrake.TestFunction(V), firedrake.TrialFunction(V))*firedrake.dx) b = firedrake.Tensor(firedrake.inner(firedrake.TestFunction(V), firedrake.TrialFunction(DG))*firedrake.dx) M = firedrake.assemble(a.inv * b) M.force_evaluation() return self._V_approx_inv_mass.setdefault(key, M.petscmat)
def solve_something(mesh): V = fd.FunctionSpace(mesh, "CG", 1) u = fd.Function(V) v = fd.TestFunction(V) x, y = fd.SpatialCoordinate(mesh) # f = fd.sin(x) * fd.sin(y) + x**2 + y**2 # uex = x**4 * y**4 uex = fd.sin(x) * fd.sin(y) #*(x*y)**3 # def source(xs, ys): # return 1/((x-xs)**2+(y-ys)**2 + 0.1) # uex = source(0, 0) uex = uex - fd.assemble(uex * fd.dx) / fd.assemble(1 * fd.dx(domain=mesh)) # f = fd.conditional(fd.ge(abs(x)-abs(y), 0), 1, 0) from firedrake import inner, grad, dx, ds, div, sym eps = fd.Constant(0.0) f = uex - div(grad(uex)) + eps * div(grad(div(grad(uex)))) n = fd.FacetNormal(mesh) g = inner(grad(uex), n) g1 = inner(grad(div(grad(uex))), n) g2 = div(grad(uex)) # F = 0.1 * inner(u, v) * dx + inner(grad(u), grad(v)) * dx + inner(grad(grad(u)), grad(grad(v))) * dx - f * v * dx - g * v * ds F = inner(u, v) * dx + inner(grad(u), grad(v)) * dx - f * v * dx - g * v * ds F += eps * inner(div(grad(u)), div(grad(v))) * dx F += eps * g1 * v * ds F -= eps * g2 * inner(grad(v), n) * ds # f = -div(grad(uex)) # F = inner(grad(u), grad(v)) * dx - f * v * dx # bc = fd.DirichletBC(V, uex, "on_boundary") bc = None fd.solve(F == 0, u, bcs=bc, solver_parameters={ "ksp_type": "cg", "ksp_atol": 1e-13, "ksp_rtol": 1e-13, "ksp_dtol": 1e-13, "ksp_stol": 1e-13, "pc_type": "jacobi", "pc_factor_mat_solver_type": "mumps", "snes_type": "ksponly", "ksp_converged_reason": None }) print("||u-uex|| =", fd.norm(u - uex)) print("||grad(u-uex)|| =", fd.norm(grad(u - uex))) print("||grad(grad(u-uex))|| =", fd.norm(grad(grad(u - uex)))) err = fd.Function( fd.TensorFunctionSpace(mesh, "DG", V.ufl_element().degree() - 2)).interpolate( grad(grad(u - uex))) # err = fd.Function(fd.FunctionSpace(mesh, "DG", V.ufl_element().degree())).interpolate(u-uex) fd.File(outdir + "sln.pvd").write(u) fd.File(outdir + "err.pvd").write(err)
def evaluate_adj_component(self, inputs, adj_inputs, block_variable, idx, prepared=None): if not self.linear and self.func == block_variable.output: # We are not able to calculate derivatives wrt initial guess. return None F_form = prepared["form"] adj_sol = prepared["adj_sol"] adj_sol_bdy = prepared["adj_sol_bdy"] c = block_variable.output c_rep = block_variable.saved_output if isinstance(c, firedrake.Function): trial_function = firedrake.TrialFunction(c.function_space()) elif isinstance(c, firedrake.Constant): mesh = self.compat.extract_mesh_from_form(F_form) trial_function = firedrake.TrialFunction( c._ad_function_space(mesh)) elif isinstance(c, firedrake.DirichletBC): tmp_bc = self.compat.create_bc( c, value=self.compat.extract_subfunction(adj_sol_bdy, c.function_space())) return [tmp_bc] elif isinstance(c, self.compat.MeshType): # Using CoordianteDerivative requires us to do action before # differentiating, might change in the future. F_form_tmp = firedrake.action(F_form, adj_sol) X = firedrake.SpatialCoordinate(c_rep) dFdm = firedrake.derivative( -F_form_tmp, X, firedrake.TestFunction(c._ad_function_space())) dFdm = self.compat.assemble_adjoint_value(dFdm, **self.assemble_kwargs) return dFdm # dFdm_cache works with original variables, not block saved outputs. if c in self._dFdm_cache: dFdm = self._dFdm_cache[c] else: dFdm = -firedrake.derivative(self.lhs, c, trial_function) dFdm = firedrake.adjoint(dFdm) self._dFdm_cache[c] = dFdm # Replace the form coefficients with checkpointed values. replace_map = self._replace_map(dFdm) replace_map[self.func] = self.get_outputs()[0].saved_output dFdm = replace(dFdm, replace_map) dFdm = dFdm * adj_sol dFdm = self.compat.assemble_adjoint_value(dFdm, **self.assemble_kwargs) return dFdm
def restrict_kernel(Vf, Vc): hierarchy, level = utils.get_level(Vc.ufl_domain()) levelf = level + Fraction(1 / hierarchy.refinements_per_level) cache = hierarchy._shared_data_cache["transfer_kernels"] coordinates = Vc.ufl_domain().coordinates key = (("restrict", ) + Vf.ufl_element().value_shape() + entity_dofs_key(Vf.finat_element.entity_dofs()) + entity_dofs_key(Vc.finat_element.entity_dofs()) + entity_dofs_key( coordinates.function_space().finat_element.entity_dofs())) try: return cache[key] except KeyError: mesh = coordinates.ufl_domain() evaluate_kernel = compile_element(firedrake.TestFunction(Vc), Vf) to_reference_kernel = to_reference_coordinates( coordinates.ufl_element()) coords_element = create_element(coordinates.ufl_element()) element = create_element(Vc.ufl_element()) eval_args = evaluate_kernel.args[:-1] args = eval_args[-1].gencode(not_scope=True) R, fine = (a.sym.symbol for a in eval_args) my_kernel = """ %(to_reference)s %(evaluate)s __attribute__((noinline)) /* Clang bug */ static void pyop2_kernel_restrict(double *R, %(args)s, const double *X, const double *Xc) { double Xref[%(tdim)d]; int cell = -1; for (int i = 0; i < %(ncandidate)d; i++) { const double *Xci = Xc + i*%(Xc_cell_inc)d; to_reference_coords_kernel(Xref, X, Xci); if (%(inside_cell)s) { cell = i; const double *Ri = %(R)s + cell*%(coarse_cell_inc)d; pyop2_kernel_evaluate(Ri, %(fine)s, Xref); break; } } } """ % { "to_reference": str(to_reference_kernel), "evaluate": str(evaluate_kernel), "ncandidate": hierarchy.fine_to_coarse_cells[levelf].shape[1], "inside_cell": inside_check(element.cell, eps=1e-8, X="Xref"), "Xc_cell_inc": coords_element.space_dimension(), "coarse_cell_inc": element.space_dimension(), "args": args, "R": R, "fine": fine, "tdim": mesh.topological_dimension() } return cache.setdefault( key, op2.Kernel(my_kernel, name="pyop2_kernel_restrict"))
def update_search_direction(self): r"""Set the search direction to be the inverse of the mass matrix times the gradient of the objective""" q, dJ = self.search_direction, self.gradient Q = q.function_space() M = firedrake.TrialFunction(Q) * firedrake.TestFunction(Q) * dx firedrake.solve(M == -dJ, q, solver_parameters=self._solver_params, form_compiler_parameters=self._fc_params)
def derivative(self, out): """ Assemble partial directional derivative wrt ControlSpace perturbations. """ v = fd.TestFunction(self.V_control) fd.assemble(self.derivative_form(v), tensor=self.deriv_r, form_compiler_parameters=self.params) out.fun.assign(self.deriv_r) out.scale(self.scale)
def estimate_timestep(mesh, V, c, estimate_max_eigenvalue=True): """Estimate the maximum stable timestep based on the spectral radius using optionally the Gershgorin Circle Theorem to estimate the maximum generalized eigenvalue. Otherwise computes the maximum generalized eigenvalue exactly ONLY WORKS WITH KMV ELEMENTS """ u, v = fire.TrialFunction(V), fire.TestFunction(V) quad_rule = finat.quadrature.make_quadrature(V.finat_element.cell, V.ufl_element().degree(), "KMV") dxlump = fire.dx(rule=quad_rule) A = fire.assemble(u * v * dxlump) ai, aj, av = A.petscmat.getValuesCSR() av_inv = [] for value in av: if value == 0: av_inv.append(0.0) else: av_inv.append(1 / value) Asp = scipy.sparse.csr_matrix((av, aj, ai)) Asp_inv = scipy.sparse.csr_matrix((av_inv, aj, ai)) K = fire.assemble(c * c * dot(grad(u), grad(v)) * dxlump) ai, aj, av = K.petscmat.getValuesCSR() Ksp = scipy.sparse.csr_matrix((av, aj, ai)) # operator Lsp = Asp_inv.multiply(Ksp) if estimate_max_eigenvalue: # absolute maximum of diagonals max_eigval = np.amax(np.abs(Lsp.diagonal())) else: print( "Computing exact eigenvalues is extremely computationally demanding!", flush=True, ) max_eigval = scipy.sparse.linalg.eigs(Ksp, M=Asp, k=1, which="LM", return_eigenvectors=False)[0] # print(max_eigval) if np.sqrt(max_eigval) > 0.0: max_dt = np.float(2 / np.sqrt(max_eigval)) else: max_dt = 100000000 #print( # f"Maximum stable timestep should be about: {np.float(2 / np.sqrt(max_eigval))} seconds", # flush=True, #) return max_dt
def __init__(self, Q, free_bids=["on_boundary"]): (V, I_interp) = Q.get_space_for_inner() self.free_bids = free_bids u = fd.TrialFunction(V) v = fd.TestFunction(V) n = fd.FacetNormal(V.mesh()) def surf_grad(u): return fd.sym(fd.grad(u) - fd.outer(fd.grad(u) * n, n)) a = (fd.inner(surf_grad(u), surf_grad(v)) + fd.inner(u, v)) * fd.ds # petsc doesn't like matrices with zero rows a += 1e-10 * fd.inner(u, v) * fd.dx A = fd.assemble(a, mat_type="aij") A = A.petscmat tdim = V.mesh().topological_dimension() lsize = fd.Function(V).vector().local_size() def get_nodes_bc(bc): nodes = bc.nodes return nodes[nodes < lsize] def get_nodes_bid(bid): bc = fd.DirichletBC(V, fd.Constant(tdim * (0, )), bid) return get_nodes_bc(bc) free_nodes = np.concatenate( [get_nodes_bid(bid) for bid in self.free_bids]) free_dofs = np.concatenate( [tdim * free_nodes + i for i in range(tdim)]) free_dofs = np.unique(np.sort(free_dofs)) self.free_is = PETSc.IS().createGeneral(free_dofs) lgr, lgc = A.getLGMap() self.global_free_is_row = lgr.applyIS(self.free_is) self.global_free_is_col = lgc.applyIS(self.free_is) A = A.createSubMatrix(self.global_free_is_row, self.global_free_is_col) # A.view() A.assemble() self.A = A Aksp = PETSc.KSP().create() Aksp.setOperators(self.A) Aksp.setOptionsPrefix("A_") opts = PETSc.Options() opts["A_ksp_type"] = "cg" opts["A_pc_type"] = "hypre" opts["A_ksp_atol"] = 1e-10 opts["A_ksp_rtol"] = 1e-10 Aksp.setUp() Aksp.setFromOptions() self.Aksp = Aksp