From f8df42b61905f4c9dc83be0b795e929b5b1d78f7 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Sat, 21 Dec 2024 11:07:32 +0000 Subject: [PATCH 01/11] Enable E501 except for burgers-hessian --- demos/burgers-goal_oriented.py | 22 +++++++------- demos/burgers_ee.py | 17 ++++++----- demos/burgers_oo.py | 3 +- demos/mantle_convection.py | 10 ++++--- demos/ode.py | 11 +++---- demos/point_discharge2d-goal_oriented.py | 13 ++++---- demos/point_discharge2d-hessian.py | 10 +++---- demos/point_discharge2d.py | 6 ++-- goalie/adjoint.py | 38 +++++++++++++----------- goalie/error_estimation.py | 13 ++++---- goalie/function_data.py | 7 +++-- goalie/go_mesh_seq.py | 8 ++--- goalie/mesh_seq.py | 16 +++++----- pyproject.toml | 5 +++- test/test_mesh_seq.py | 5 +++- test/test_options.py | 10 +++++-- 16 files changed, 111 insertions(+), 83 deletions(-) diff --git a/demos/burgers-goal_oriented.py b/demos/burgers-goal_oriented.py index f1b68669..00beafca 100644 --- a/demos/burgers-goal_oriented.py +++ b/demos/burgers-goal_oriented.py @@ -1,11 +1,12 @@ # Burgers equation with Goal-oriented mesh adaptation # =================================================== -# In the `Hessian-based adaptation <./burgers-hessian.py.html>`__, we applied a Hessian-based -# mesh adaptation to the time-dependent Burgers equation. Here, we alternatively consider -# a goal-oriented error estimation to drive the mesh adaptation. Again, we will -# choose to partition the problem over multiple subintervals and hence multiple meshes -# to adapt. We also chose to apply a QoI which integrates in time as well as space. +# In the `Hessian-based adaptation <./burgers-hessian.py.html>`__, we applied a +# Hessian-based mesh adaptation to the time-dependent Burgers equation. Here, we +# alternatively consider a goal-oriented error estimation to drive the mesh adaptation. +# Again, we will choose to partition the problem over multiple subintervals and hence +# multiple meshes to adapt. We also chose to apply a QoI which integrates in time as +# well as space. # # We copy over the setup as before. The only difference is that we import from # ``goalie_adjoint`` rather than ``goalie``. :: @@ -115,8 +116,8 @@ def time_integrated_qoi(t): # Compared to the previous `Hessian-based adaptation <./burgers-hessian.py.html>`__, # this adaptor depends on adjoint solution data as well as forward solution data. -# For simplicity, we begin by using :meth:`~.RiemannianMetric.compute_isotropic_metric()`. -# :: +# For simplicity, we begin by using +# :meth:`~.RiemannianMetric.compute_isotropic_metric()`. :: def adaptor(mesh_seq, solutions=None, indicators=None): @@ -257,7 +258,8 @@ def adaptor(mesh_seq, solutions=None, indicators=None): # (See documentation for details.) To use it, we just need to define # a different adaptor function. The same error indicator is used as # for the isotropic approach. Additionally, the Hessian of the forward -# solution is estimated as in the `Hessian-based adaptation <./burgers-hessian.py.html>`__ +# solution is estimated as in the +# `Hessian-based adaptation <./burgers-hessian.py.html>`__ # to give anisotropy to the metric. # # For this driver, normalisation is handled differently than for @@ -351,8 +353,8 @@ def adaptor(mesh_seq, solutions=None, indicators=None): fig.savefig(f"burgers-anisotropic_mesh{iteration + 1}.jpg") plt.close() - # Since we have multiple subintervals, we should check if the target complexity has been - # (approximately) reached on each subinterval + # Since we have multiple subintervals, we should check if the target complexity has + # been (approximately) reached on each subinterval continue_unconditionally = np.array(complexities) < 0.90 * target return continue_unconditionally diff --git a/demos/burgers_ee.py b/demos/burgers_ee.py index 3fe848a0..c578890c 100644 --- a/demos/burgers_ee.py +++ b/demos/burgers_ee.py @@ -122,14 +122,15 @@ def end_time_qoi(): qoi_type="end_time", ) -# Given the description of the PDE problem in the form of a :class:`GoalOrientedMeshSeq`, -# Goalie is able to extract all of the relevant information to automatically compute -# error estimators. During the computation, we solve the forward and adjoint equations -# over the mesh sequence, as before. In addition, we solve the adjoint problem again -# in an *enriched* finite element space. Currently, Goalie supports uniform refinement -# of the meshes (:math:`h`-refinement) or globally increasing the polynomial order -# (:math:`p`-refinement). Choosing one (or both) of these as the ``"enrichment_method"``, -# we are able to compute error indicator fields as follows. :: +# Given the description of the PDE problem in the form of a +# :class:`GoalOrientedMeshSeq`, Goalie is able to extract all of the relevant +# information to automatically compute error estimators. During the computation, we +# solve the forward and adjoint equations over the mesh sequence, as before. In +# addition, we solve the adjoint problem again in an *enriched* finite element space. +# Currently, Goalie supports uniform refinement of the meshes (:math:`h`-refinement) or +# globally increasing the polynomial order (:math:`p`-refinement). Choosing one (or +# both) of these as the ``"enrichment_method"``, we are able to compute error indicator +# fields as follows. :: solutions, indicators = mesh_seq.indicate_errors( enrichment_kwargs={"enrichment_method": "h"} diff --git a/demos/burgers_oo.py b/demos/burgers_oo.py index a38ec020..3d67cfe4 100644 --- a/demos/burgers_oo.py +++ b/demos/burgers_oo.py @@ -110,7 +110,8 @@ def time_integrated_qoi(t): enrichment_kwargs={"enrichment_method": "h"} ) -# Plotting this, we find that the results are consistent with those generated previously. :: +# Plotting this, we find that the results are consistent with those generated +# previously. :: fig, axes, tcs = plot_indicator_snapshots(indicators, time_partition, "u", levels=50) fig.savefig("burgers-oo_ee.jpg") diff --git a/demos/mantle_convection.py b/demos/mantle_convection.py index cae83661..e94e263b 100644 --- a/demos/mantle_convection.py +++ b/demos/mantle_convection.py @@ -5,8 +5,8 @@ # involve a time derivative. Those are clearly *time-dependent* equations. However, # time-dependent equations need not involve a time derivative. For example, they might # include fields that vary with time. Examples of where this might happen are -# in continuous pressure projection approaches, ice sheet and glaciological modelling, and -# mantle convection modelling. In this demo, we illustrate how Goalie can be used +# in continuous pressure projection approaches, ice sheet and glaciological modelling, +# and mantle convection modelling. In this demo, we illustrate how Goalie can be used # to solve such problems. # We consider the problem of a mantle convection in a 2D unit square domain. The @@ -19,8 +19,10 @@ # # .. math:: # \begin{align} -# \nabla \cdot \mu \left[\nabla \mathbf{u} + (\nabla \mathbf{u})^T \right] - \nabla p + \mathrm{Ra}\,T\,\mathbf{k} &= 0, \\ -# \frac{\partial T}{\partial t} \cdot \mathbf{u}\cdot\nabla T - \nabla \cdot (\kappa\nabla T) &= 0, +# \nabla \cdot \mu \left[\nabla \mathbf{u} + (\nabla \mathbf{u})^T \right] - +# \nabla p + \mathrm{Ra}\,T\,\mathbf{k} &= 0, \\ +# \frac{\partial T}{\partial t} \cdot \mathbf{u}\cdot\nabla T +# - \nabla \cdot (\kappa\nabla T) &= 0, # \end{align} # # where :math:`\mu`, :math:`\kappa`, and :math:`\mathrm{Ra}` are constant viscosity, diff --git a/demos/ode.py b/demos/ode.py index dd456808..04163495 100644 --- a/demos/ode.py +++ b/demos/ode.py @@ -154,10 +154,10 @@ def solver(index): solutions = point_seq.solve_forward()["u"]["forward"] -# Note that the solution trajectory does not include the initial value, so we prepend it. -# We also convert the solution :class:`~.Function`\s to :class:`~.float`\s, for plotting -# purposes. Whilst there is only one subinterval in this example, we show how to loop -# over subintervals, as this is instructive for the general case. :: +# Note that the solution trajectory does not include the initial value, so we prepend +# it. We also convert the solution :class:`~.Function`\s to :class:`~.float`\s, for +# plotting purposes. Whilst there is only one subinterval in this example, we show how +# to loop over subintervals, as this is instructive for the general case. :: forward_euler_trajectory = [1] forward_euler_trajectory += [ @@ -257,7 +257,8 @@ def solver(index): # .. math:: # \frac{u_{i+1} - u_i}{\Delta t} = (\theta u_{i+1} + (1-\theta) u_i), # -# where :math:`\theta\in(0,1)`. The standard choice is to take :math:`\theta=\frac12`. :: +# where :math:`\theta\in(0,1)`. The standard choice is to take :math:`\theta=\frac12`. +# :: def get_solver_crank_nicolson(point_seq): diff --git a/demos/point_discharge2d-goal_oriented.py b/demos/point_discharge2d-goal_oriented.py index 92cf5c41..db22f79c 100644 --- a/demos/point_discharge2d-goal_oriented.py +++ b/demos/point_discharge2d-goal_oriented.py @@ -242,11 +242,11 @@ def adaptor(mesh_seq, solutions, indicators): # # Looking at the final adapted mesh, we can make a few observations. Firstly, the mesh # elements are indeed isotropic. Secondly, there is clearly increased resolution -# surrounding the point source, as well as the "receiver region" which the QoI integrates -# over. There is also a band of increased resolution between these two regions. Finally, -# the mesh has low resolution downstream of the receiver region. This is to be expected -# because we have an advection-dominated problem, so the QoI value is independent of the -# dynamics there. +# surrounding the point source, as well as the "receiver region" which the QoI +# integrates over. There is also a band of increased resolution between these two +# regions. Finally, the mesh has low resolution downstream of the receiver region. This +# is to be expected because we have an advection-dominated problem, so the QoI value is +# independent of the dynamics there. # # Goalie also provides drivers for *anisotropic* goal-oriented mesh adaptation. Here, # we consider the ``anisotropic_dwr_metric`` driver. (See documentation for details.) To @@ -372,4 +372,5 @@ def adaptor(mesh_seq, solutions, indicators): # In the `next demo <./burgers-hessian.py.html>`__, we consider mesh adaptation in the # time-dependent case. # -# This demo can also be accessed as a `Python script `__. +# This demo can also be accessed as a +# `Python script `__. diff --git a/demos/point_discharge2d-hessian.py b/demos/point_discharge2d-hessian.py index 980ef420..19928b75 100644 --- a/demos/point_discharge2d-hessian.py +++ b/demos/point_discharge2d-hessian.py @@ -10,7 +10,7 @@ # before progressing with this demo. # # In addition to importing from Firedrake and Goalie, we also import the mesh -# adaptation functionality from Firedrake, which can be found in ``firedrake.meshadapt``. +# adaptation functionality from Animate, which can be found in ``animate.adapt``. # :: from animate.adapt import adapt @@ -20,8 +20,8 @@ from goalie import * # We again consider the "point discharge with diffusion" test case from the -# `previous demo <./point_discharge2d.py.html>`__, approximating the tracer concentration -# :math:`c` in :math:`\mathbb P1` space. :: +# `previous demo <./point_discharge2d.py.html>`__, approximating the tracer +# concentration :math:`c` in :math:`\mathbb P1` space. :: field_names = ["c"] @@ -187,8 +187,8 @@ def adaptor(mesh_seq, solutions): ) solutions = mesh_seq.fixed_point_iteration(adaptor, parameters=params) -# Mesh adaptation often gives slightly different results on difference machines. However, -# the output should look something like +# Mesh adaptation often gives slightly different results on difference machines. +# However, the output should look something like # # .. code-block:: console # diff --git a/demos/point_discharge2d.py b/demos/point_discharge2d.py index 5db8e0d7..db9fcea7 100644 --- a/demos/point_discharge2d.py +++ b/demos/point_discharge2d.py @@ -15,7 +15,8 @@ # \left\{\begin{array}{rl} # \mathbf u\cdot\nabla c - \nabla\cdot(D\nabla c) = S & \text{in}\:\Omega\\ # c=0 & \text{on}\:\partial\Omega_{\mathrm{inflow}}\\ -# \nabla c\cdot\widehat{\mathbf n}=0 & \text{on}\:\partial\Omega\backslash\partial\Omega_{\mathrm{inflow}} +# \nabla c\cdot\widehat{\mathbf n}=0 & +# \text{on}\:\partial\Omega\backslash\partial\Omega_{\mathrm{inflow}} # \end{array}\right., # # for a tracer concentration :math:`c`, with fluid velocity @@ -73,7 +74,8 @@ def source(mesh): # With these ingredients, we can now define the :meth:`get_solver` method. Don't forget # to apply the corresponding `ad_block_tag` to the solve call. Additionally, we must # communicate the defined variational form to ``mesh_seq`` using the -# :meth:`mesh_seq.read_form()` method for Goalie to utilise it during error indication. :: +# :meth:`mesh_seq.read_form()` method for Goalie to utilise it during error indication. +# :: def get_solver(mesh_seq): diff --git a/goalie/adjoint.py b/goalie/adjoint.py index 95e8a4a7..4e7a28ea 100644 --- a/goalie/adjoint.py +++ b/goalie/adjoint.py @@ -103,7 +103,8 @@ def __init__(self, time_partition, initial_meshes, **kwargs): ) elif self.qoi_type != "steady" and self.steady: raise ValueError( - f"Time partition is steady but the QoI type is set to '{self.qoi_type}'." + "Time partition is steady but the QoI type is set to" + " '{self.qoi_type}'." ) self._controls = None self.qoi_values = [] @@ -128,8 +129,8 @@ def get_qoi(self, subinterval): :rtype: :class:`ufl.form.Form` ``` - :arg solution_map: a dictionary whose keys are the solution field names and whose - values are the corresponding solutions + :arg solution_map: a dictionary whose keys are the solution field names and + whose values are the corresponding solutions :type solution_map: :class:`dict` with :class:`str` keys and values and :class:`firedrake.function.Function` values :arg subinterval: the subinterval index @@ -231,16 +232,16 @@ def get_solve_blocks(self, field, subinterval, has_adj_sol=True): for block in solve_blocks: if element != block.function_space.ufl_element(): raise ValueError( - f"Solve block list for field '{field}' contains mismatching elements:" - f" {element} vs. {block.function_space.ufl_element()}." + f"Solve block list for field '{field}' contains mismatching" + f" elements: {element} vs. {block.function_space.ufl_element()}." ) # Check that the number of timesteps does not exceed the number of solve blocks num_timesteps = self.time_partition.num_timesteps_per_subinterval[subinterval] if num_timesteps > N: raise ValueError( - f"Number of timesteps exceeds number of solve blocks for field '{field}'" - f" on subinterval {subinterval}: {num_timesteps} > {N}." + f"Number of timesteps exceeds number of solve blocks for field" + f" '{field}' on subinterval {subinterval}: {num_timesteps} > {N}." ) # Check the number of timesteps is divisible by the number of solve blocks @@ -257,7 +258,8 @@ def get_solve_blocks(self, field, subinterval, has_adj_sol=True): # Check that adjoint solutions exist if all(block.adj_sol is None for block in solve_blocks): self.warning( - "No block has an adjoint solution. Has the adjoint equation been solved?" + "No block has an adjoint solution. Has the adjoint equation been" + " solved?" ) # Default adjoint solution to zero, rather than None @@ -270,8 +272,8 @@ def get_solve_blocks(self, field, subinterval, has_adj_sol=True): def _output(self, field, subinterval, solve_block): """ - For a given solve block and solution field, get the block's outputs corresponding - to the solution from the current timestep. + For a given solve block and solution field, get the block's outputs + corresponding to the solution from the current timestep. :arg field: field of interest :type field: :class:`str` @@ -390,15 +392,15 @@ def _solve_adjoint( yielded at the end of each subinterval, before clearing the tape. :kwarg solver_kwargs: parameters for the forward solver, as well as any - parameters for the QoI, which should be included as a sub-dictionary with key - 'qoi_kwargs' + parameters for the QoI, which should be included as a sub-dictionary with + key 'qoi_kwargs' :type solver_kwargs: :class:`dict` with :class:`str` keys and values which may take various types :kwarg adj_solver_kwargs: parameters for the adjoint solver :type adj_solver_kwargs: :class:`dict` with :class:`str` keys and values which may take various types - :kwarg get_adj_values: if ``True``, adjoint actions are also returned at exported - timesteps + :kwarg get_adj_values: if ``True``, adjoint actions are also returned at + exported timesteps :type get_adj_values: :class:`bool` :kwarg test_checkpoint_qoi: solve over the final subinterval when checkpointing so that the QoI value can be checked across runs @@ -650,15 +652,15 @@ def solve_adjoint( :class:`~.AdjointSolutionData` for more information. :kwarg solver_kwargs: parameters for the forward solver, as well as any - parameters for the QoI, which should be included as a sub-dictionary with key - 'qoi_kwargs' + parameters for the QoI, which should be included as a sub-dictionary with + key 'qoi_kwargs' :type solver_kwargs: :class:`dict` with :class:`str` keys and values which may take various types :kwarg adj_solver_kwargs: parameters for the adjoint solver :type adj_solver_kwargs: :class:`dict` with :class:`str` keys and values which may take various types - :kwarg get_adj_values: if ``True``, adjoint actions are also returned at exported - timesteps + :kwarg get_adj_values: if ``True``, adjoint actions are also returned at + exported timesteps :type get_adj_values: :class:`bool` :kwarg test_checkpoint_qoi: solve over the final subinterval when checkpointing so that the QoI value can be checked across runs diff --git a/goalie/error_estimation.py b/goalie/error_estimation.py index 3f83b6eb..d7a18b11 100644 --- a/goalie/error_estimation.py +++ b/goalie/error_estimation.py @@ -66,8 +66,8 @@ def get_dwr_indicator(F, adjoint_error, test_space=None): dual weighted residual (DWR) error indicator. Note that each term of a 1-form contains only one - :class:`firedrake.ufl_expr.TestFunction`. The 1-form most commonly corresponds to the - variational form of a PDE. If the PDE is linear, it should be written as in the + :class:`firedrake.ufl_expr.TestFunction`. The 1-form most commonly corresponds to + the variational form of a PDE. If the PDE is linear, it should be written as in the nonlinear case (i.e., with the solution field in place of any :class:`firedrake.ufl_expr.TrialFunction`\s. @@ -97,7 +97,8 @@ def get_dwr_indicator(F, adjoint_error, test_space=None): adjoint_error = {name: adjoint_error} elif not isinstance(adjoint_error, dict): raise TypeError( - f"Expected 'adjoint_error' to be a Function or dict, not '{type(adjoint_error)}'." + "Expected 'adjoint_error' to be a Function or dict, not" + " '{type(adjoint_error)}'." ) # Process input for test_space as a dictionary @@ -109,7 +110,8 @@ def get_dwr_indicator(F, adjoint_error, test_space=None): test_space = {key: test_space for key in adjoint_error} elif not isinstance(test_space, dict): raise TypeError( - f"Expected 'test_space' to be a FunctionSpace or dict, not '{type(test_space)}'." + "Expected 'test_space' to be a FunctionSpace or dict, not" + " '{type(test_space)}'." ) # Construct the mapping for each component @@ -119,7 +121,8 @@ def get_dwr_indicator(F, adjoint_error, test_space=None): fs = test_space[key] if not isinstance(fs, WithGeometry): raise TypeError( - f"Expected 'test_space['{key}']' to be a FunctionSpace, not '{type(fs)}'." + f"Expected 'test_space['{key}']' to be a FunctionSpace, not" + " '{type(fs)}'." ) if F.ufl_domain() != err.function_space().mesh(): raise ValueError( diff --git a/goalie/function_data.py b/goalie/function_data.py index 0393d473..bd8d4880 100644 --- a/goalie/function_data.py +++ b/goalie/function_data.py @@ -83,8 +83,8 @@ def _data_by_label(self): """ Extract field data array in an alternative layout: as a doubly-nested dictionary whose first key is the field label and second key is the field name. Entries - of the doubly-nested dictionary are doubly-nested lists, which retain the default - layout: indexed first by subinterval and then by export. + of the doubly-nested dictionary are doubly-nested lists, which retain the + default layout: indexed first by subinterval and then by export. """ tp = self.time_partition return AttrDict( @@ -133,7 +133,8 @@ def extract(self, layout="field"): Choosing a different layout simply promotes the specified variable to first access: * ``layout == "label"`` implies ``data[label][field][subinterval][export]`` - * ``layout == "subinterval"`` implies ``data[subinterval][field][label][export]`` + * ``layout == "subinterval"`` implies + ``data[subinterval][field][label][export]`` The export index is not promoted because the number of exports may differ across subintervals. diff --git a/goalie/go_mesh_seq.py b/goalie/go_mesh_seq.py index 9db5ed0c..75c2a035 100644 --- a/goalie/go_mesh_seq.py +++ b/goalie/go_mesh_seq.py @@ -171,8 +171,8 @@ def indicate_errors( :type enrichment_kwargs: :class:`dict` with :class:`str` keys and values which may take various types :kwarg solver_kwargs: parameters for the forward solver, as well as any - parameters for the QoI, which should be included as a sub-dictionary with key - 'qoi_kwargs' + parameters for the QoI, which should be included as a sub-dictionary with + key 'qoi_kwargs' :type solver_kwargs: :class:`dict` with :class:`str` keys and values which may take various types :kwarg indicator_fn: function which maps the form, adjoint error and enriched @@ -330,8 +330,8 @@ def fixed_point_iteration( r""" Apply goal-oriented mesh adaptation using a fixed point iteration loop approach. - :arg adaptor: function for adapting the mesh sequence. Its arguments are the mesh - sequence and the solution and indicator data objects. It should return + :arg adaptor: function for adapting the mesh sequence. Its arguments are the + mesh sequence and the solution and indicator data objects. It should return ``True`` if the convergence criteria checks are to be skipped for this iteration. Otherwise, it should return ``False``. :kwarg parameters: parameters to apply to the mesh adaptation process diff --git a/goalie/mesh_seq.py b/goalie/mesh_seq.py index e0f82202..4fba22bf 100644 --- a/goalie/mesh_seq.py +++ b/goalie/mesh_seq.py @@ -457,8 +457,8 @@ def _solve_forward(self, update_solutions=True, solver_kwargs=None): :kwarg update_solutions: if ``True``, updates the solution data :type update_solutions: :class:`bool` :kwarg solver_kwargs: parameters for the forward solver - :type solver_kwargs: :class:`dict` whose keys are :class:`str`\s and whose values - may take various types + :type solver_kwargs: :class:`dict` whose keys are :class:`str`\s and whose + values may take various types :yields: the solution data of the forward solves :ytype: :class:`~.ForwardSolutionData` """ @@ -563,8 +563,8 @@ def solve_forward(self, solver_kwargs=None): for more details. :kwarg solver_kwargs: parameters for the forward solver - :type solver_kwargs: :class:`dict` whose keys are :class:`str`\s and whose values - may take various types + :type solver_kwargs: :class:`dict` whose keys are :class:`str`\s and whose + values may take various types :returns: the solution data of the forward solves :rtype: :class:`~.ForwardSolutionData` """ @@ -607,8 +607,8 @@ def check_element_count_convergence(self): else: pyrint( f"Element count converged on subinterval {i} after" - f" {self.fp_iteration+1} iterations under relative tolerance" - f" {self.params.element_rtol}." + f" {self.fp_iteration+1} iterations under relative" + f" tolerance {self.params.element_rtol}." ) # Check only early subintervals are marked as converged @@ -630,8 +630,8 @@ def fixed_point_iteration( r""" Apply mesh adaptation using a fixed point iteration loop approach. - :arg adaptor: function for adapting the mesh sequence. Its arguments are the mesh - sequence and the solution data object. It should return ``True`` if the + :arg adaptor: function for adapting the mesh sequence. Its arguments are the + mesh sequence and the solution data object. It should return ``True`` if the convergence criteria checks are to be skipped for this iteration. Otherwise, it should return ``False``. :kwarg parameters: parameters to apply to the mesh adaptation process diff --git a/pyproject.toml b/pyproject.toml index 65bcaf67..3d63f41e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,6 @@ select = [ ] ignore = [ "C901", # function is too complex (TODO #165: enable this) - "E501", # line too long "E203", # whitespace before ':' "E226", # missing whitespace around arithmetic operator "E402", # module level import not at top of file @@ -60,6 +59,10 @@ ignore = [ "F403", # unable to detect undefined names "F405", # name may be undefined, or defined from star imports ] +[tool.ruff.lint.per-file-ignores] +"demos/burgers-hessian.py" = [ + "E501", # line too long +] [tool.pytest.ini_options] filterwarnings = [ diff --git a/test/test_mesh_seq.py b/test/test_mesh_seq.py index ccef4fdc..fa918297 100644 --- a/test/test_mesh_seq.py +++ b/test/test_mesh_seq.py @@ -158,7 +158,10 @@ def test_mesh_seq_time_partition_str(self): def test_mesh_seq_time_interval_repr(self): mesh_seq = MeshSeq(self.time_interval, [UnitSquareMesh(1, 1)]) - expected = "MeshSeq([Mesh(VectorElement(FiniteElement('Lagrange', triangle, 1), dim=2), .*)])" + expected = ( + "MeshSeq([Mesh(VectorElement(" + "FiniteElement('Lagrange', triangle, 1), dim=2), .*)])" + ) self.assertTrue(re.match(repr(mesh_seq), expected)) def test_mesh_seq_time_partition_2_repr(self): diff --git a/test/test_options.py b/test/test_options.py index 2bdb5462..849ae186 100644 --- a/test/test_options.py +++ b/test/test_options.py @@ -70,7 +70,10 @@ def test_maxiter_type_error(self): def test_element_rtol_type_error(self): with self.assertRaises(TypeError) as cm: AdaptParameters({"element_rtol": "0.001"}) - msg = "Expected attribute 'element_rtol' to be of type 'float' or 'int', not 'str'." + msg = ( + "Expected attribute 'element_rtol' to be of type 'float' or 'int', not" + " 'str'." + ) self.assertEqual(str(cm.exception), msg) def test_drop_out_converged_type_error(self): @@ -125,7 +128,10 @@ def test_convergence_criteria_type_error(self): def test_convergence_criteria_value_error(self): with self.assertRaises(ValueError) as cm: GoalOrientedAdaptParameters({"convergence_criteria": "both"}) - msg = "Unsupported value 'both' for 'convergence_criteria'. Choose from ['all', 'any']." + msg = ( + "Unsupported value 'both' for 'convergence_criteria'. Choose from" + " ['all', 'any']." + ) self.assertEqual(str(cm.exception), msg) def test_qoi_rtol_type_error(self): From 4fd38385dc23c6bda982babe44c73f7175fad615 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Sat, 21 Dec 2024 11:08:03 +0000 Subject: [PATCH 02/11] Enable E203 --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3d63f41e..1c76ebf2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,6 @@ select = [ ] ignore = [ "C901", # function is too complex (TODO #165: enable this) - "E203", # whitespace before ':' "E226", # missing whitespace around arithmetic operator "E402", # module level import not at top of file "E741", # ambiguous variable name From 9339ed1889cfbc6e21c15167c1eedeb564878f5e Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Sat, 21 Dec 2024 11:08:13 +0000 Subject: [PATCH 03/11] Enable E226 --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1c76ebf2..f23a1316 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,6 @@ select = [ ] ignore = [ "C901", # function is too complex (TODO #165: enable this) - "E226", # missing whitespace around arithmetic operator "E402", # module level import not at top of file "E741", # ambiguous variable name "F403", # unable to detect undefined names From 00ce1287944ddcc0e6db1efb26f86e7d7aa3a33a Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Sat, 21 Dec 2024 11:08:46 +0000 Subject: [PATCH 04/11] Enable E402 except in demos --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f23a1316..5c9c6e9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,6 @@ select = [ ] ignore = [ "C901", # function is too complex (TODO #165: enable this) - "E402", # module level import not at top of file "E741", # ambiguous variable name "F403", # unable to detect undefined names "F405", # name may be undefined, or defined from star imports @@ -61,6 +60,9 @@ ignore = [ "demos/burgers-hessian.py" = [ "E501", # line too long ] +"demos/*.py" = [ + "E402", # module level import not at top of file +] [tool.pytest.ini_options] filterwarnings = [ From 272505dbd66235e5d25a5a4490c8275296a69ad1 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Sat, 21 Dec 2024 11:10:19 +0000 Subject: [PATCH 05/11] Enable E741 --- pyproject.toml | 1 - test/sensors.py | 2 +- test/utility.py | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5c9c6e9e..3b03d8d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,6 @@ select = [ ] ignore = [ "C901", # function is too complex (TODO #165: enable this) - "E741", # ambiguous variable name "F403", # unable to detect undefined names "F405", # name may be undefined, or defined from star imports ] diff --git a/test/sensors.py b/test/sensors.py index 2d8330ce..0f957eb1 100644 --- a/test/sensors.py +++ b/test/sensors.py @@ -32,7 +32,7 @@ def interweaved(x, y): def mesh_for_sensors(dim, n): - mesh = uniform_mesh(dim, n, l=2) + mesh = uniform_mesh(dim, n, length=2) coords = firedrake.Function(mesh.coordinates) coords.interpolate(coords - as_vector([1] * dim)) return firedrake.Mesh(coords) diff --git a/test/utility.py b/test/utility.py index 5afc7eab..87961e6a 100644 --- a/test/utility.py +++ b/test/utility.py @@ -8,8 +8,8 @@ from goalie.metric import RiemannianMetric -def uniform_mesh(dim, n, l=1, **kwargs): - args = [n] * dim + [l] +def uniform_mesh(dim, n, length=1, **kwargs): + args = [n] * dim + [length] return (firedrake.SquareMesh if dim == 2 else firedrake.CubeMesh)(*args, **kwargs) From 6fa4c51922e508f528286bb57479eb68d4282d11 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Sat, 21 Dec 2024 11:28:14 +0000 Subject: [PATCH 06/11] Fix format string --- goalie/adjoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goalie/adjoint.py b/goalie/adjoint.py index 4e7a28ea..ae1d4d24 100644 --- a/goalie/adjoint.py +++ b/goalie/adjoint.py @@ -104,7 +104,7 @@ def __init__(self, time_partition, initial_meshes, **kwargs): elif self.qoi_type != "steady" and self.steady: raise ValueError( "Time partition is steady but the QoI type is set to" - " '{self.qoi_type}'." + f" '{self.qoi_type}'." ) self._controls = None self.qoi_values = [] From 50e5d8762713b76de3e9d0054de2199718485604 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Sat, 21 Dec 2024 11:26:51 +0000 Subject: [PATCH 07/11] Enable F403 except in demos --- goalie/error_estimation.py | 6 +- pyproject.toml | 2 +- test/adjoint/examples/burgers.py | 26 +++-- test/adjoint/examples/point_discharge2d.py | 83 +++++++------- test/adjoint/examples/point_discharge3d.py | 104 ++++++++---------- test/adjoint/examples/steady_flow_past_cyl.py | 55 +++++---- test/adjoint/test_adjoint.py | 9 +- test/adjoint/test_adjoint_mesh_seq.py | 6 +- test/adjoint/test_demos.py | 2 +- test/adjoint/test_fp_iteration.py | 16 ++- test/adjoint/test_utils.py | 20 ++-- test/sensors.py | 23 ++-- test/test_error_estimation.py | 37 ++++--- test/test_function_data.py | 14 ++- test/test_math.py | 2 +- test/test_metric.py | 37 ++++--- test/test_options.py | 6 +- test/test_parallel.py | 3 +- test/test_time_partition.py | 4 - test/test_utility.py | 10 +- test/utility.py | 9 ++ 21 files changed, 238 insertions(+), 236 deletions(-) diff --git a/goalie/error_estimation.py b/goalie/error_estimation.py index d7a18b11..76155d4b 100644 --- a/goalie/error_estimation.py +++ b/goalie/error_estimation.py @@ -98,7 +98,7 @@ def get_dwr_indicator(F, adjoint_error, test_space=None): elif not isinstance(adjoint_error, dict): raise TypeError( "Expected 'adjoint_error' to be a Function or dict, not" - " '{type(adjoint_error)}'." + f" '{type(adjoint_error)}'." ) # Process input for test_space as a dictionary @@ -111,7 +111,7 @@ def get_dwr_indicator(F, adjoint_error, test_space=None): elif not isinstance(test_space, dict): raise TypeError( "Expected 'test_space' to be a FunctionSpace or dict, not" - " '{type(test_space)}'." + f" '{type(test_space)}'." ) # Construct the mapping for each component @@ -122,7 +122,7 @@ def get_dwr_indicator(F, adjoint_error, test_space=None): if not isinstance(fs, WithGeometry): raise TypeError( f"Expected 'test_space['{key}']' to be a FunctionSpace, not" - " '{type(fs)}'." + f" '{type(fs)}'." ) if F.ufl_domain() != err.function_space().mesh(): raise ValueError( diff --git a/pyproject.toml b/pyproject.toml index 3b03d8d4..1a4fd520 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,6 @@ select = [ ] ignore = [ "C901", # function is too complex (TODO #165: enable this) - "F403", # unable to detect undefined names "F405", # name may be undefined, or defined from star imports ] [tool.ruff.lint.per-file-ignores] @@ -61,6 +60,7 @@ ignore = [ ] "demos/*.py" = [ "E402", # module level import not at top of file + "F403", # unable to detect undefined names ] [tool.pytest.ini_options] diff --git a/test/adjoint/examples/burgers.py b/test/adjoint/examples/burgers.py index 6e1042ea..390e9f05 100644 --- a/test/adjoint/examples/burgers.py +++ b/test/adjoint/examples/burgers.py @@ -7,8 +7,14 @@ https://firedrakeproject.org/demos/burgers.py.html """ -from firedrake import * +import ufl from firedrake.__future__ import interpolate +from firedrake.assemble import assemble +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace, VectorFunctionSpace +from firedrake.solving import solve +from firedrake.ufl_expr import TestFunction +from firedrake.utility_meshes import UnitSquareMesh # Problem setup n = 32 @@ -22,9 +28,7 @@ def get_function_spaces(mesh): - r""" - :math:`\mathbb P2` space. - """ + r""":math:`\mathbb P2` space.""" return {"uv_2d": VectorFunctionSpace(mesh, "CG", 2)} @@ -47,9 +51,9 @@ def solver(i): nu = Function(R).assign(0.0001) v = TestFunction(fs) F = ( - inner((u - u_) / dtc, v) * dx - + inner(dot(u, nabla_grad(u)), v) * dx - + nu * inner(grad(u), grad(v)) * dx + ufl.inner((u - u_) / dtc, v) * ufl.dx + + ufl.inner(ufl.dot(u, ufl.nabla_grad(u)), v) * ufl.dx + + nu * ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx ) # Time integrate from t_start to t_end @@ -72,8 +76,10 @@ def get_initial_condition(self): Initial condition which is sinusoidal in the x-direction. """ init_fs = self.function_spaces["uv_2d"][0] - x, y = SpatialCoordinate(self.meshes[0]) - return {"uv_2d": assemble(interpolate(as_vector([sin(pi * x), 0]), init_fs))} + x, y = ufl.SpatialCoordinate(self.meshes[0]) + return { + "uv_2d": assemble(interpolate(ufl.as_vector([ufl.sin(ufl.pi * x), 0]), init_fs)) + } def get_qoi(self, i): @@ -86,7 +92,7 @@ def get_qoi(self, i): def time_integrated_qoi(t): u = self.fields["uv_2d"][0] - return dtc * inner(u, u) * ds(2) + return dtc * ufl.inner(u, u) * ufl.ds(2) def end_time_qoi(): return time_integrated_qoi(end_time) diff --git a/test/adjoint/examples/point_discharge2d.py b/test/adjoint/examples/point_discharge2d.py index e0f18ceb..05af42a1 100644 --- a/test/adjoint/examples/point_discharge2d.py +++ b/test/adjoint/examples/point_discharge2d.py @@ -1,23 +1,24 @@ """ -Problem specification for a simple -advection-diffusion test case with a -point source, from [Riadh et al. 2014]. - -This test case is notable for Goalie -because it has an analytical solution, -meaning the effectivity index can be -computed. - -[Riadh et al. 2014] A. Riadh, G. - Cedric, M. Jean, "TELEMAC modeling - system: 2D hydrodynamics TELEMAC-2D - software release 7.0 user manual." - Paris: R&D, Electricite de France, - p. 134 (2014). +Problem specification for a simple advection-diffusion test case with a point source, +from [Riadh et al. 2014]. + +This test case is notable for Goalie because it has an analytical solution, meaning the +effectivity index can be computed. + +[Riadh et al. 2014] A. Riadh, G. Cedric, M. Jean, "TELEMAC modeling system: 2D + hydrodynamics TELEMAC-2D software release 7.0 user manual." Paris: R&D, Electricite + de France, p. 134 (2014). """ import numpy as np -from firedrake import * +import ufl +from firedrake.assemble import assemble +from firedrake.bcs import DirichletBC +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace +from firedrake.solving import solve +from firedrake.ufl_expr import TestFunction +from firedrake.utility_meshes import RectangleMesh from goalie.math import bessk0 @@ -43,19 +44,15 @@ def get_function_spaces(mesh): def source(mesh): """ - Gaussian approximation to a point source - at (2, 5) with discharge rate 100 on a + Gaussian approximation to a point source at (2, 5) with discharge rate 100 on a given mesh. """ - x, y = SpatialCoordinate(mesh) - return 100.0 * exp(-((x - src_x) ** 2 + (y - src_y) ** 2) / src_r**2) + x, y = ufl.SpatialCoordinate(mesh) + return 100.0 * ufl.exp(-((x - src_x) ** 2 + (y - src_y) ** 2) / src_r**2) def get_solver(self): - """ - Advection-diffusion equation - solved using a direct method. - """ + """Advection-diffusion equation solved using a direct method.""" def solver(i): fs = self.function_spaces["tracer_2d"][i] @@ -67,22 +64,22 @@ def solver(i): D = Function(R).assign(0.1) u_x = Function(R).assign(1.0) u_y = Function(R).assign(0.0) - u = as_vector([u_x, u_y]) - h = CellSize(self[i]) + u = ufl.as_vector([u_x, u_y]) + h = ufl.CellSize(self[i]) S = source(self[i]) # Stabilisation parameter - unorm = sqrt(dot(u, u)) + unorm = ufl.sqrt(ufl.dot(u, u)) tau = 0.5 * h / unorm - tau = min_value(tau, unorm * h / (6 * D)) + tau = ufl.min_value(tau, unorm * h / (6 * D)) # Setup variational problem psi = TestFunction(fs) - psi = psi + tau * dot(u, grad(psi)) + psi = psi + tau * ufl.dot(u, ufl.grad(psi)) F = ( - S * psi * dx - - dot(u, grad(c)) * psi * dx - - inner(D * grad(c), grad(psi)) * dx + S * psi * ufl.dx + - ufl.dot(u, ufl.grad(c)) * psi * ufl.dx + - ufl.inner(D * ufl.grad(c), ufl.grad(psi)) * ufl.dx ) # Zero Dirichlet condition on the left-hand (inlet) boundary @@ -104,32 +101,30 @@ def solver(i): def get_qoi(self, i): """ - Quantity of interest which integrates - the tracer concentration over an offset + Quantity of interest which integrates the tracer concentration over an offset receiver region. """ def steady_qoi(): c = self.fields["tracer_2d"] - x, y = SpatialCoordinate(self[i]) - kernel = conditional((x - rec_x) ** 2 + (y - rec_y) ** 2 < rec_r**2, 1, 0) - area = assemble(kernel * dx) - area_analytical = pi * rec_r**2 + x, y = ufl.SpatialCoordinate(self[i]) + kernel = ufl.conditional((x - rec_x) ** 2 + (y - rec_y) ** 2 < rec_r**2, 1, 0) + area = assemble(kernel * ufl.dx) + area_analytical = ufl.pi * rec_r**2 scaling = 1.0 if np.allclose(area, 0.0) else area_analytical / area - return scaling * kernel * c * dx + return scaling * kernel * c * ufl.dx return steady_qoi def analytical_solution(mesh): """ - Analytical solution as represented on - a given mesh. See [Riadh et al. 2014]. + Analytical solution as represented on a given mesh. See [Riadh et al. 2014]. """ - x, y = SpatialCoordinate(mesh) + x, y = ufl.SpatialCoordinate(mesh) R = FunctionSpace(mesh, "R", 0) u = Function(R).assign(1.0) D = Function(R).assign(0.1) Pe = 0.5 * u / D - r = max_value(sqrt((x - src_x) ** 2 + (y - src_y) ** 2), src_r) - return 0.5 / (pi * D) * exp(Pe * (x - src_x)) * bessk0(Pe * r) + r = ufl.max_value(ufl.sqrt((x - src_x) ** 2 + (y - src_y) ** 2), src_r) + return 0.5 / (ufl.pi * D) * ufl.exp(Pe * (x - src_x)) * bessk0(Pe * r) diff --git a/test/adjoint/examples/point_discharge3d.py b/test/adjoint/examples/point_discharge3d.py index 29a4d36b..2fb07798 100644 --- a/test/adjoint/examples/point_discharge3d.py +++ b/test/adjoint/examples/point_discharge3d.py @@ -1,32 +1,28 @@ """ -Problem specification for a simple -advection-diffusion test case with a -point source. Extended from -[Riadh et al. 2014] as in -[Wallwork et al. 2020]. - -This test case is notable for Goalie -because it is in 3D and has an -analytical solution, meaning the -effectivity index can be computed. - -[Riadh et al. 2014] A. Riadh, G. - Cedric, M. Jean, "TELEMAC modeling - system: 2D hydrodynamics TELEMAC-2D - software release 7.0 user manual." - Paris: R&D, Electricite de France, - p. 134 (2014). - -[Wallwork et al. 2020] J.G. Wallwork, - N. Barral, D.A. Ham, M.D. Piggott, - "Anisotropic Goal-Oriented Mesh - Adaptation in Firedrake". In: - Proceedings of the 28th International - Meshing Roundtable (2020). +Problem specification for a simple advection-diffusion test case with a point source. +Extended from [Riadh et al. 2014] as in [Wallwork et al. 2020]. + +This test case is notable for Goalie because it is in 3D and has an analytical solution, +meaning the effectivity index can be computed. + +[Riadh et al. 2014] A. Riadh, G. Cedric, M. Jean, "TELEMAC modeling system: 2D + hydrodynamics TELEMAC-2D software release 7.0 user manual." Paris: R&D, Electricite + de France, p. 134 (2014). + +[Wallwork et al. 2020] J.G. Wallwork, N. Barral, D.A. Ham, M.D. Piggott, "Anisotropic + Goal-Oriented Mesh Adaptation in Firedrake". In: Proceedings of the 28th + International Meshing Roundtable (2020). """ import numpy as np -from firedrake import * +import ufl +from firedrake.assemble import assemble +from firedrake.bcs import DirichletBC +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace +from firedrake.solving import solve +from firedrake.ufl_expr import TestFunction +from firedrake.utility_meshes import BoxMesh from goalie.math import bessk0 @@ -44,29 +40,23 @@ def get_function_spaces(mesh): - r""" - :math:`\mathbb P1` space. - """ + r""":math:`\mathbb P1` space.""" return {"tracer_3d": FunctionSpace(mesh, "CG", 1)} def source(mesh): """ - Gaussian approximation to a point source - at (2, 5, 5) with discharge rate 100 on a + Gaussian approximation to a point source at (2, 5, 5) with discharge rate 100 on a given mesh. """ - x, y, z = SpatialCoordinate(mesh) - return 100.0 * exp( + x, y, z = ufl.SpatialCoordinate(mesh) + return 100.0 * ufl.exp( -((x - src_x) ** 2 + (y - src_y) ** 2 + (z - src_z) ** 2) / src_r**2 ) def get_solver(self): - """ - Advection-diffusion equation - solved using a direct method. - """ + """Advection-diffusion equation solved using a direct method.""" def solver(i): fs = self.function_spaces["tracer_3d"][i] @@ -79,22 +69,22 @@ def solver(i): u_x = Function(R).assign(1.0) u_y = Function(R).assign(0.0) u_z = Function(R).assign(0.0) - u = as_vector([u_x, u_y, u_z]) - h = CellSize(self[i]) + u = ufl.as_vector([u_x, u_y, u_z]) + h = ufl.CellSize(self[i]) S = source(self[i]) # Stabilisation parameter - unorm = sqrt(dot(u, u)) + unorm = ufl.sqrt(ufl.dot(u, u)) tau = 0.5 * h / unorm - tau = min_value(tau, unorm * h / (6 * D)) + tau = ufl.min_value(tau, unorm * h / (6 * D)) # Setup variational problem psi = TestFunction(fs) - psi = psi + tau * dot(u, grad(psi)) + psi = psi + tau * ufl.dot(u, ufl.grad(psi)) F = ( - S * psi * dx - - dot(u, grad(c)) * psi * dx - - inner(D * grad(c), grad(psi)) * dx + S * psi * ufl.dx + - ufl.dot(u, ufl.grad(c)) * psi * ufl.dx + - ufl.inner(D * ufl.grad(c), ufl.grad(psi)) * ufl.dx ) # Zero Dirichlet condition on the left-hand (inlet) boundary @@ -116,34 +106,32 @@ def solver(i): def get_qoi(self, i): """ - Quantity of interest which integrates - the tracer concentration over an offset + Quantity of interest which integrates the tracer concentration over an offset receiver region. """ def steady_qoi(): c = self.fields["tracer_3d"] - x, y, z = SpatialCoordinate(self[i]) - kernel = conditional( + x, y, z = ufl.SpatialCoordinate(self[i]) + kernel = ufl.conditional( (x - rec_x) ** 2 + (y - rec_y) ** 2 + (z - rec_z) ** 2 < rec_r**2, 1, 0 ) - area = assemble(kernel * dx) - area_analytical = pi * rec_r**2 + area = assemble(kernel * ufl.dx) + area_analytical = ufl.pi * rec_r**2 scaling = 1.0 if np.allclose(area, 0.0) else area_analytical / area - return scaling * kernel * c * dx + return scaling * kernel * c * ufl.dx return steady_qoi def analytical_solution(mesh): - """ - Analytical solution as represented on - a given mesh. - """ - x, y, z = SpatialCoordinate(mesh) + """Analytical solution as represented on a given mesh.""" + x, y, z = ufl.SpatialCoordinate(mesh) R = FunctionSpace(mesh, "R", 0) u = Function(R).assign(1.0) D = Function(R).assign(0.1) Pe = 0.5 * u / D - r = max_value(sqrt((x - src_x) ** 2 + (y - src_y) ** 2 + (z - src_z) ** 2), src_r) - return 0.5 / (pi * D) * exp(Pe * (x - src_x)) * bessk0(Pe * r) + r = ufl.max_value( + ufl.sqrt((x - src_x) ** 2 + (y - src_y) ** 2 + (z - src_z) ** 2), src_r + ) + return 0.5 / (ufl.pi * D) * ufl.exp(Pe * (x - src_x)) * bessk0(Pe * r) diff --git a/test/adjoint/examples/steady_flow_past_cyl.py b/test/adjoint/examples/steady_flow_past_cyl.py index 811456e9..7a4bbf23 100644 --- a/test/adjoint/examples/steady_flow_past_cyl.py +++ b/test/adjoint/examples/steady_flow_past_cyl.py @@ -1,11 +1,8 @@ """ -Problem specification for a simple steady -state flow-past-a-cylinder test case which +Problem specification for a simple steady state flow-past-a-cylinder test case which solves a Stokes problem. -The test case is notable for Goalie -because the prognostic equation is -nonlinear. +The test case is notable for Goalie because the prognostic equation is nonlinear. Code here is based on that found at https://nbviewer.jupyter.org/github/firedrakeproject/firedrake/blob/master/docs/notebooks/06-pde-constrained-optimisation.ipynb @@ -13,8 +10,15 @@ import os -from firedrake import * +import ufl from firedrake.__future__ import interpolate +from firedrake.assemble import assemble +from firedrake.bcs import DirichletBC +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace, VectorFunctionSpace +from firedrake.mesh import Mesh +from firedrake.solving import solve +from firedrake.ufl_expr import TestFunctions mesh = Mesh(os.path.join(os.path.dirname(__file__), "mesh-with-hole.msh")) fields = ["up"] @@ -26,17 +30,12 @@ def get_function_spaces(mesh): - r""" - Taylor-Hood :math:`\mathbb P2-\mathbb P1` space. - """ + r"""Taylor-Hood :math:`\mathbb P2-\mathbb P1` space.""" return {"up": VectorFunctionSpace(mesh, "CG", 2) * FunctionSpace(mesh, "CG", 1)} def get_solver(self): - """ - Stokes problem solved using a - direct method. - """ + """Stokes problem solved using a direct method.""" def solver(i): W = self.function_spaces["up"][i] @@ -45,18 +44,18 @@ def solver(i): # Define variational problem R = FunctionSpace(self[i], "R", 0) nu = Function(R).assign(1.0) - u, p = split(up) + u, p = ufl.split(up) v, q = TestFunctions(W) F = ( - inner(dot(u, nabla_grad(u)), v) * dx - + nu * inner(grad(u), grad(v)) * dx - - inner(p, div(v)) * dx - - inner(q, div(u)) * dx + ufl.inner(ufl.dot(u, ufl.nabla_grad(u)), v) * ufl.dx + + nu * ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx + - ufl.inner(p, ufl.div(v)) * ufl.dx + - ufl.inner(q, ufl.div(u)) * ufl.dx ) # Define inflow and no-slip boundary conditions - y = SpatialCoordinate(self[i])[1] - u_inflow = as_vector([y * (10 - y) / 25.0, 0]) + y = ufl.SpatialCoordinate(self[i])[1] + u_inflow = ufl.as_vector([y * (10 - y) / 25.0, 0]) noslip = DirichletBC(W.sub(0), (0, 0), (3, 5)) inflow = DirichletBC(W.sub(0), assemble(interpolate(u_inflow, W.sub(0))), 1) bcs = [inflow, noslip, DirichletBC(W.sub(0), 0, 4)] @@ -84,12 +83,11 @@ def solver(i): def get_initial_condition(self): """ - Dummy initial condition function which - acts merely to pass over the + Dummy initial condition function which acts merely to pass over the :class:`FunctionSpace`. """ - x, y = SpatialCoordinate(self[0]) - u_inflow = as_vector([y * (10 - y) / 25.0, 0]) + x, y = ufl.SpatialCoordinate(self[0]) + u_inflow = ufl.as_vector([y * (10 - y) / 25.0, 0]) up = Function(self.function_spaces["up"][0]) u, p = up.subfunctions u.interpolate(u_inflow) @@ -97,13 +95,10 @@ def get_initial_condition(self): def get_qoi(self, i): - """ - Quantity of interest which integrates - pressure over the boundary of the hole. - """ + """Quantity of interest which integrates pressure over the boundary of the hole.""" def steady_qoi(): - u, p = split(self.fields["up"]) - return p * ds(4) + u, p = ufl.split(self.fields["up"]) + return p * ufl.ds(4) return steady_qoi diff --git a/test/adjoint/test_adjoint.py b/test/adjoint/test_adjoint.py index b917260e..40377ebe 100644 --- a/test/adjoint/test_adjoint.py +++ b/test/adjoint/test_adjoint.py @@ -7,12 +7,17 @@ import sys import unittest +import numpy as np import pyadjoint import pytest from animate.utility import errornorm, norm -from firedrake import Cofunction, UnitTriangleMesh +from firedrake.cofunction import Cofunction +from firedrake.utility_meshes import UnitTriangleMesh -from goalie_adjoint import * +from goalie.adjoint import AdjointMeshSeq +from goalie.log import DEBUG, pyrint, set_log_level +from goalie.time_partition import TimeInterval, TimePartition +from goalie.utility import AttrDict, VTKFile sys.path.append(os.path.join(os.path.dirname(__file__), "examples")) diff --git a/test/adjoint/test_adjoint_mesh_seq.py b/test/adjoint/test_adjoint_mesh_seq.py index 9ad23659..c5e230a2 100644 --- a/test/adjoint/test_adjoint_mesh_seq.py +++ b/test/adjoint/test_adjoint_mesh_seq.py @@ -24,10 +24,10 @@ from parameterized import parameterized from pyadjoint.block_variable import BlockVariable +from goalie.adjoint import AdjointMeshSeq from goalie.go_mesh_seq import GoalOrientedMeshSeq -from goalie.log import * -from goalie.time_partition import TimeInterval -from goalie_adjoint import * +from goalie.log import WARNING +from goalie.time_partition import TimeInterval, TimePartition class TestBlockLogic(unittest.TestCase): diff --git a/test/adjoint/test_demos.py b/test/adjoint/test_demos.py index 9d036864..333c1ec5 100644 --- a/test/adjoint/test_demos.py +++ b/test/adjoint/test_demos.py @@ -10,7 +10,7 @@ import pytest -from goalie.log import * +from goalie.log import WARNING, set_log_level cwd = os.path.abspath(os.path.dirname(__file__)) demo_dir = os.path.abspath(os.path.join(cwd, "..", "..", "demos")) diff --git a/test/adjoint/test_fp_iteration.py b/test/adjoint/test_fp_iteration.py index 08d87637..93a873fd 100644 --- a/test/adjoint/test_fp_iteration.py +++ b/test/adjoint/test_fp_iteration.py @@ -2,17 +2,25 @@ import unittest from unittest.mock import MagicMock -from firedrake import * +import numpy as np +import ufl +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace +from firedrake.utility_meshes import UnitSquareMesh, UnitTriangleMesh from parameterized import parameterized -from goalie_adjoint import * +from goalie.adjoint import AdjointMeshSeq +from goalie.go_mesh_seq import GoalOrientedAdaptParameters, GoalOrientedMeshSeq +from goalie.mesh_seq import MeshSeq +from goalie.options import AdaptParameters +from goalie.time_partition import TimeInstant, TimePartition def constant_qoi(mesh_seq, index): R = FunctionSpace(mesh_seq[index], "R", 0) def qoi(): - return Function(R).assign(1) * dx + return Function(R).assign(1) * ufl.dx return qoi @@ -21,7 +29,7 @@ def oscillating_qoi(mesh_seq, index): R = FunctionSpace(mesh_seq[index], "R", 0) def qoi(): - return Function(R).assign(1 if mesh_seq.fp_iteration % 2 == 0 else 2) * dx + return Function(R).assign(1 if mesh_seq.fp_iteration % 2 == 0 else 2) * ufl.dx return qoi diff --git a/test/adjoint/test_utils.py b/test/adjoint/test_utils.py index 91d54ad3..ee3b3bad 100644 --- a/test/adjoint/test_utils.py +++ b/test/adjoint/test_utils.py @@ -1,10 +1,14 @@ import unittest import numpy as np -from firedrake import * +import ufl +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace +from firedrake.utility_meshes import UnitSquareMesh -from goalie.adjoint import annotate_qoi -from goalie_adjoint import * +from goalie.adjoint import AdjointMeshSeq, annotate_qoi +from goalie.go_mesh_seq import GoalOrientedAdaptParameters +from goalie.time_partition import TimeInterval class TestAdjointUtils(unittest.TestCase): @@ -27,7 +31,7 @@ def get_qoi(mesh_seq, i): R = FunctionSpace(mesh_seq[i], "R", 0) def qoi(): - return Function(R).assign(1) * dx + return Function(R).assign(1) * ufl.dx return qoi @@ -39,7 +43,7 @@ def get_qoi(mesh_seq, i): R = FunctionSpace(mesh_seq[i], "R", 0) def qoi(t): - return Function(R).assign(1) * dx + return Function(R).assign(1) * ufl.dx return qoi @@ -51,7 +55,7 @@ def get_qoi(mesh_seq, i): R = FunctionSpace(mesh_seq[i], "R", 0) def qoi(): - return Function(R).assign(1) * dx + return Function(R).assign(1) * ufl.dx return qoi @@ -66,7 +70,7 @@ def get_qoi(mesh_seq, i): R = FunctionSpace(mesh_seq[i], "R", 0) def qoi(t): - return Function(R).assign(1) * dx + return Function(R).assign(1) * ufl.dx return qoi @@ -81,7 +85,7 @@ def get_qoi(mesh_seq, i): R = FunctionSpace(mesh_seq[i], "R", 0) def qoi(t, r): - return Function(R).assign(1) * dx + return Function(R).assign(1) * ufl.dx return qoi diff --git a/test/sensors.py b/test/sensors.py index 0f957eb1..ac847926 100644 --- a/test/sensors.py +++ b/test/sensors.py @@ -6,11 +6,9 @@ moving geometries. Diss. 2011. """ -import firedrake -from ufl import * -from utility import uniform_mesh +import ufl -__all__ = ["bowl", "hyperbolic", "multiscale", "interweaved", "mesh_for_sensors"] +__all__ = ["bowl", "hyperbolic", "multiscale", "interweaved"] def bowl(*coords): @@ -18,21 +16,16 @@ def bowl(*coords): def hyperbolic(x, y): - return conditional( - abs(x * y) < 2 * pi / 50, 0.01 * sin(50 * x * y), sin(50 * x * y) + return ufl.conditional( + abs(x * y) < 2 * ufl.pi / 50, 0.01 * ufl.sin(50 * x * y), ufl.sin(50 * x * y) ) def multiscale(x, y): - return 0.1 * sin(50 * x) + atan(0.1 / (sin(5 * y) - 2 * x)) + return 0.1 * ufl.sin(50 * x) + ufl.atan(0.1 / (ufl.sin(5 * y) - 2 * x)) def interweaved(x, y): - return atan(0.1 / (sin(5 * y) - 2 * x)) + atan(0.5 / (sin(3 * y) - 7 * x)) - - -def mesh_for_sensors(dim, n): - mesh = uniform_mesh(dim, n, length=2) - coords = firedrake.Function(mesh.coordinates) - coords.interpolate(coords - as_vector([1] * dim)) - return firedrake.Mesh(coords) + return ufl.atan(0.1 / (ufl.sin(5 * y) - 2 * x)) + ufl.atan( + 0.5 / (ufl.sin(3 * y) - 7 * x) + ) diff --git a/test/test_error_estimation.py b/test/test_error_estimation.py index bc74fe61..1ea19a67 100644 --- a/test/test_error_estimation.py +++ b/test/test_error_estimation.py @@ -1,6 +1,11 @@ import unittest -from firedrake import * +import numpy as np +import ufl +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace +from firedrake.ufl_expr import TestFunction, TrialFunction +from firedrake.utility_meshes import UnitSquareMesh, UnitTriangleMesh from parameterized import parameterized from goalie.error_estimation import ( @@ -38,20 +43,20 @@ def test_form_type_error(self): self.assertEqual(str(cm.exception), msg) def test_exterior_facet_integral(self): - F = self.one * ds(1) - self.one * ds(2) + F = self.one * ufl.ds(1) - self.one * ufl.ds(2) indicator = form2indicator(F) self.assertAlmostEqual(indicator.dat.data[0], -1) self.assertAlmostEqual(indicator.dat.data[1], 1) def test_interior_facet_integral(self): - F = avg(self.one) * dS + F = ufl.avg(self.one) * ufl.dS indicator = form2indicator(F) - self.assertAlmostEqual(indicator.dat.data[0], sqrt(2)) - self.assertAlmostEqual(indicator.dat.data[1], sqrt(2)) + self.assertAlmostEqual(indicator.dat.data[0], np.sqrt(2)) + self.assertAlmostEqual(indicator.dat.data[1], np.sqrt(2)) def test_cell_integral(self): - x, y = SpatialCoordinate(self.mesh) - F = conditional(x + y < 1, 1, 0) * dx + x, y = ufl.SpatialCoordinate(self.mesh) + F = ufl.conditional(x + y < 1, 1, 0) * ufl.dx indicator = form2indicator(F) self.assertAlmostEqual(indicator.dat.data[0], 0) self.assertAlmostEqual(indicator.dat.data[1], 0.5) @@ -88,14 +93,14 @@ def test_absolute_value_type_error(self): def test_unit_time_instant(self): mesh_seq = self.mesh_seq(time_partition=TimeInstant("field", time=1.0)) - mesh_seq.indicators["field"][0][0].assign(form2indicator(self.one * dx)) + mesh_seq.indicators["field"][0][0].assign(form2indicator(self.one * ufl.dx)) estimator = mesh_seq.error_estimate() self.assertAlmostEqual(estimator, 1) # 1 * (0.5 + 0.5) @parameterized.expand([[False], [True]]) def test_unit_time_instant_abs(self, absolute_value): mesh_seq = self.mesh_seq(time_partition=TimeInstant("field", time=1.0)) - mesh_seq.indicators["field"][0][0].assign(form2indicator(-self.one * dx)) + mesh_seq.indicators["field"][0][0].assign(form2indicator(-self.one * ufl.dx)) estimator = mesh_seq.error_estimate(absolute_value=absolute_value) self.assertAlmostEqual( estimator, 1 if absolute_value else -1 @@ -103,7 +108,7 @@ def test_unit_time_instant_abs(self, absolute_value): def test_half_time_instant(self): mesh_seq = self.mesh_seq(time_partition=TimeInstant("field", time=0.5)) - mesh_seq.indicators["field"][0][0].assign(form2indicator(self.one * dx)) + mesh_seq.indicators["field"][0][0].assign(form2indicator(self.one * ufl.dx)) estimator = mesh_seq.error_estimate() self.assertAlmostEqual(estimator, 0.5) # 0.5 * (0.5 + 0.5) @@ -111,7 +116,7 @@ def test_time_partition_same_timestep(self): mesh_seq = self.mesh_seq( time_partition=TimePartition(1.0, 2, [0.5, 0.5], ["field"]) ) - mesh_seq.indicators["field"][0][0].assign(form2indicator(2 * self.one * dx)) + mesh_seq.indicators["field"][0][0].assign(form2indicator(2 * self.one * ufl.dx)) estimator = mesh_seq.error_estimate() self.assertAlmostEqual(estimator, 1) # 2 * 0.5 * (0.5 + 0.5) @@ -119,7 +124,7 @@ def test_time_partition_different_timesteps(self): mesh_seq = self.mesh_seq( time_partition=TimePartition(1.0, 2, [0.5, 0.25], ["field"]) ) - indicator = form2indicator(self.one * dx) + indicator = form2indicator(self.one * ufl.dx) mesh_seq.indicators["field"][0][0].assign(indicator) mesh_seq.indicators["field"][1][0].assign(indicator) mesh_seq.indicators["field"][1][1].assign(indicator) @@ -132,7 +137,7 @@ def test_time_instant_multiple_fields(self): mesh_seq = self.mesh_seq( time_partition=TimeInstant(["field1", "field2"], time=1.0) ) - indicator = form2indicator(self.one * dx) + indicator = form2indicator(self.one * ufl.dx) mesh_seq.indicators["field1"][0][0].assign(indicator) mesh_seq.indicators["field2"][0][0].assign(indicator) estimator = mesh_seq.error_estimate() @@ -148,7 +153,7 @@ def setUp(self): super().setUp() self.two = Function(self.fs, name="Dos") self.two.assign(2) - self.F = self.one * self.test * dx + self.F = self.one * self.test * ufl.dx def test_form_type_error(self): with self.assertRaises(TypeError) as cm: @@ -248,7 +253,3 @@ def test_convert_adjoint_error_mismatch(self): get_dwr_indicator(self.F, self.two, test_space=test_space) msg = "Key 'Dos' does not exist in the test space provided." self.assertEqual(str(cm.exception), msg) - - -if __name__ == "__main__": - unittest.main() # pragma: no cover diff --git a/test/test_function_data.py b/test/test_function_data.py index b88375fa..87576c55 100644 --- a/test/test_function_data.py +++ b/test/test_function_data.py @@ -3,12 +3,18 @@ """ import abc +import os import unittest from tempfile import TemporaryDirectory -from firedrake import * +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace +from firedrake.mesh import MeshHierarchy +from firedrake.utility_meshes import UnitTriangleMesh -from goalie import * +from goalie.function_data import AdjointSolutionData, ForwardSolutionData, IndicatorData +from goalie.time_partition import TimePartition +from goalie.utility import AttrDict class BaseTestCases: @@ -453,7 +459,3 @@ def test_transfer_prolong(self): source_function.dat.data.all() == target_function.dat.data.all() ) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test_math.py b/test/test_math.py index 50f6ade9..5571bb09 100644 --- a/test/test_math.py +++ b/test/test_math.py @@ -4,7 +4,7 @@ import scipy as sp from firedrake import UnitTriangleMesh -from goalie.math import * +from goalie.math import bessi0, bessk0 class TestBessel(unittest.TestCase): diff --git a/test/test_metric.py b/test/test_metric.py index e877596c..6c2637e1 100644 --- a/test/test_metric.py +++ b/test/test_metric.py @@ -6,14 +6,19 @@ import numpy as np import pytest +import sensors from animate.metric import RiemannianMetric from animate.utility import errornorm -from firedrake import * +from firedrake.functionspace import TensorFunctionSpace from parameterized import parameterized -from sensors import * -from utility import uniform_mesh +from utility import mesh_for_sensors, uniform_mesh -from goalie import * +from goalie.metric import ( + enforce_variable_constraints, + ramp_complexity, + space_time_normalise, +) +from goalie.time_partition import TimeInterval, TimePartition class BaseClasses: @@ -127,18 +132,18 @@ def test_target_complexity_negative_error(self): @pytest.mark.slow @parameterized.expand( [ - (bowl, 1), - (bowl, 2), - (bowl, np.inf), - (hyperbolic, 1), - (hyperbolic, 2), - (hyperbolic, np.inf), - (multiscale, 1), - (multiscale, 2), - (multiscale, np.inf), - (interweaved, 1), - (interweaved, 2), - (interweaved, np.inf), + (sensors.bowl, 1), + (sensors.bowl, 2), + (sensors.bowl, np.inf), + (sensors.hyperbolic, 1), + (sensors.hyperbolic, 2), + (sensors.hyperbolic, np.inf), + (sensors.multiscale, 1), + (sensors.multiscale, 2), + (sensors.multiscale, np.inf), + (sensors.interweaved, 1), + (sensors.interweaved, 2), + (sensors.interweaved, np.inf), ] ) def test_consistency(self, sensor, degree): diff --git a/test/test_options.py b/test/test_options.py index 849ae186..89e54dd2 100644 --- a/test/test_options.py +++ b/test/test_options.py @@ -1,6 +1,6 @@ import unittest -from goalie.options import * +from goalie.options import AdaptParameters, GoalOrientedAdaptParameters class TestAdaptParameters(unittest.TestCase): @@ -148,7 +148,3 @@ def test_estimator_rtol_type_error(self): " 'str'." ) self.assertEqual(str(cm.exception), msg) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test_parallel.py b/test/test_parallel.py index 544f6d52..917a2330 100644 --- a/test/test_parallel.py +++ b/test/test_parallel.py @@ -1,5 +1,6 @@ import pytest -from firedrake import * +from firedrake.utility_meshes import UnitCubeMesh, UnitSquareMesh +from pyop2.mpi import COMM_WORLD from goalie.mesh_seq import MeshSeq from goalie.time_partition import TimeInterval diff --git a/test/test_time_partition.py b/test/test_time_partition.py index f4a23606..2f57a5fa 100644 --- a/test/test_time_partition.py +++ b/test/test_time_partition.py @@ -339,7 +339,3 @@ def test_time_partition_slice(self): self.assertAlmostEqual(tp12.timesteps, timesteps[1:3]) self.assertEqual(tp0.field_names, self.field_names) self.assertEqual(tp0.field_names, tp12.field_names) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test_utility.py b/test/test_utility.py index 9713e262..29c2d762 100644 --- a/test/test_utility.py +++ b/test/test_utility.py @@ -7,10 +7,12 @@ import shutil import unittest -from firedrake import * +from firedrake.function import Function +from firedrake.functionspace import FunctionSpace from utility import uniform_mesh -from goalie import * +from goalie.error_estimation import effectivity_index +from goalie.utility import create_directory pointwise_norm_types = [["l1"], ["l2"], ["linf"]] integral_scalar_norm_types = [["L1"], ["L2"], ["L4"], ["H1"], ["HCurl"]] @@ -78,7 +80,3 @@ def test_create_directory(): raise OSError( f"Can't remove {fpath} because it isn't empty. Contents: {ls}." ) from e - - -if __name__ == "__main__": - unittest.main() diff --git a/test/utility.py b/test/utility.py index 87961e6a..b4800b81 100644 --- a/test/utility.py +++ b/test/utility.py @@ -7,6 +7,8 @@ from goalie.metric import RiemannianMetric +__all__ = ["uniform_mesh", "uniform_metric", "mesh_for_sensors"] + def uniform_mesh(dim, n, length=1, **kwargs): args = [n] * dim + [length] @@ -18,3 +20,10 @@ def uniform_metric(function_space, scaling): metric = RiemannianMetric(function_space) metric.interpolate(scaling * ufl.Identity(dim)) return metric + + +def mesh_for_sensors(dim, n): + mesh = uniform_mesh(dim, n, length=2) + coords = firedrake.Function(mesh.coordinates) + coords.interpolate(coords - ufl.as_vector([1] * dim)) + return firedrake.Mesh(coords) From 9677cd1188b340ae26b1cff5a290713249b962ec Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 24 Dec 2024 10:13:17 +0000 Subject: [PATCH 08/11] Enable F405 except in demos --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1a4fd520..bcfe7fab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,6 @@ select = [ ] ignore = [ "C901", # function is too complex (TODO #165: enable this) - "F405", # name may be undefined, or defined from star imports ] [tool.ruff.lint.per-file-ignores] "demos/burgers-hessian.py" = [ @@ -61,6 +60,7 @@ ignore = [ "demos/*.py" = [ "E402", # module level import not at top of file "F403", # unable to detect undefined names + "F405", # name may be undefined, or defined from star imports ] [tool.pytest.ini_options] From 6a213d87bcbccfe7ebd64c28504b883e47eb5174 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 24 Dec 2024 10:23:55 +0000 Subject: [PATCH 09/11] Fix imports --- test/adjoint/examples/point_discharge2d.py | 4 ++-- test/adjoint/examples/point_discharge3d.py | 4 ++-- test/adjoint/test_adjoint.py | 3 ++- test/test_function_data.py | 2 +- test/test_utility.py | 3 +-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/adjoint/examples/point_discharge2d.py b/test/adjoint/examples/point_discharge2d.py index 05af42a1..38da03e0 100644 --- a/test/adjoint/examples/point_discharge2d.py +++ b/test/adjoint/examples/point_discharge2d.py @@ -17,7 +17,7 @@ from firedrake.function import Function from firedrake.functionspace import FunctionSpace from firedrake.solving import solve -from firedrake.ufl_expr import TestFunction +from firedrake.ufl_expr import CellSize, TestFunction from firedrake.utility_meshes import RectangleMesh from goalie.math import bessk0 @@ -65,7 +65,7 @@ def solver(i): u_x = Function(R).assign(1.0) u_y = Function(R).assign(0.0) u = ufl.as_vector([u_x, u_y]) - h = ufl.CellSize(self[i]) + h = CellSize(self[i]) S = source(self[i]) # Stabilisation parameter diff --git a/test/adjoint/examples/point_discharge3d.py b/test/adjoint/examples/point_discharge3d.py index 2fb07798..cb00e8c6 100644 --- a/test/adjoint/examples/point_discharge3d.py +++ b/test/adjoint/examples/point_discharge3d.py @@ -21,7 +21,7 @@ from firedrake.function import Function from firedrake.functionspace import FunctionSpace from firedrake.solving import solve -from firedrake.ufl_expr import TestFunction +from firedrake.ufl_expr import CellSize, TestFunction from firedrake.utility_meshes import BoxMesh from goalie.math import bessk0 @@ -70,7 +70,7 @@ def solver(i): u_y = Function(R).assign(0.0) u_z = Function(R).assign(0.0) u = ufl.as_vector([u_x, u_y, u_z]) - h = ufl.CellSize(self[i]) + h = CellSize(self[i]) S = source(self[i]) # Stabilisation parameter diff --git a/test/adjoint/test_adjoint.py b/test/adjoint/test_adjoint.py index 40377ebe..0fb81ea9 100644 --- a/test/adjoint/test_adjoint.py +++ b/test/adjoint/test_adjoint.py @@ -12,12 +12,13 @@ import pytest from animate.utility import errornorm, norm from firedrake.cofunction import Cofunction +from firedrake.output.vtk_output import VTKFile from firedrake.utility_meshes import UnitTriangleMesh from goalie.adjoint import AdjointMeshSeq from goalie.log import DEBUG, pyrint, set_log_level from goalie.time_partition import TimeInterval, TimePartition -from goalie.utility import AttrDict, VTKFile +from goalie.utility import AttrDict sys.path.append(os.path.join(os.path.dirname(__file__), "examples")) diff --git a/test/test_function_data.py b/test/test_function_data.py index 87576c55..b969cb86 100644 --- a/test/test_function_data.py +++ b/test/test_function_data.py @@ -9,7 +9,7 @@ from firedrake.function import Function from firedrake.functionspace import FunctionSpace -from firedrake.mesh import MeshHierarchy +from firedrake.mg.mesh import MeshHierarchy from firedrake.utility_meshes import UnitTriangleMesh from goalie.function_data import AdjointSolutionData, ForwardSolutionData, IndicatorData diff --git a/test/test_utility.py b/test/test_utility.py index 29c2d762..b02a1963 100644 --- a/test/test_utility.py +++ b/test/test_utility.py @@ -11,8 +11,7 @@ from firedrake.functionspace import FunctionSpace from utility import uniform_mesh -from goalie.error_estimation import effectivity_index -from goalie.utility import create_directory +from goalie.utility import create_directory, effectivity_index pointwise_norm_types = [["l1"], ["l2"], ["linf"]] integral_scalar_norm_types = [["L1"], ["L2"], ["L4"], ["H1"], ["HCurl"]] From 4c42130634310ee6a3ec9cb1b6500a59c6802706 Mon Sep 17 00:00:00 2001 From: Joe Wallwork <22053413+jwallwork23@users.noreply.github.com> Date: Tue, 24 Dec 2024 12:09:56 +0000 Subject: [PATCH 10/11] Avoid polluting Goalie namespace (#271) --- demos/burgers-hessian.py | 1 + demos/burgers_time_integrated.py | 56 ++++++++++++++------------------ goalie/__init__.py | 7 ---- goalie/utility.py | 2 ++ 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/demos/burgers-hessian.py b/demos/burgers-hessian.py index 13429722..2f170064 100644 --- a/demos/burgers-hessian.py +++ b/demos/burgers-hessian.py @@ -13,6 +13,7 @@ from animate.adapt import adapt from animate.metric import RiemannianMetric from firedrake import * +from firedrake.__future__ import interpolate from goalie import * diff --git a/demos/burgers_time_integrated.py b/demos/burgers_time_integrated.py index cedd8c77..da8e0b0a 100644 --- a/demos/burgers_time_integrated.py +++ b/demos/burgers_time_integrated.py @@ -1,19 +1,20 @@ # Adjoint Burgers equation with a time integrated QoI # ====================================================== # -# So far, we only considered a quantity of interest -# corresponding to a spatial integral at the end time. -# For some problems, it is more suitable to have a QoI -# which integrates in time as well as space. +# So far, we only considered a quantity of interest corresponding to a spatial integral +# at the end time. For some problems, it is more suitable to have a QoI which integrates +# in time as well as space. # -# Begin by importing from Goalie and the first Burgers demo. :: +# Begin by importing from Firedrake and Goalie. Note that we use the *future* version +# Firedrake's `interpolate` function: :function:`firedrake.__future__.interpolate`. :: from firedrake import * +from firedrake.__future__ import interpolate from goalie_adjoint import * -# Redefine the ``get_initial_condition`` and ``get_function_spaces``, -# functions as in the first Burgers demo. :: +# Redefine the ``get_initial_condition`` and ``get_function_spaces``, functions as in +# the first Burgers demo. :: def get_function_spaces(mesh): @@ -26,14 +27,12 @@ def get_initial_condition(mesh_seq): return {"u": assemble(interpolate(as_vector([sin(pi * x), 0]), fs))} -# The solver needs to be modified slightly in order to take -# account of time dependent QoIs. The Burgers solver -# uses backward Euler timestepping. The corresponding -# quadrature routine is like the midpoint rule, but takes -# the value from the next timestep, rather than the average -# between that and the current value. As such, the QoI may -# be computed by simply incrementing the :attr:`J` attribute -# of the :class:`AdjointMeshSeq` as follows. :: +# The solver needs to be modified slightly in order to take account of time dependent +# QoIs. The Burgers solver uses backward Euler timestepping. The corresponding +# quadrature routine is like the midpoint rule, but takes the value from the next +# timestep, rather than the average between that and the current value. As such, the QoI +# may be computed by simply incrementing the :attr:`J` attribute of the +# :class:`AdjointMeshSeq` as follows. :: def get_solver(mesh_seq): @@ -69,17 +68,15 @@ def solver(index): return solver -# The QoI is effectively just a time-integrated version -# of the one previously seen. +# The QoI is effectively just a time-integrated version of the one previously seen. # # .. math:: # J(u) = \int_0^{T_{\mathrm{end}}} \int_0^1 # \mathbf u(1,y,t) \cdot \mathbf u(1,y,t) # \;\mathrm dy\;\mathrm dt. # -# Note that in this case we multiply by the timestep. -# It is wrapped in a :class:`Function` from `'R'` space to avoid -# recompilation if the value is changed. :: +# Note that in this case we multiply by the timestep. It is wrapped in a +# :class:`Function` from `'R'` space to avoid recompilation if the value is changed. :: def get_qoi(mesh_seq, i): @@ -106,9 +103,8 @@ def time_integrated_qoi(t): end_time, num_subintervals, dt, ["u"], num_timesteps_per_export=1 ) -# The only difference when defining the :class:`AdjointMeshSeq` -# is that we specify ``qoi_type="time_integrated"``, rather than -# ``qoi_type="end_time"``. :: +# The only difference when defining the :class:`AdjointMeshSeq` is that we specify +# ``qoi_type="time_integrated"``, rather than ``qoi_type="end_time"``. :: mesh_seq = AdjointMeshSeq( time_partition, @@ -128,14 +124,12 @@ def time_integrated_qoi(t): # :figwidth: 90% # :align: center # -# With a time-integrated QoI, the adjoint problem -# has a source term at the right-hand boundary, rather -# than a instantaneous pulse at the terminal time. As such, -# the adjoint solution field accumulates at the right-hand -# boundary, as well as propagating westwards. +# With a time-integrated QoI, the adjoint problem has a source term at the right-hand +# boundary, rather than a instantaneous pulse at the terminal time. As such, the adjoint +# solution field accumulates at the right-hand boundary, as well as propagating +# westwards. # -# In the `next demo <./burgers_oo.py.html>`__, we solve -# the Burgers problem one last time, but using an -# object-oriented approach. +# In the `next demo <./burgers_oo.py.html>`__, we solve the Burgers problem one last +# time, but using an object-oriented approach. # # This demo can also be accessed as a `Python script `__. diff --git a/goalie/__init__.py b/goalie/__init__.py index afa8221a..af7db6e4 100644 --- a/goalie/__init__.py +++ b/goalie/__init__.py @@ -10,11 +10,4 @@ from goalie.function_data import * # noqa from goalie.error_estimation import * # noqa -from animate.utility import Mesh, VTKFile # noqa - -from firedrake.__future__ import interpolate # noqa - -import numpy as np # noqa -import os # noqa - __version__ = "0.1" diff --git a/goalie/utility.py b/goalie/utility.py index 4971f8a6..b3d3626b 100644 --- a/goalie/utility.py +++ b/goalie/utility.py @@ -7,6 +7,8 @@ import firedrake import numpy as np +__all__ = ["AttrDict", "create_directory", "effectivity_index"] + class AttrDict(dict): """ From 9036b4551e04e5fac1cd1955484d435249659430 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 24 Dec 2024 15:21:57 +0000 Subject: [PATCH 11/11] Fix lint after update --- goalie/go_mesh_seq.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/goalie/go_mesh_seq.py b/goalie/go_mesh_seq.py index 9028364f..4caa5592 100644 --- a/goalie/go_mesh_seq.py +++ b/goalie/go_mesh_seq.py @@ -242,7 +242,8 @@ def indicate_errors( # Initialise adjoint solver generators on the MeshSeq and its enriched version adj_sol_gen = self._solve_adjoint(**solver_kwargs) - # Track form coefficient changes in the enriched problem if the problem is unsteady + # Track form coefficient changes in the enriched problem if the problem is + # unsteady adj_sol_gen_enriched = enriched_mesh_seq._solve_adjoint( track_coefficients=not self.steady, **solver_kwargs,