def test_form_splitter_matrices(shape): mesh = UnitSquareMesh(dolfin.MPI.comm_self, 3, 3) Vu = FunctionSpace(mesh, 'DG', 2) Vp = FunctionSpace(mesh, 'DG', 1) def define_eq(u, v, p, q): "A simple Stokes-like coupled weak form" eq = Constant(2) * dot(u, v) * dx eq += dot(grad(p), v) * dx eq -= dot(Constant([1, 1]), v) * dx eq += dot(grad(q), u) * dx eq -= dot(Constant(0.3), q) * dx return eq if shape == (2, 2): eu = MixedElement([Vu.ufl_element(), Vu.ufl_element()]) ew = MixedElement([eu, Vp.ufl_element()]) W = FunctionSpace(mesh, ew) u, p = TrialFunctions(W) v, q = TestFunctions(W) u = as_vector([u[0], u[1]]) v = as_vector([v[0], v[1]]) elif shape == (3, 3): ew = MixedElement( [Vu.ufl_element(), Vu.ufl_element(), Vp.ufl_element()]) W = FunctionSpace(mesh, ew) u0, u1, p = TrialFunctions(W) v0, v1, q = TestFunctions(W) u = as_vector([u0, u1]) v = as_vector([v0, v1]) eq = define_eq(u, v, p, q) # Define coupled problem eq = define_eq(u, v, p, q) # Split the weak form into a saddle point block matrix system mat, vec = split_form_into_matrix(eq, W, W) # Check shape and that appropriate elements are none assert mat.shape == shape assert mat[-1, -1] is None for i in range(shape[0] - 1): for j in range(shape[1] - 1): if i != j: assert mat[i, j] is None # Check that the split matrices are identical with the # coupled matrix when reassembled into a single matrix compare_split_matrices(eq, mat, vec, W, W)
def _test_linear_solver_sparse(callback_type): # Create mesh and define function space mesh = IntervalMesh(132, 0, 2 * pi) V = FunctionSpace(mesh, "Lagrange", 1) # Define Dirichlet boundary (x = 0 or x = 2*pi) def boundary(x): return x[0] < 0 + DOLFIN_EPS or x[0] > 2 * pi - 10 * DOLFIN_EPS # Define exact solution exact_solution_expression = Expression("x[0] + sin(2*x[0])", element=V.ufl_element()) exact_solution = project(exact_solution_expression, V) # Define variational problem u = TrialFunction(V) v = TestFunction(V) g = Expression("4*sin(2*x[0])", element=V.ufl_element()) a = inner(grad(u), grad(v)) * dx f = g * v * dx x = inner(u, v) * dx # Assemble inner product matrix X = assemble(x) # Define boundary condition bc = [DirichletBC(V, exact_solution_expression, boundary)] # Defin solution sparse_solution = Function(V) # Define linear solver depending on callback type, and solve the problem assert callback_type in ("form callbacks", "tensor callbacks") if callback_type == "form callbacks": A = None F = None sparse_solver = SparseLinearSolver(a, sparse_solution, f, bc) elif callback_type == "tensor callbacks": A = assemble(a) F = assemble(f) sparse_solver = SparseLinearSolver(A, sparse_solution, F, bc) sparse_solver.solve() # Compute the error sparse_error = Function(V) sparse_error.vector().add_local(+sparse_solution.vector().get_local()) sparse_error.vector().add_local(-exact_solution.vector().get_local()) sparse_error.vector().apply("") sparse_error_norm = sparse_error.vector().inner(X * sparse_error.vector()) print("SparseLinearSolver error (" + callback_type + "):", sparse_error_norm) assert isclose(sparse_error_norm, 0., atol=1.e-5) return (sparse_error_norm, V, A, F, X, exact_solution)
def mk_scheme(N, Vname, Vorder, cpp_expr, expr_args, convection_inp, dim=2, comm=None): if comm is None: comm = MPI.comm_world parameters['ghost_mode'] = 'shared_vertex' if dim == 2: mesh = UnitSquareMesh(comm, N, N) else: mesh = UnitCubeMesh(comm, N, N, N) V = FunctionSpace(mesh, Vname, Vorder) C = Function(V) e = Expression(cpp_expr, element=V.ufl_element(), **expr_args) C.interpolate(e) D = Function(V) D.assign(C) sim = Simulation() sim.set_mesh(mesh) sim.data['constrained_domain'] = None sim.data['C'] = C for key, value in convection_inp.items(): sim.input.set_value('convection/C/%s' % key, value) scheme_name = convection_inp['convection_scheme'] return get_convection_scheme(scheme_name)(sim, 'C')
def mk_vel(sim, Vname, Vorder, cpp_exprs): mesh = sim.data['mesh'] V = FunctionSpace(mesh, Vname, Vorder) vel = [] for cpp in cpp_exprs: u = Function(V) u.interpolate(Expression(cpp, element=V.ufl_element())) vel.append(u) return as_vector(vel)
def test_estimator(): # setup solution multi vector mis = [Multiindex([0]), Multiindex([1]), Multiindex([0, 1]), Multiindex([0, 2])] mesh = UnitSquare(4, 4) fs = FunctionSpace(mesh, "CG", 1) F = [interpolate(Expression("*".join(["x[0]"] * i)), fs) for i in range(1, 5)] vecs = [FEniCSVector(f) for f in F] w = MultiVectorWithProjection() for mi, vec in zip(mis, vecs): w[mi] = vec # v = A * w # define coefficient field aN = 4 a = [Expression('2.+sin(20.*pi*I*x[0]*x[1])', I=i, degree=3, element=fs.ufl_element()) for i in range(1, aN)] rvs = [UniformRV(), NormalRV(mu=0.5)] coeff_field = ListCoefficientField(a[0], a[1:], rvs) # define source term f = Constant("1.0") # evaluate residual and projection error estimators resind, reserr = ResidualEstimator.evaluateResidualEstimator(w, coeff_field, f) projind, projerr = ResidualEstimator.evaluateProjectionError(w, coeff_field) print resind[mis[0]].as_array().shape, projind[mis[0]].as_array().shape print "RESIDUAL:", resind[mis[0]].as_array() print "PROJECTION:", projind[mis[0]].as_array() print "residual error estimate for mu" for mu in reserr: print "\t eta", mu, " is ", reserr[mu] print "\t delta", mu, " is ", projerr[mu] assert_equal(w.active_indices(), resind.active_indices()) print "active indices are ", resind.active_indices()
def test_local_assembler_on_facet_integrals(): mesh = UnitSquareMesh(MPI.comm_world, 4, 4, 'right') Vdg = FunctionSpace(mesh, 'DG', 0) Vdgt = FunctionSpace(mesh, 'DGT', 1) v = TestFunction(Vdgt) # Initialize DG function "w" in discontinuous pattern w = Expression('(1.0 + pow(x[0], 2.2) + 1/(0.1 + pow(x[1], 3)))*300.0', element=Vdg.ufl_element()) # Define form that tests that the correct + and - values are used L = w('-') * v('+') * dS # Compile form. This is collective L = Form(L) # Get global cell 10. This will return a cell only on one of the # processes c = get_cell_at(mesh, 5 / 12, 1 / 3, 0) if c: # Assemble locally on the selected cell b_e = assemble_local(L, c) # Compare to values from phonyx (fully independent # implementation) b_phonyx = numpy.array( [266.55210302, 266.55210302, 365.49000122, 365.49000122, 0.0, 0.0]) error = sum((b_e - b_phonyx)**2)**0.5 error = float(error) # MPI.max does strange things to numpy.float64 else: error = 0.0 error = MPI.max(MPI.comm_world, float(error)) assert error < 1e-8
def test_funvec2img_pb(self): m, n = 10, 20 # Define mesh. mesh = UnitSquareMesh(m - 1, n - 1) # Define function spaces V = FunctionSpace(mesh, 'CG', 1, constrained_domain=dh.PeriodicBoundary()) class MyUserExpression(UserExpression): def eval(self, value, x): value[0] = x[0] def value_shape(self): return (1, ) f = interpolate(MyUserExpression(element=V.ufl_element()), V) v = dh.funvec2img_pb(f.vector().get_local(), m, n) np.testing.assert_allclose(v.shape, (m, n)) np.testing.assert_allclose(v[:, 0], v[:, -1])
class State(object): def __init__(self, mesh, celldomains = {'magnetic': 1, 'conducting': 1}, facetdomains = {'outermagnet': 1}, material = None, scale = 1.0, t = 0.0, **kwargs): """ This class holds the complete state of the simulation and provides some convenience wrappers for the handling of multiple domains. Furthermore it provides an interface for attribute handling and caching. *Domain/Material Examples* .. code-block:: python state = State(mesh, celldomains = {'magnetic': (1,3), 'conducting': 2, 'iron': 1, 'cobalt': 3} m = Expression(...) ) # Set materials for different regions state.material['iron'] = Material(...) state.material['cobalt'] = Material(...) state.material['conducting'] = Material(...) # Use integration measures with named domains assemble(Constant(1.0) * state.dx('all')) # All named domains assemble(Constant(1.0) * state.dx('magnetic')) # Magnetic region assemble(Constant(1.0) * state.dx('!magnetic')) # Nonmagnetic region assemble(Constant(1.0) * state.dx(1)) # Region by ID # Compute average of magnetization m state.m.average() # Over whole space state.m.average('iron') # Over iron region # Crop magnetization to subdomain and save as PVD f = File("m_iron.pvd") f << state.m.crop('iron') # Normalize the magnetization state.m.normalize() *Attribute Examples* .. code-block:: python # initialize state with constant magnetization. m is automatically # interpolated on a suitable discrete function space. state = State(mesh, m = Constant((1.0, 0.0, 0.0)), ) # define a current as function of the time state.j = lambda state: Constant((state.t * 1e12, 0.0, 0.0)) # define some functional depending on m state.E = lambda state: (assemble(inner(state.m, state.m)*state.dx()), "m") # dependencies can be arbirtrarily nested state.E_times_2 = lambda state: (2*state.E, "E") # all values are cached according to their dependencies state.E_times_2 # triggers computation of E_times_2 state.E_times_2 # taken from cache, no computation state.m = Constant((0.0, 1.0, 0.0)) state.E_times_2 # triggers computation of E_times_2 *Arguments* mesh (:class:`dolfin.Mesh`) The mesh including all subdomains as :class:`dolfin.MeshDomains` celldomains (:class:`dict`) naming of the cell subdomains, at least the subdomains 'magnetic' and 'conducting' should be defined. facetdomains (:class:`dict`) naming of the facet subdomains material (:class:`Material`) the material of the sample. If material differs from subdomain to subdomain, use material setters instead. scale (:class:`float`) the spatial scaling of the mesh. Use 1e-9 if you use nanometers as length measure. t (:class:`float`) the time **kwargs (:class:`dict`) add any state variables like magnetization (m) or spin diffusion (s). Expressions are automatically interpolated on the corresponding discrete spaces. """ self._uuid = uuid.uuid4() self.mesh = mesh self.scale = scale self.t = t self.set_celldomains(celldomains) self.set_facetdomains(facetdomains) # TODO add args to set domains via mesh functions directly self.cell_domains = MeshFunction('size_t', mesh, 3, mesh.domains()) self.facet_domains = MeshFunction('size_t', mesh, 2, mesh.domains()) self._dx = Measure('dx', mesh)[self.cell_domains] self._ds = Measure('ds', mesh)[self.facet_domains] self._dS = Measure('dS', mesh)[self.facet_domains] self._dP = {} self._VS = FunctionSpace(self.mesh, 'CG', 1) # TODO lazy initialize self._VV = VectorFunctionSpace(self.mesh, 'CG', 1, 3) # TODO lazy initialize self.material = material self._wrapped_meshes = {} self._volumes = {} self._func_attributes = {} self._M_inv_diag = {} for key, value in kwargs.iteritems(): setattr(self, key, value) def _decorate_attr(self, name, value): if isinstance(value, GenericFunction): value._state = self value.update_uuid = types.MethodType(_update_uuid, value) value.crop = types.MethodType(_crop, value) value.average = types.MethodType(_average, value) value.normalize = types.MethodType(_normalize, value) value.rename(name, value.label()) value.update_uuid() return value def __setattr__(self, name, value): # handle lambda attributes if isfunction(value): self._func_attributes[name] = Cache( func = value, uuid = None, value = None, keys = None, in_process = False ) return if isinstance(value, GenericFunction): # always interpolate value = self.interpolate(value) # use assign if object exists if hasattr(self, name): attr = getattr(self, name) attr.assign(value) attr.update_uuid() return # decorate if object does not exist else: self._decorate_attr(name, value) super(State, self).__setattr__(name, value) def __getattr__(self, name): # handle lambda attributes if self._func_attributes.has_key(name): attr = self._func_attributes[name] # return uncached attribute if attr.keys == (): return attr.func(self) # check cycle dependency if attr.in_process: raise AttributeCycleDependency("Attribute dependency cycle detected.") # check cache hit if attr.keys is not None: uuid = self.uuid(*attr.keys) if uuid == attr.uuid: return attr.value # update cache attr.in_process = True value = attr.func(self) attr.in_process = False # set default hash keys if not isinstance(value, tuple): # primitives default to their value if isinstance(value, (int, float, str)): value = (value,) # others default to the time else: value = (value, "t") # XXX this seems to create a memory leak if isinstance(attr.value, Function): attr.value.assign(value[0]) attr.value.update_uuid() else: attr.value = self._decorate_attr(name, value[0]) attr.keys = value[1:] if attr.keys != (): attr.uuid = self.uuid(*attr.keys) return attr.value raise AttributeError("'State' object has no attribute '%s'" % name) def uuid(self, *names): """ Returns a uuid for a given list of attributes. The uuid changes if the attribute if reset. *Arguments* names ([:class:`string`]) list of attribute names *Returns* the uuid """ if len(names) == 0: return self._uuid elif len(names) == 1: name = names[0] # lambda attribute if self._func_attributes.has_key(name): attr = self._func_attributes[name] if attr.keys == None: getattr(self, name) if attr.keys == (): return getattr(self, name) else: return self.uuid(*attr.keys) # no lambda attribute else: attr = getattr(self, name) # object attribute (e.g. Function) if hasattr(attr, "uuid"): return attr.uuid # primitive attribute else: return attr else: return map(lambda x: self.uuid(x), names) def wrapped_mesh_for(self, domain): """ Creates a :class:`WrappedMesh` object for a certain domain. This can be used to crop certain state values like the magnetization to a region of interest. *Arguments* domain (:class:`string` / :class:`int`) domain identifier (either string or id) *Returns* :class:`WrappedMesh` the wrapped mesh """ if not self._wrapped_meshes.has_key(domain): self._wrapped_meshes[domain] = WrappedMesh.create(self.mesh, self.domain_ids(domain)) return self._wrapped_meshes[domain] def volume(self, domain = 'all'): """ Computes the volume of a certain domain. *Arguments* domain (:class:`string` / :class:`int`) domain identifier (either string or id) *Returns* :class:`float` the volume of the region """ if not self._volumes.has_key(domain): self._volumes[domain] = assemble(Constant(1.0)*self.dx(domain)) return self._volumes[domain] @property def material(self): return self._material @material.setter def material(self, value): if value is None: self._material = CompositeMaterial(self, Material()) elif isinstance(value, Material): self._material = CompositeMaterial(self, value) else: raise AttributeError("Type not supported.") def set_celldomains(self, celldomains): self.celldomains = {} for material in celldomains: ids = celldomains[material] if isinstance(ids, int): ids = (ids,) if isinstance(ids, list): ids = tuple(ids) self.celldomains[material] = ids def set_facetdomains(self, facetdomains): self.facetdomains = {} for material in facetdomains: ids = facetdomains[material] if isinstance(ids, int): ids = (ids,) if isinstance(ids, list): ids = tuple(ids) self.facetdomains[material] = ids def domain_ids(self, domain, domaintype = 'cell'): """ Get the domain IDs for a named domain. *Arguments* domain (:class:`string`/ :class:`int`) name or ID of domain domaintype (:class:`string`) domain type, (cell, facet) *Returns* :class:`[int]` List of domain IDs """ if isinstance(domain, int): return [domain] domains = { 'cell': self.celldomains, 'facet': self.facetdomains }[domaintype] if domain == 'all': ids = tuple() for material in domains: ids = ids + domains[material] return tuple(set(ids)) elif domain[0] == '!': ids = set(self.domain_ids('all', domaintype)) for id in self.domain_ids(domain[1:], domaintype): ids.remove(id) return tuple(ids) else: try: return domains[domain] except KeyError: raise KeyError("Unknown domain '%s'." % domain) def dx(self, domain = "all", dx = None): """ Convenience wrapper for integral-cell measure. If the mesh does not contain any cell domains, the measure for the whole mesh is returned. *Arguments* domain (:class:`string` / :class:`int`) name or ID of domain dx (:class:`dolfin:Measure`) alternative measure *Returns* :class:`dolfin:Measure` the measure # Compute volume of magnetic domain assemble(Constant(1.0) * state.dx('magnetic')) # Compute volume of domain with ID 3 assemble(Constant(1.0) * state.dx(3)) """ # return measure for whole mesh if no domains are defined if not self.mesh_has_domains(): return self._dx domain_ids = self.domain_ids(domain) # XXX fall back to zero measure if len(domain_ids) == 0: domain_ids = (999999,) if dx == None: dx = self._dx return reduce(lambda x,y: x+y, map(lambda i: dx(i), domain_ids)) def dP(self, domain = "all"): """ Convenience wrapper for integral-point measure. If the mesh does not contain any cell domains, the measure for the whole mesh is returned. *Arguments* domain (:class:`string` / :class:`int`) name or ID of domain *Returns* :class:`dolfin:Measure` the measure """ if not self.mesh_has_domains(): return Measure('dP', self.mesh) if not self._dP.has_key(domain): v = TestFunction(self.FunctionSpace()) values = assemble(v*self.dx(domain)).array() # TODO improve this? markers = ((np.sign(np.ceil(values) - 0.5) + 1.0) / 2.0).astype(np.uint64) vertex_domains = MeshFunction('size_t', self.mesh, 0) vertex_domains.array()[:] = markers self._dP[domain] = Measure('dP', self.mesh)[vertex_domains](1) return self._dP[domain] def ds(self, domain = "all", ds = None, intersect = None): """ Convenience wrapper for integral-facet measure. If the mesh does not contain any cell domains, the measure for the whole mesh is returned. *Arguments* domain (:class:`string` / :class:`int`) name or ID of domain ds (:class:`dolfin:Measure`) alternative measure intersect (:class:`string` / :class:`int`) name or ID of anothe domain to intersect with *Returns* :class:`dolfin:Measure` the measure """ # return measure for whole mesh if no domains are defined if not self.mesh_has_domains(): return self._ds domain_ids = self.domain_ids(domain, 'facet') if intersect is not None: domain_ids = list(set(self.domain_ids(intersect, 'facet')).intersection(domain_ids)) # XXX fall back to zero measure if len(domain_ids) == 0: domain_ids = (999999,) if ds == None: ds = self._ds measure = ds(domain_ids[0]) for domain_id in domain_ids[1:]: measure += ds(domain_id) return reduce(lambda x,y: x+y, map(lambda i: ds(i), domain_ids)) def dS(self, domain = "all", dS = None): """ Convenience wrapper for integral-interior-facet measure. If the mesh does not contain any cell domains, the measure for the whole mesh is returned. *Arguments* domain (:class:`string` / :class:`int`) name or ID of domain dS (:class:`dolfin:Measure`) alternative measure *Returns* :class:`dolfin:Measure` the measure """ if dS == None: return self.ds(domain, self._dS) else: return self.ds(domain, dS) def mesh_has_domains(self): """ Returns True if mesh contains cell-domain data. *Returns* :class:`bool` True if mesh contains cell-domain data """ return self.mesh.domains().num_marked(3) != 0 def interpolate(self, expressions): """ Interpolates a collection of expressions, each defined for a specific domain, on a suitable function space. Also works for a single expression. *Arguments* expressions (:class:`dict`) Expressions to be interpolated. *Returns* :class:`dolfin.Function` Interpolated function *Example* .. code-block:: python f1 = state.interpolate({ 'magnetic': Constant((1.0, 0.0, 0.0)), '!magnetic': Constant((0.0, 1.0, 0.0)) }) f2 = state.interpolate(Constant(1.0)) """ # single expression if isinstance(expressions, GenericFunction): if isinstance(expressions, Function): # TODO check if function spaces match return expressions else: return interpolate( expressions, self.FunctionSpace(rank = expressions.rank()) ) # multiple expressions (dict) else: result = Function(self.FunctionSpace(rank = expressions.itervalues().next().rank())) result.vector().zero() for domain in expressions: wmesh = self.wrapped_mesh_for(domain) expression = expressions[domain] f = interpolate(expression, self.FunctionSpace(wmesh, expression.rank())) wmesh.expand(f, result) return result def step(self, integrators, dt): """ Calls the step method on every integrator and increases :code:`t`. *Arguments* integrators (:class:`list` or :class:`Integrator`) Integrator on which the step method is called. Can be either a list of integrators or a single integrator. """ if not isinstance(integrators, collections.Iterable): integrators = [integrators] for integrator in integrators: integrator.step(self, dt) self.t += dt def VectorFunctionSpace(self, mesh = None): """ Returns the vector-function space for a mesh. *Arguments* mesh (:class:`dolfin.Mesh`) the mesh, defaults to mesh of the state if set to :code:`None` *Returns* :class:`dolfin.FunctionSpace` the function space """ if mesh == None: return self._VV element = self._VS.ufl_element() return VectorFunctionSpace(mesh, element.family(), element.degree()) def FunctionSpace(self, mesh = None, rank = 0): """ Returns a function space for a mesh. *Arguments* mesh (:class:`dolfin.Mesh`) the mesh, defaults to mesh of the state if set to :code:`None` rank (:class:`int`) the rank of the function space (0 for scalar space, 1 for vector space) *Returns* :class:`dolfin.FunctionSpace` the function space """ if rank == 0: if mesh == None: return self._VS element = self._VS.ufl_element() return FunctionSpace(mesh, element.family(), element.degree()) elif rank == 1: return self.VectorFunctionSpace(mesh) else: raise Exception("Rank > 1 not supported.") def M_inv_diag(self, domain = "all"): """ Returns the inverse lumped mass matrix for the vector function space. The result ist cached. .. note:: This method requires the PETSc Backend to be enabled. *Returns* :class:`dolfin.Matrix` the matrix """ if not self._M_inv_diag.has_key(domain): v = TestFunction(self.VectorFunctionSpace()) u = TrialFunction(self.VectorFunctionSpace()) mass_form = inner(v, u) * self.dx(domain) mass_action_form = action(mass_form, Constant((1.0, 1.0, 1.0))) diag = assemble(mass_action_form) as_backend_type(diag).vec().reciprocal() result = assemble(inner(v, u) * dP) result.zero() result.set_diagonal(diag) self._M_inv_diag[domain] = result return self._M_inv_diag[domain] @property def full(self): return self def expand(self, func): return func def cut(self, func): return func def mask(self, domain): # TODO also check if domain spans the whole mesh if self.mesh_has_domains(): return MaskedState(self, domain) else: return self
class Data(object): def __init__(self, Th, callback_type): # Create mesh and define function space mesh = UnitSquareMesh(Th, Th) self.V = FunctionSpace(mesh, "Lagrange", 1) # Define variational problem du = TrialFunction(self.V) v = TestFunction(self.V) self.u = Function(self.V) self.r = lambda u, g: inner(grad(u), grad(v)) * dx + inner( u + u**3, v) * dx - g * v * dx self.j = lambda u, r: derivative(r, u, du) # Define initial guess self.initial_guess_expression = Expression( "0.1 + 0.9*x[0]*x[1]", element=self.V.ufl_element()) # Define callback function depending on callback type assert callback_type in ("form callbacks", "tensor callbacks") if callback_type == "form callbacks": def callback(arg): return arg elif callback_type == "tensor callbacks": def callback(arg): return assemble(arg) self.callback_type = callback_type self.callback = callback def generate_random(self): # Generate random forcing g = RandomDolfinFunction(self.V) # Generate correspondingly residual and jacobian forms r = self.r(self.u, g) j = self.j(self.u, r) # Prepare problem wrapper class ProblemWrapper(NonlinearProblemWrapper): # Residual and jacobian functions def residual_eval(self_, solution): return self.callback(r) def jacobian_eval(self_, solution): return self.callback(j) # Define boundary condition def bc_eval(self_): return None # Empty solution monitor def monitor(self_, solution): pass problem_wrapper = ProblemWrapper() # Return return (r, j, problem_wrapper) def evaluate_builtin(self, r, j, problem_wrapper): project(self.initial_guess_expression, self.V, function=self.u) solve(r == 0, self.u, J=j, solver_parameters={ "nonlinear_solver": "snes", "snes_solver": { "linear_solver": "mumps", "maximum_iterations": 20, "relative_tolerance": 1e-9, "absolute_tolerance": 1e-9, "maximum_residual_evaluations": 10000, "report": True } }) return self.u.copy(deepcopy=True) def evaluate_backend(self, r, j, problem_wrapper): project(self.initial_guess_expression, self.V, function=self.u) solver = NonlinearSolver(problem_wrapper, self.u) solver.set_parameters({ "linear_solver": "mumps", "maximum_iterations": 20, "relative_tolerance": 1e-9, "absolute_tolerance": 1e-9, "report": True }) solver.solve() return self.u.copy(deepcopy=True) def assert_backend(self, r, j, problem_wrapper, result_backend): result_builtin = self.evaluate_builtin(r, j, problem_wrapper) error = Function(self.V) error.vector().add_local(+result_backend.vector().get_local()) error.vector().add_local(-result_builtin.vector().get_local()) error.vector().apply("add") relative_error = error.vector().norm( "l2") / result_builtin.vector().norm("l2") assert isclose(relative_error, 0., atol=1e-12)
mesh = UnitSquareMesh(200, 200) # mesh = create_dolfin_mesh(*meshzoo.triangle(1500, corners=[[0, 0], [1, 0], [0, 1]])) V = FunctionSpace(mesh, "CG", 1) u = TrialFunction(V) v = TestFunction(V) n = FacetNormal(mesh) # A = assemble(dot(grad(u), grad(v)) * dx - dot(n, grad(u)) * v * ds) A = assemble(dot(grad(u), grad(v)) * dx - dot(n, grad(u)) * v * ds) M = assemble(u * v * dx) f = Expression("sin(pi * x[0]) * sin(pi * x[1])", element=V.ufl_element()) x = project(f, V) Ax = A * x.vector() Minv_Ax = Function(V).vector() solve(M, Minv_Ax, Ax) val = Ax.inner(Minv_Ax) print(val) # Exact value x = sympy.Symbol("x") y = sympy.Symbol("y") f = sympy.sin(sympy.pi * x) * sympy.sin(sympy.pi * y) f2 = sympy.diff(f, x, x) + sympy.diff(f, y, y)
def _test_nonlinear_solver_sparse(callback_type): from dolfin import Function from rbnics.backends.dolfin import NonlinearSolver # Create mesh and define function space mesh = IntervalMesh(132, 0, 2 * pi) V = FunctionSpace(mesh, "Lagrange", 1) # Define Dirichlet boundary (x = 0 or x = 2 * pi) def boundary(x): return x[0] < 0 + DOLFIN_EPS or x[0] > 2 * pi - 10 * DOLFIN_EPS # Define exact solution exact_solution_expression = Expression("x[0] + sin(2*x[0])", element=V.ufl_element()) exact_solution = project(exact_solution_expression, V) # Define variational problem du = TrialFunction(V) v = TestFunction(V) u = Function(V) g = Expression( "4 * sin(2 * x[0]) * (pow(x[0] + sin(2 * x[0]), 2) + 1)" + " - 2 * (x[0] + sin(2 * x[0])) * pow(2 * cos(2 * x[0]) + 1," + " 2)", element=V.ufl_element()) r = inner((1 + u**2) * grad(u), grad(v)) * dx - g * v * dx j = derivative(r, u, du) x = inner(du, v) * dx # Assemble inner product matrix X = assemble(x) # Define initial guess def initial_guess(): initial_guess_expression = Expression("0.1 + 0.9 * x[0]", element=V.ufl_element()) return project(initial_guess_expression, V) # Define boundary condition bc = [DirichletBC(V, exact_solution_expression, boundary)] # Define callback function depending on callback type assert callback_type in ("form callbacks", "tensor callbacks") if callback_type == "form callbacks": def callback(arg): return arg elif callback_type == "tensor callbacks": def callback(arg): return assemble(arg) # Define problem wrapper class ProblemWrapper(NonlinearProblemWrapper): # Residual function def residual_eval(self, solution): return callback(r) # Jacobian function def jacobian_eval(self, solution): return callback(j) # Define boundary condition def bc_eval(self): return bc # Define custom monitor to plot the solution def monitor(self, solution): if matplotlib.get_backend() != "agg": plot(solution, title="u") plt.show(block=False) plt.pause(1) else: print("||u|| = " + str(solution.vector().norm("l2"))) # Solve the nonlinear problem problem_wrapper = ProblemWrapper() solution = u assign(solution, initial_guess()) solver = NonlinearSolver(problem_wrapper, solution) solver.set_parameters({ "linear_solver": "mumps", "maximum_iterations": 20, "report": True }) solver.solve() # Compute the error error = Function(V) error.vector().add_local(+solution.vector().get_local()) error.vector().add_local(-exact_solution.vector().get_local()) error.vector().apply("") error_norm = error.vector().inner(X * error.vector()) print("Sparse error (" + callback_type + "):", error_norm) assert isclose(error_norm, 0., atol=1.e-5) return (error_norm, V, u, r, j, X, initial_guess, exact_solution)
def RunJob(Tb, mu_value, path): runtimeInit = clock() tfile = File(path + '/t6t.pvd') mufile = File(path + "/mu.pvd") ufile = File(path + '/velocity.pvd') gradpfile = File(path + '/gradp.pvd') pfile = File(path + '/pstar.pvd') parameters = open(path + '/parameters', 'w', 0) vmeltfile = File(path + '/vmelt.pvd') rhofile = File(path + '/rhosolid.pvd') for name in dir(): ev = str(eval(name)) if name[0] != '_' and ev[0] != '<': parameters.write(name + ' = ' + ev + '\n') temp_values = [27. + 273, Tb + 273, 1300. + 273, 1305. + 273] dTemp = temp_values[3] - temp_values[0] temp_values = [x / dTemp for x in temp_values] # non dimensionalising temp mu_a = mu_value # this was taken from the blankenbach paper, can change.. Ep = b / dTemp mu_bot = exp(-Ep * (temp_values[3] * dTemp - 1573) + cc) * mu_a Ra = rho_0 * alpha * g * dTemp * h**3 / (kappa_0 * mu_a) w0 = rho_0 * alpha * g * dTemp * h**2 / mu_a tau = h / w0 p0 = mu_a * w0 / h print(mu_a, mu_bot, Ra, w0, p0) vslipx = 1.6e-09 / w0 vslip = Constant((vslipx, 0.0)) # nondimensional noslip = Constant((0.0, 0.0)) dt = 3.E11 / tau tEnd = 3.E13 / tau # non-dimensionalising times class PeriodicBoundary(SubDomain): def inside(self, x, on_boundary): return left(x, on_boundary) def map(self, x, y): y[0] = x[0] - MeshWidth y[1] = x[1] pbc = PeriodicBoundary() class TempExp(Expression): def eval(self, value, x): if x[1] >= LAB(x): value[0] = temp_values[0] + (temp_values[1] - temp_values[0] ) * (MeshHeight - x[1]) / (MeshHeight - LAB(x)) else: value[0] = temp_values[3] - ( temp_values[3] - temp_values[2]) * (x[1]) / (LAB(x)) class FluidTemp(Expression): def eval(self, value, x): if value[0] < 1295: value[0] = 1295 mesh = RectangleMesh(Point(0.0, 0.0), Point(MeshWidth, MeshHeight), nx, ny) Svel = VectorFunctionSpace(mesh, 'CG', 2, constrained_domain=pbc) Spre = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Stemp = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Smu = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Sgradp = VectorFunctionSpace(mesh, 'CG', 2, constrained_domain=pbc) Srho = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) S0 = MixedFunctionSpace([Svel, Spre, Stemp]) u = Function(S0) v, p, T = split(u) v_t, p_t, T_t = TestFunctions(S0) T0 = interpolate(TempExp(), Stemp) muExp = Expression( 'exp(-Ep * (T_val * dTemp - 1573) + cc * x[2] / meshHeight)', Smu.ufl_element(), Ep=Ep, dTemp=dTemp, cc=cc, meshHeight=MeshHeight, T_val=T0) mu = interpolate(muExp, Smu) rhosolid = Function(Srho) deltarho = Function(Srho) v0 = Function(Svel) vmelt = Function(Svel) v_theta = (1. - theta) * v0 + theta * v T_theta = (1. - theta) * T + theta * T0 r_v = (inner(sym(grad(v_t)), 2.*mu*sym(grad(v))) \ - div(v_t)*p \ - T*v_t[1] )*dx r_p = p_t * div(v) * dx r_T = (T_t*((T - T0) \ + dt*inner(v_theta, grad(T_theta))) \ + (dt/Ra)*inner(grad(T_t), grad(T_theta)) )*dx # + k_s*(Tf-T_theta)*dt Tf = T0.interpolate(FluidTemp()) # Tf = T0.interpolate(Expression('value[0] >= 1295.0 ? value[0] : 1295.0')) # Tf.interpolate(Expression('value[0] >= 1295 ? value[0] : 1295')) # project(Expression('value[0] >= 1295 ? value[0] : 1295'), Tf) # Alex, a question for you: # can you see if there is a way to set Tf = T in regions where T >=1295 celsius # # 1295 celsius is my arbitrary choice for the LAB isotherm. In regions # where T < 1295 C, set Tf to be some constant for now, such as 1295 C. # Once we do this, then we can add in a term like that last line above where # it will only be non-zero when the solid temperature, T, is cooler than 1295 # can you do this? After this is done, we will then worry about a calculation # where we solve for Tf as a function of time in the regions cooler than 1295 C # Makes sense? If not, we can skype soon -- email me with questions # 3/19/16 r = r_v + r_p + r_T bcv0 = DirichletBC(S0.sub(0), noslip, top) bcv1 = DirichletBC(S0.sub(0), vslip, bottom) bcp0 = DirichletBC(S0.sub(1), Constant(0.0), bottom) bct0 = DirichletBC(S0.sub(2), Constant(temp_values[0]), top) bct1 = DirichletBC(S0.sub(2), Constant(temp_values[3]), bottom) bcs = [bcv0, bcv1, bcp0, bct0, bct1] t = 0 count = 0 while (t < tEnd): solve(r == 0, u, bcs) t += dt nV, nP, nT = u.split() gp = grad(nP) rhosolid = rho_0 * (1 - alpha * (nT * dTemp - 1573)) deltarho = rhosolid - rhomelt yvec = Constant((0.0, 1.0)) vmelt = nV * w0 - darcy * (gp * p0 / h - deltarho * yvec * g) if (count % 100 == 0): pfile << nP ufile << nV tfile << nT mufile << mu gradpfile << project(grad(nP), Sgradp) mufile << project(mu * mu_a, Smu) rhofile << project(rhosolid, Srho) vmeltfile << project(vmelt, Svel) count += 1 assign(T0, nT) assign(v0, nV) mu.interpolate(muExp) print('Case mu=%g, Tb=%g complete.' % (mu_a, Tb), ' Run time =', clock() - runtimeInit, 's')
if edgefields: fh.write("CELL_DATA {0}\n".format(elems.shape[0])) for n,f in edgefields: PUTFIELD(n,f) fh.close() if __name__=="__main__": from dolfin import UnitSquareMesh, Function, FunctionSpace, VectorFunctionSpace, TensorFunctionSpace, Expression mesh = UnitSquareMesh(10,10) S=FunctionSpace(mesh,"DG",0) V=VectorFunctionSpace(mesh,"DG",0) T=TensorFunctionSpace(mesh,"DG",0) Tsym = TensorFunctionSpace(mesh,"DG",0,symmetry=True) s = Function(S) s.interpolate(Expression('x[0]',element=S.ufl_element())) v = Function(V) v.interpolate(Expression(('x[0]','x[1]'),element=V.ufl_element())) t = Function(T) t.interpolate(Expression(( ('x[0]','1.0'),('2.0','x[1]')),element=T.ufl_element())) ts = Function(Tsym) ts.interpolate(Expression(( ('x[0]','1.0'),('x[1]',)),element=Tsym.ufl_element())) write_vtk_f("test.vtk",cellfunctions={'s':s,'v':v,'t':t,'tsym':ts})
def _test_time_stepping_1_sparse(callback_type, integrator_type): # Create mesh and define function space mesh = IntervalMesh(132, 0, 2*pi) V = FunctionSpace(mesh, "Lagrange", 1) # Define Dirichlet boundary (x = 0 or x = 2*pi) def boundary(x): return x[0] < 0 + DOLFIN_EPS or x[0] > 2*pi - 10*DOLFIN_EPS # Define time step dt = 0.01 T = 1. # Define exact solution exact_solution_expression = Expression("sin(x[0]+t)", t=0, element=V.ufl_element()) # ... and interpolate it at the final time exact_solution_expression.t = T exact_solution = project(exact_solution_expression, V) # Define exact solution dot exact_solution_dot_expression = Expression("cos(x[0]+t)", t=0, element=V.ufl_element()) # ... and interpolate it at the final time exact_solution_dot_expression.t = T exact_solution_dot = project(exact_solution_dot_expression, V) # Define variational problem du = TrialFunction(V) du_dot = TrialFunction(V) v = TestFunction(V) u = Function(V) u_dot = Function(V) g = Expression("sin(x[0]+t) + cos(x[0]+t)", t=0., element=V.ufl_element()) r_u = inner(grad(u), grad(v))*dx j_u = derivative(r_u, u, du) r_u_dot = inner(u_dot, v)*dx j_u_dot = derivative(r_u_dot, u_dot, du_dot) r = r_u_dot + r_u - g*v*dx x = inner(du, v)*dx def bc(t): exact_solution_expression.t = t return [DirichletBC(V, exact_solution_expression, boundary)] # Assemble inner product matrix X = assemble(x) # Define callback function depending on callback type assert callback_type in ("form callbacks", "tensor callbacks") if callback_type == "form callbacks": def callback(arg): return arg elif callback_type == "tensor callbacks": def callback(arg): return assemble(arg) # Define problem wrapper class SparseProblemWrapper(TimeDependentProblem1Wrapper): # Residual and jacobian functions def residual_eval(self, t, solution, solution_dot): g.t = t return callback(r) def jacobian_eval(self, t, solution, solution_dot, solution_dot_coefficient): return callback(Constant(solution_dot_coefficient)*j_u_dot + j_u) # Define boundary condition def bc_eval(self, t): return bc(t) # Define initial condition def ic_eval(self): exact_solution_expression.t = 0. return project(exact_solution_expression, V) # Define custom monitor to plot the solution def monitor(self, t, solution, solution_dot): if matplotlib.get_backend() != "agg": plt.subplot(1, 2, 1).clear() plot(solution, title="u at t = " + str(t)) plt.subplot(1, 2, 2).clear() plot(solution_dot, title="u_dot at t = " + str(t)) plt.show(block=False) plt.pause(DOLFIN_EPS) else: print("||u|| at t = " + str(t) + ": " + str(solution.vector().norm("l2"))) print("||u_dot|| at t = " + str(t) + ": " + str(solution_dot.vector().norm("l2"))) # Solve the time dependent problem sparse_problem_wrapper = SparseProblemWrapper() (sparse_solution, sparse_solution_dot) = (u, u_dot) sparse_solver = SparseTimeStepping(sparse_problem_wrapper, sparse_solution, sparse_solution_dot) sparse_solver.set_parameters({ "initial_time": 0.0, "time_step_size": dt, "final_time": T, "exact_final_time": "stepover", "integrator_type": integrator_type, "problem_type": "linear", "linear_solver": "mumps", "monitor": sparse_problem_wrapper.monitor, "report": True }) all_sparse_solutions_time, all_sparse_solutions, all_sparse_solutions_dot = sparse_solver.solve() assert len(all_sparse_solutions_time) == int(T/dt + 1) assert len(all_sparse_solutions) == int(T/dt + 1) assert len(all_sparse_solutions_dot) == int(T/dt + 1) # Compute the error sparse_error = Function(V) sparse_error.vector().add_local(+ sparse_solution.vector().get_local()) sparse_error.vector().add_local(- exact_solution.vector().get_local()) sparse_error.vector().apply("") sparse_error_norm = sparse_error.vector().inner(X*sparse_error.vector()) sparse_error_dot = Function(V) sparse_error_dot.vector().add_local(+ sparse_solution_dot.vector().get_local()) sparse_error_dot.vector().add_local(- exact_solution_dot.vector().get_local()) sparse_error_dot.vector().apply("") sparse_error_dot_norm = sparse_error_dot.vector().inner(X*sparse_error_dot.vector()) print("SparseTimeStepping error (" + callback_type + ", " + integrator_type + "):", sparse_error_norm, sparse_error_dot_norm) assert isclose(sparse_error_norm, 0., atol=1.e-4) assert isclose(sparse_error_dot_norm, 0., atol=1.e-4) return ((sparse_error_norm, sparse_error_dot_norm), V, dt, T, u, u_dot, g, r, j_u, j_u_dot, X, exact_solution_expression, exact_solution, exact_solution_dot)
def RunJob(Tb, mu_value, path): runtimeInit = clock() tfile = File(path + '/t6t.pvd') mufile = File(path + "/mu.pvd") ufile = File(path + '/velocity.pvd') gradpfile = File(path + '/gradp.pvd') pfile = File(path + '/pstar.pvd') parameters = open(path + '/parameters', 'w', 0) vmeltfile = File(path + '/vmelt.pvd') rhofile = File(path + '/rhosolid.pvd') for name in dir(): ev = str(eval(name)) if name[0] != '_' and ev[0] != '<': parameters.write(name + ' = ' + ev + '\n') temp_values = [27. + 273, Tb + 273, 1300. + 273, 1305. + 273] dTemp = temp_values[3] - temp_values[0] temp_values = [x / dTemp for x in temp_values] # non dimensionalising temp mu_a = mu_value # this was taken from the blankenbach paper, can change.. Ep = b / dTemp mu_bot = exp(-Ep * (temp_values[3] * dTemp - 1573) + cc) * mu_a Ra = rho_0 * alpha * g * dTemp * h**3 / (kappa_0 * mu_a) w0 = rho_0 * alpha * g * dTemp * h**2 / mu_a tau = h / w0 p0 = mu_a * w0 / h print(mu_a, mu_bot, Ra, w0, p0) vslipx = 1.6e-09 / w0 vslip = Constant((vslipx, 0.0)) # nondimensional noslip = Constant((0.0, 0.0)) dt = 3.E11 / tau tEnd = 3.E13 / tau # non-dimensionalising times class PeriodicBoundary(SubDomain): def inside(self, x, on_boundary): return left(x, on_boundary) def map(self, x, y): y[0] = x[0] - MeshWidth y[1] = x[1] pbc = PeriodicBoundary() class TempExp(Expression): def eval(self, value, x): if x[1] >= LAB(x): value[0] = temp_values[0] + (temp_values[1] - temp_values[0]) * (MeshHeight - x[1]) / (MeshHeight - LAB(x)) else: value[0] = temp_values[3] - (temp_values[3] - temp_values[2]) * (x[1]) / (LAB(x)) class FluidTemp(Expression): def eval(self, value, x): if value[0] < 1295: value[0] = 1295 mesh = RectangleMesh(Point(0.0, 0.0), Point(MeshWidth, MeshHeight), nx, ny) Svel = VectorFunctionSpace(mesh, 'CG', 2, constrained_domain=pbc) Spre = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Stemp = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Smu = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) Sgradp = VectorFunctionSpace(mesh, 'CG', 2, constrained_domain=pbc) Srho = FunctionSpace(mesh, 'CG', 1, constrained_domain=pbc) S0 = MixedFunctionSpace([Svel, Spre, Stemp]) u = Function(S0) v, p, T = split(u) v_t, p_t, T_t = TestFunctions(S0) T0 = interpolate(TempExp(), Stemp) muExp = Expression('exp(-Ep * (T_val * dTemp - 1573) + cc * x[2] / meshHeight)', Smu.ufl_element(), Ep=Ep, dTemp=dTemp, cc=cc, meshHeight=MeshHeight, T_val=T0) mu = interpolate(muExp, Smu) rhosolid = Function(Srho) deltarho = Function(Srho) v0 = Function(Svel) vmelt = Function(Svel) v_theta = (1. - theta)*v0 + theta*v T_theta = (1. - theta)*T + theta*T0 r_v = (inner(sym(grad(v_t)), 2.*mu*sym(grad(v))) \ - div(v_t)*p \ - T*v_t[1] )*dx r_p = p_t*div(v)*dx r_T = (T_t*((T - T0) \ + dt*inner(v_theta, grad(T_theta))) \ + (dt/Ra)*inner(grad(T_t), grad(T_theta)) )*dx # + k_s*(Tf-T_theta)*dt Tf = T0.interpolate(FluidTemp()) # Tf = T0.interpolate(Expression('value[0] >= 1295.0 ? value[0] : 1295.0')) # Tf.interpolate(Expression('value[0] >= 1295 ? value[0] : 1295')) # project(Expression('value[0] >= 1295 ? value[0] : 1295'), Tf) # Alex, a question for you: # can you see if there is a way to set Tf = T in regions where T >=1295 celsius # # 1295 celsius is my arbitrary choice for the LAB isotherm. In regions # where T < 1295 C, set Tf to be some constant for now, such as 1295 C. # Once we do this, then we can add in a term like that last line above where # it will only be non-zero when the solid temperature, T, is cooler than 1295 # can you do this? After this is done, we will then worry about a calculation # where we solve for Tf as a function of time in the regions cooler than 1295 C # Makes sense? If not, we can skype soon -- email me with questions # 3/19/16 r = r_v + r_p + r_T bcv0 = DirichletBC(S0.sub(0), noslip, top) bcv1 = DirichletBC(S0.sub(0), vslip, bottom) bcp0 = DirichletBC(S0.sub(1), Constant(0.0), bottom) bct0 = DirichletBC(S0.sub(2), Constant(temp_values[0]), top) bct1 = DirichletBC(S0.sub(2), Constant(temp_values[3]), bottom) bcs = [bcv0, bcv1, bcp0, bct0, bct1] t = 0 count = 0 while (t < tEnd): solve(r == 0, u, bcs) t += dt nV, nP, nT = u.split() gp = grad(nP) rhosolid = rho_0 * (1 - alpha * (nT * dTemp - 1573)) deltarho = rhosolid - rhomelt yvec = Constant((0.0, 1.0)) vmelt = nV * w0 - darcy * (gp * p0 / h - deltarho * yvec * g) if (count % 100 == 0): pfile << nP ufile << nV tfile << nT mufile << mu gradpfile << project(grad(nP), Sgradp) mufile << project(mu * mu_a, Smu) rhofile << project(rhosolid, Srho) vmeltfile << project(vmelt, Svel) count += 1 assign(T0, nT) assign(v0, nV) mu.interpolate(muExp) print('Case mu=%g, Tb=%g complete.' % (mu_a, Tb), ' Run time =', clock() - runtimeInit, 's')
def _test_linear_solver_sparse(callback_type): from dolfin import Function from rbnics.backends.dolfin import LinearSolver # Create mesh and define function space mesh = IntervalMesh(132, 0, 2 * pi) V = FunctionSpace(mesh, "Lagrange", 1) # Define Dirichlet boundary (x = 0 or x = 2 * pi) def boundary(x): return x[0] < 0 + DOLFIN_EPS or x[0] > 2 * pi - 10 * DOLFIN_EPS # Define exact solution exact_solution_expression = Expression("x[0] + sin(2 * x[0])", element=V.ufl_element()) exact_solution = project(exact_solution_expression, V) # Define variational problem u = TrialFunction(V) v = TestFunction(V) g = Expression("4 * sin(2 * x[0])", element=V.ufl_element()) a = inner(grad(u), grad(v)) * dx f = g * v * dx x = inner(u, v) * dx # Assemble inner product matrix X = assemble(x) # Define boundary condition bc = [DirichletBC(V, exact_solution_expression, boundary)] # Define callback function depending on callback type assert callback_type in ("form callbacks", "tensor callbacks") if callback_type == "form callbacks": def callback(arg): return arg elif callback_type == "tensor callbacks": def callback(arg): return assemble(arg) # Define problem wrapper class ProblemWrapper(LinearProblemWrapper): # Vector function def vector_eval(self): return callback(f) # Matrix function def matrix_eval(self): return callback(a) # Define boundary condition def bc_eval(self): return bc # Define custom monitor to plot the solution def monitor(self, solution): if matplotlib.get_backend() != "agg": plot(solution, title="u") plt.show(block=False) plt.pause(1) else: print("||u|| = " + str(solution.vector().norm("l2"))) # Solve the linear problem problem_wrapper = ProblemWrapper() solution = Function(V) solver = LinearSolver(problem_wrapper, solution) solver.solve() # Compute the error error = Function(V) error.vector().add_local(+ solution.vector().get_local()) error.vector().add_local(- exact_solution.vector().get_local()) error.vector().apply("") error_norm = error.vector().inner(X * error.vector()) print("Sparse error (" + callback_type + "):", error_norm) assert isclose(error_norm, 0., atol=1.e-5) return (error_norm, V, a, f, X, exact_solution)
def _test_nonlinear_solver_sparse(callback_type): # Create mesh and define function space mesh = IntervalMesh(132, 0, 2*pi) V = FunctionSpace(mesh, "Lagrange", 1) # Define Dirichlet boundary (x = 0 or x = 2*pi) def boundary(x): return x[0] < 0 + DOLFIN_EPS or x[0] > 2*pi - 10*DOLFIN_EPS # Define exact solution exact_solution_expression = Expression("x[0] + sin(2*x[0])", element=V.ufl_element()) exact_solution = project(exact_solution_expression, V) # Define variational problem du = TrialFunction(V) v = TestFunction(V) u = Function(V) g = Expression("4*sin(2*x[0])*(pow(x[0]+sin(2*x[0]), 2)+1)-2*(x[0]+sin(2*x[0]))*pow(2*cos(2*x[0])+1, 2)", element=V.ufl_element()) r = inner((1+u**2)*grad(u), grad(v))*dx - g*v*dx j = derivative(r, u, du) x = inner(du, v)*dx # Assemble inner product matrix X = assemble(x) # Define initial guess def sparse_initial_guess(): initial_guess_expression = Expression("0.1 + 0.9*x[0]", element=V.ufl_element()) return project(initial_guess_expression, V) # Define boundary condition bc = [DirichletBC(V, exact_solution_expression, boundary)] # Define callback function depending on callback type assert callback_type in ("form callbacks", "tensor callbacks") if callback_type == "form callbacks": def callback(arg): return arg elif callback_type == "tensor callbacks": def callback(arg): return assemble(arg) # Define problem wrapper class SparseFormProblemWrapper(NonlinearProblemWrapper): # Residual function def residual_eval(self, solution): return callback(r) # Jacobian function def jacobian_eval(self, solution): return callback(j) # Define boundary condition def bc_eval(self): return bc # Solve the nonlinear problem sparse_problem_wrapper = SparseFormProblemWrapper() sparse_solution = u assign(sparse_solution, sparse_initial_guess()) sparse_solver = SparseNonlinearSolver(sparse_problem_wrapper, sparse_solution) sparse_solver.set_parameters({ "linear_solver": "mumps", "maximum_iterations": 20, "report": True }) sparse_solver.solve() # Compute the error sparse_error = Function(V) sparse_error.vector().add_local(+ sparse_solution.vector().get_local()) sparse_error.vector().add_local(- exact_solution.vector().get_local()) sparse_error.vector().apply("") sparse_error_norm = sparse_error.vector().inner(X*sparse_error.vector()) print("SparseNonlinearSolver error (" + callback_type + "):", sparse_error_norm) assert isclose(sparse_error_norm, 0., atol=1.e-5) return (sparse_error_norm, V, u, r, j, X, sparse_initial_guess, exact_solution)