Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop lint ignores #126

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions demos/laplacian_smoothing.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
# -\Delta_{\boldsymbol{\xi}}\mathbf{v} = \boldsymbol{0},
#
# where :math:`\mathbf{v}` is the so-called *mesh velocity* that we solve for. Note that
# the derivatives in the Laplace equation are in terms of the *computational coordinates*
# :math:`\boldsymbol{\xi}`, rather than the physical coordinates :math:`\mathbf{x}`.
# the derivatives in the Laplace equation are in terms of the *computational
# coordinates* :math:`\boldsymbol{\xi}`, rather than the physical coordinates
# :math:`\mathbf{x}`.
#
# With the mesh velocity, we update the physical coordinates according to
#
Expand Down Expand Up @@ -64,7 +65,8 @@
# on the top boundary and see how the mesh responds. Consider the velocity
#
# .. math::
# \mathbf{v}_f(x,y,t) = \left[0, A\:\sin\left(\frac{2\pi t}T\right)\:\sin(\pi x)\right]
# \mathbf{v}_f(x,y,t) = \left[0, A
# \:\sin\left(\frac{2\pi t}T\right)\:\sin(\pi x)\right]
#
# acting only in the vertical direction, where :math:`A` is the amplitude and :math:`T`
# is the time period. The displacement follows a sinusoidal pattern along that
Expand Down Expand Up @@ -103,9 +105,10 @@ def boundary_velocity(x, t):
#
# To enforce this boundary velocity, we need to create a :class:`~.LaplacianSmoother`
# instance and define a function for updating the boundary conditions. Since we are
# going to enforce the velocity on the top boundary, we create a :class:`~firedrake.function.Function` to
# represent the boundary condition values and pass this to a :class:`~firedrake.bcs.DirichletBC`
# object. We then define a function which updates it as time progresses. ::
# going to enforce the velocity on the top boundary, we create a
# :class:`~firedrake.function.Function` to represent the boundary condition values and
# pass this to a :class:`~firedrake.bcs.DirichletBC` object. We then define a function
# which updates it as time progresses. ::

mover = LaplacianSmoother(mesh, timestep)
top = Function(mover.coord_space)
Expand All @@ -120,7 +123,8 @@ def update_boundary_velocity(t):
bv_data[i][1] = boundary_velocity(x, t)


# In addition to the moving boundary, we specify the remaining boundaries to be fixed. ::
# In addition to the moving boundary, we specify the remaining boundaries to be fixed.
# ::

fixed_boundaries = DirichletBC(mover.coord_space, 0, [1, 2, 3])
boundary_conditions = (fixed_boundaries, moving_boundary)
Expand Down
30 changes: 16 additions & 14 deletions demos/lineal_spring.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
# .. math::
# \underline{\mathbf{K}} \mathbf{u} = \boldsymbol{0},
#
# where :math:`\mathbf{u}\in\mathbb{R}^{2N}` is a 'flattened' version of the displacement
# vector. By solving this equation, we see how the structure of beams responds to the
# forced boundary displacement.
# where :math:`\mathbf{u}\in\mathbb{R}^{2N}` is a 'flattened' version of the
# displacement vector. By solving this equation, we see how the structure of beams
# responds to the forced boundary displacement.
#
# There are two main differences to note with the Laplacian smoothing approach. The first
# is that Laplacian smoothing is formulated in terms of *mesh velocity*, whereas this
# method is formulated in terms of displacements. Secondly, the mesh velocity
# There are two main differences to note with the Laplacian smoothing approach. The
# first is that Laplacian smoothing is formulated in terms of *mesh velocity*, whereas
# this method is formulated in terms of displacements. Secondly, the mesh velocity
# :math:`\mathbf{v}` in the Laplacian smoothing method may be approximated at timestep
# :math:`n` as
#
Expand Down Expand Up @@ -86,7 +86,8 @@
# structure responds. We use a very similar formula,
#
# .. math::
# \mathbf{u}_f(x,y,t)=\left[0, A\:\sin\left(\frac{2\pi t}T\right)\:\sin(\pi x)\right],
# \mathbf{u}_f(x,y,t)=\left[0,
# A\:\sin\left(\frac{2\pi t}T\right)\:\sin(\pi x)\right],
#
# where :math:`A` is the amplitude and :math:`T` is the time period, but again note that
# it is now expressed as a boundary *displacement* :math:`\mathbf{u}_f`:, rather than a
Expand Down Expand Up @@ -139,7 +140,8 @@ def update_boundary_displacement(t):
bd_data[i][1] = boundary_displacement(x, t)


# In addition to the moving boundary, we specify the remaining boundaries to be fixed. ::
# In addition to the moving boundary, we specify the remaining boundaries to be fixed.
# ::

fixed_boundaries = DirichletBC(mover.coord_space, 0, [1, 2, 3])
boundary_conditions = (fixed_boundaries, moving_boundary)
Expand Down Expand Up @@ -187,9 +189,9 @@ def update_boundary_displacement(t):
# :figwidth: 100%
# :align: center
#
# Again, the mesh is deformed according to the vertical displacement on the top boundary,
# with the left, right, and bottom boundaries remaining fixed, returning to be very
# close to its original state after one period. Let's check this in the
# Again, the mesh is deformed according to the vertical displacement on the top
# boundary, with the left, right, and bottom boundaries remaining fixed, returning to
# be very close to its original state after one period. Let's check this in the
# :math:`\ell_\infty` norm. ::

coord_data = mover.mesh.coordinates.dat.data
Expand All @@ -201,9 +203,9 @@ def update_boundary_displacement(t):
#
# l_infinity error: 0.001 m
#
# Note that the mesh doesn't return to its original state quite as neatly with the lineal
# spring method as it does with the Laplacian smoothing method. However, the result is
# still very good (as can be seen from the plots above).
# Note that the mesh doesn't return to its original state quite as neatly with the
# lineal spring method as it does with the Laplacian smoothing method. However, the
# result is still very good (as can be seen from the plots above).
#
# We can view the sparsity pattern of the stiffness matrix as follows. ::

Expand Down
11 changes: 7 additions & 4 deletions demos/monge_ampere1.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
# such that it is the solution of the Monge-Ampère type equation,
#
# .. math::
# m(\mathbf{x}) \det(\underline{\mathbf{I}}+\nabla_{\boldsymbol{\xi}}\nabla_{\boldsymbol{\xi}}\phi) = \theta,
# m(\mathbf{x}) \det(\underline{\mathbf{I}}
# +\nabla_{\boldsymbol{\xi}}\nabla_{\boldsymbol{\xi}}\phi) = \theta,
#
# where :math:`m(\mathbf{x})` is a so-called *monitor function* and :math:`\theta` is a
# strictly positive normalisation function. The monitor function is of key importance
Expand Down Expand Up @@ -67,7 +68,8 @@
# the domain, according to the formula,
#
# .. math::
# m(x,y) = 1 + \frac{\alpha}{\cosh^2\left(\beta\left(\left(x-\frac{1}{2}\right)^2+\left(y-\frac{1}{2}\right)^2-\gamma\right)\right)},
# m(x,y) = 1 + \frac{\alpha}{\cosh^2\left(\beta\left(
# \left(x-\frac{1}{2}\right)^2+\left(y-\frac{1}{2}\right)^2-\gamma\right)\right)},
#
# for some values of the parameters :math:`\alpha`, :math:`\beta`, and :math:`\gamma`.
# Unity is added at the start to ensure that the monitor function doesn't get too
Expand All @@ -88,8 +90,9 @@ def ring_monitor(mesh):


# With an initial mesh and a monitor function, we are able to construct a
# :class:`~movement.monge_ampere.MongeAmpereMover` instance and adapt the mesh. By default, the Monge-Ampère
# equation is solved to a relative tolerance of :math:`10^{-8}`. ::
# :class:`~movement.monge_ampere.MongeAmpereMover` instance and adapt the mesh. By
# default, the Monge-Ampère equation is solved to a relative tolerance of
# :math:`10^{-8}`. ::

rtol = 1.0e-08
mover = MongeAmpereMover(mesh, ring_monitor, method="quasi_newton", rtol=rtol)
Expand Down
10 changes: 5 additions & 5 deletions demos/monge_ampere_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ def sinatan3(mesh):
#
# m = 1 + \alpha \frac{H(u_h):H(u_h)}{\max_{{\bf x}\in\Omega} H(u_h):H(u_h)}
#
# where :math:`:` indicates the inner product, i.e. :math:`\sqrt{H:H}` is the Frobenius norm
# of :math:`H`. We have normalised such that the minimum of the monitor function is one (where
# the error is zero), and its maximum is :math:`1 + \alpha` (where the curvature is maximal). This
# means that we can select the ratio between the largest and smallest cell volume in the
# moved mesh as :math:`1+\alpha`.
# where :math:`:` indicates the inner product, i.e. :math:`\sqrt{H:H}` is the Frobenius
# norm of :math:`H`. We have normalised such that the minimum of the monitor function
# is one (where the error is zero), and its maximum is :math:`1 + \alpha` (where the
# curvature is maximal). This means that we can select the ratio between the largest and
# smallest cell volume in the moved mesh as :math:`1+\alpha`.
#
# As in the `previous Monge-Ampère demo <./monge_ampere1.py.html>`__, we use the
# :class:`~movement.monge_ampere.MongeAmpereMover` to perform the mesh movement based on
Expand Down
12 changes: 7 additions & 5 deletions demos/monge_ampere_helmholtz.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
#
# Based on the code in the Firedrake demo, we first solve the PDE on a uniform mesh.
# Because our chosen solution does not satisfy homogeneous Neumann boundary conditions,
# we instead apply Dirichlet boundary conditions based on the chosen analytical solution.
# we instead apply Dirichlet boundary conditions based on the chosen analytical
# solution.

from firedrake import *

Expand Down Expand Up @@ -102,10 +103,11 @@ def solve_helmholtz(mesh):
# this numerical error. We use the same monitor function as
# in the `previous Monge-Ampère demo <./monge_ampere_3d.py.html>`__
# based on the norm of the Hessian of the solution.
# In the following implementation we use the exact solution :math:`u_{\text{exact}}` which we
# have as a symbolic UFL expression, and thus we can also obtain the Hessian symbolically as
# :code:`grad(grad(u_exact))`. To compute its maximum norm however we do interpolate it
# to a P1 function space `V` and take the maximum of the array of nodal values.
# In the following implementation we use the exact solution :math:`u_{\text{exact}}`
# which we have as a symbolic UFL expression, and thus we can also obtain the Hessian
# symbolically as :code:`grad(grad(u_exact))`. To compute its maximum norm however we
# do interpolate it to a P1 function space `V` and take the maximum of the array of
# nodal values.

alpha = Constant(5.0)

Expand Down
6 changes: 4 additions & 2 deletions movement/laplacian_smoothing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ class LaplacianSmoother(PrimeMover):
determined by solving a vector Laplace problem

.. math::
\nabla^2_{\boldsymbol{\xi}}\mathbf{v} = \boldsymbol{0}, \quad \boldsymbol{\xi}\in\Omega,
\nabla^2_{\boldsymbol{\xi}}\mathbf{v} = \boldsymbol{0},
\quad \boldsymbol{\xi}\in\Omega,

under non-zero Dirichlet boundary conditions on a forced boundary section
:math:`\partial\Omega_f` and zero Dirichlet boundary conditions elsewhere:

.. math::
\mathbf{v} = \left\{\begin{array}{rl}
\mathbf{v}_D, & \boldsymbol{\xi}\in\partial\Omega_f\\
\boldsymbol{0}, & \boldsymbol{\xi}\in\partial\Omega\backslash\partial\Omega_f
\boldsymbol{0}, & \boldsymbol{\xi}\in
\partial\Omega\backslash\partial\Omega_f
\end{array}\right.

where the computational coordinates :math:`\boldsymbol{\xi} := \mathbf{x}(t_0)` are
Expand Down
76 changes: 38 additions & 38 deletions movement/monge_ampere.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ def MongeAmpereMover(mesh, monitor_function, method="relaxation", **kwargs):
m(\mathbf{x})\det(\mathbf{I} + \mathbf{H}(\phi)) = \theta,

for a convex scalar potential :math:`\phi=\phi(\boldsymbol{\xi})`, which is a
function of the coordinates of the *computational* mesh. Here :math:`m=m(\mathbf{x})`
is a function of the coordinates of the *physical* mesh, :math:`\mathbf{I}` is the
identity matrix, :math:`\theta` is a normalisation coefficient and
:math:`\mathbf{H}(\phi)` denotes the Hessian of :math:`\phi` with respect to
:math:`\boldsymbol{\xi}`.
function of the coordinates of the *computational* mesh. Here
:math:`m=m(\mathbf{x})` is a function of the coordinates of the *physical* mesh,
:math:`\mathbf{I}` is the identity matrix, :math:`\theta` is a normalisation
coefficient and :math:`\mathbf{H}(\phi)` denotes the Hessian of :math:`\phi` with
respect to :math:`\boldsymbol{\xi}`.

Different implementations solve the Monge-Ampère equation in different ways. If the
`method` argument is set to `"relaxation"` then it is solved in parabolised form in
:class:`~.MongeAmpereMover_Relaxation`. If the argument is set to `"quasi_newton"`
then it is solved in its elliptic form using a quasi-Newton method in
:class:`~.MongeAmpereMover_QuasiNewton`. Descriptions of both methods may be found in
:cite:`MCB:18`.
:class:`~.MongeAmpereMover_QuasiNewton`. Descriptions of both methods may be found
in :cite:`MCB:18`.

The physical mesh coordinates :math:`\mathbf{x}` are updated according to

Expand All @@ -71,9 +71,9 @@ def MongeAmpereMover(mesh, monitor_function, method="relaxation", **kwargs):
:type dtol: :class:`float`
:kwarg pseudo_timestep: pseudo-timestep (only relevant to relaxation method)
:type pseudo_timestep: :class:`float`
:kwarg fixed_boundary_segments: labels corresponding to boundary segments to be fixed
with a zero Dirichlet condition. The 'on_boundary' label indicates the whole
domain boundary
:kwarg fixed_boundary_segments: labels corresponding to boundary segments to be
fixed with a zero Dirichlet condition. The 'on_boundary' label indicates the
whole domain boundary
:type fixed_boundary_segments: :class:`list` of :class:`str` or :class:`int`
:return: the Monge-Ampère Mover object
:rtype: :class:`MongeAmpereMover_Relaxation` or
Expand Down Expand Up @@ -108,8 +108,8 @@ class MongeAmpereMover_Base(PrimeMover, metaclass=abc.ABCMeta):
Base class for mesh movers based on the solution of Monge-Ampère type equations.

Currently implemented subclasses: :class:`~.MongeAmpereMover_Relaxation` and
:class:`~.MongeAmpereMover_QuasiNewton`. Descriptions of both methods may be found in
:cite:`MCB:18`.
:class:`~.MongeAmpereMover_QuasiNewton`. Descriptions of both methods may be found
in :cite:`MCB:18`.
"""

def __init__(self, mesh, monitor_function, **kwargs):
Expand Down Expand Up @@ -191,8 +191,8 @@ def apply_initial_guess(self, phi_init, H_init):
Initialise the approximations to the scalar potential and its Hessian with an
initial guess.

By default, both are initialised to zero, which corresponds to the case where the
computational and physical meshes coincide.
By default, both are initialised to zero, which corresponds to the case where
the computational and physical meshes coincide.

:arg phi_init: initial guess for the scalar potential
:type phi_init: :class:`firedrake.function.Function`
Expand Down Expand Up @@ -232,8 +232,8 @@ def _update_physical_coordinates(self):
.. math::
\mathbf{x} = \boldsymbol{\xi} + \nabla_{\boldsymbol{\xi}}\phi.

After updating the coordinates, this method also checks for mesh tangling if this
is turned on. (It will be turned on by default in the 2D case.)
After updating the coordinates, this method also checks for mesh tangling if
this is turned on. (It will be turned on by default in the 2D case.)
"""
try:
self.grad_phi.assign(self._grad_phi)
Expand Down Expand Up @@ -352,14 +352,14 @@ class MongeAmpereMover_Relaxation(MongeAmpereMover_Base):
m(\mathbf{x})\det(\mathbf{I} + \mathbf{H}(\phi)) = \theta,

for a convex scalar potential :math:`\phi=\phi(\boldsymbol{\xi})`, which is a
function of the coordinates of the *computational* mesh. Here :math:`m=m(\mathbf{x})`
is a user-provided monitor function, which is a function of the coordinates of the
*physical* mesh. :math:`\mathbf{I}` is the identity matrix, :math:`\theta` is a
normalisation coefficient and :math:`\mathbf{H}(\phi)` denotes the Hessian of
:math:`\phi` with respect to :math:`\boldsymbol{\xi}`.
function of the coordinates of the *computational* mesh. Here
:math:`m=m(\mathbf{x})` is a user-provided monitor function, which is a function of
the coordinates of the *physical* mesh. :math:`\mathbf{I}` is the identity matrix,
:math:`\theta` is a normalisation coefficient and :math:`\mathbf{H}(\phi)` denotes
the Hessian of :math:`\phi` with respect to :math:`\boldsymbol{\xi}`.

In this mesh mover, the Monge-Ampère equation is instead solved in a parabolised form
using a pseudo-time relaxation,
In this mesh mover, the Monge-Ampère equation is instead solved in a parabolised
form using a pseudo-time relaxation,

.. math::
-\frac\partial{\partial\tau}\Delta\phi
Expand Down Expand Up @@ -404,9 +404,9 @@ def __init__(self, mesh, monitor_function, phi_init=None, H_init=None, **kwargs)
self.apply_initial_guess(phi_init, H_init)

# Setup residuals
I = ufl.Identity(self.dim)
self.theta_form = self.monitor * ufl.det(I + self.H_old) * self.dx
self.residual = self.monitor * ufl.det(I + self.H_old) - self.theta
id = ufl.Identity(self.dim)
self.theta_form = self.monitor * ufl.det(id + self.H_old) * self.dx
self.residual = self.monitor * ufl.det(id + self.H_old) - self.theta
psi = firedrake.TestFunction(self.P1)
self._residual_l2_form = psi * self.residual * self.dx
self._norm_l2_form = psi * self.theta * self.dx
Expand Down Expand Up @@ -548,14 +548,14 @@ class MongeAmpereMover_QuasiNewton(MongeAmpereMover_Base):
m(\mathbf{x})\det(\mathbf{I} + \mathbf{H}(\phi)) = \theta,

for a convex scalar potential :math:`\phi=\phi(\boldsymbol{\xi})`, which is a
function of the coordinates of the *computational* mesh. Here :math:`m=m(\mathbf{x})`
is a user-provided monitor function, which is a function of the coordinates of the
*physical* mesh. :math:`\mathbf{I}` is the identity matrix, :math:`\theta` is a
normalisation coefficient and :math:`\mathbf{H}(\phi)` denotes the Hessian of
:math:`\phi` with respect to :math:`\boldsymbol{\xi}`.
function of the coordinates of the *computational* mesh. Here
:math:`m=m(\mathbf{x})` is a user-provided monitor function, which is a function of
the coordinates of the *physical* mesh. :math:`\mathbf{I}` is the identity matrix,
:math:`\theta` is a normalisation coefficient and :math:`\mathbf{H}(\phi)` denotes
the Hessian of :math:`\phi` with respect to :math:`\boldsymbol{\xi}`.

In this mesh mover, the elliptic Monge-Ampère equation is solved using a quasi-Newton
method (see :cite:`MCB:18` for details).
In this mesh mover, the elliptic Monge-Ampère equation is solved using a
quasi-Newton method (see :cite:`MCB:18` for details).

This approach typically takes fewer than ten iterations to converge, but each
iteration is relatively expensive.
Expand Down Expand Up @@ -586,9 +586,9 @@ def __init__(self, mesh, monitor_function, phi_init=None, H_init=None, **kwargs)
self.apply_initial_guess(phi_init, H_init)

# Setup residuals
I = ufl.Identity(self.dim)
self.theta_form = self.monitor * ufl.det(I + self.H_old) * self.dx
self.residual = self.monitor * ufl.det(I + self.H_old) - self.theta
id = ufl.Identity(self.dim)
self.theta_form = self.monitor * ufl.det(id + self.H_old) * self.dx
self.residual = self.monitor * ufl.det(id + self.H_old) - self.theta
psi = firedrake.TestFunction(self.P1)
self._residual_l2_form = psi * self.residual * self.dx
self._norm_l2_form = psi * self.theta * self.dx
Expand Down Expand Up @@ -630,14 +630,14 @@ def equidistributor(self):
if hasattr(self, "_equidistributor"):
return self._equidistributor
n = ufl.FacetNormal(self.mesh)
I = ufl.Identity(self.dim)
id = ufl.Identity(self.dim)
phi, H = firedrake.split(self.phi_and_hessian)
psi, tau = firedrake.TestFunctions(self.V)
F = (
ufl.inner(tau, H) * self.dx
+ ufl.dot(ufl.div(tau), ufl.grad(phi)) * self.dx
- ufl.dot(ufl.dot(tangential(ufl.grad(phi), n), tau), n) * self.ds
- psi * (self.monitor * ufl.det(I + H) - self.theta) * self.dx
- psi * (self.monitor * ufl.det(id + H) - self.theta) * self.dx
)
phi, H = firedrake.TrialFunctions(self.V)

Expand Down
Loading
Loading