# Source code for pde.pdes.laplace

"""
Solvers for Poisson's and Laplace's equation

.. codeauthor:: David Zwicker <david.zwicker@ds.mpg.de>
"""

from ..fields import ScalarField
from ..grids.base import GridBase
from ..grids.boundaries.axes import BoundariesData  # @UnusedImport
from ..tools.docstrings import fill_in_docstring

[docs]@fill_in_docstring
def solve_poisson_equation(
rhs: ScalarField,
bc: "BoundariesData",
label: str = "Solution to Poisson's equation",
**kwargs,
) -> ScalarField:
r"""Solve Laplace's equation on a given grid

Denoting the current field by :math:u, we thus solve for :math:f,
defined by the equation

.. math::
\nabla^2 u(\boldsymbol r) = -f(\boldsymbol r)

with boundary conditions specified by bc.

Note:
In case of periodic or Neumann boundary conditions, the right hand
side :math:f(\boldsymbol r) needs to satisfy the following
condition

.. math::
\int f \, \mathrm{d}V = \oint g \, \mathrm{d}S \;,

where :math:g denotes the function specifying the outwards
derivative for Neumann conditions. Note that for periodic boundaries
:math:g vanishes, so that this condition implies that the integral
over
:math:f must vanish for neutral Neumann or periodic conditions.

Args:
rhs (:class:~pde.fields.scalar.ScalarField):
The scalar field :math:f describing the right hand side
bc:
The boundary conditions applied to the field.
{ARG_BOUNDARIES}
label (str):
The label of the returned field.

Returns:
:class:~pde.fields.scalar.ScalarField: The field :math:u that solves
the equation. This field will be defined on the same grid as rhs.
"""
# get the operator information
operator = rhs.grid._get_operator_info("poisson_solver")
# get the boundary conditions
bcs = rhs.grid.get_boundary_conditions(bc)
# get the actual solver
solver = operator.factory(bcs=bcs, **kwargs)

# solve the poisson problem
result = ScalarField(rhs.grid, label=label)
try:
solver(rhs.data, result.data)
except RuntimeError:
magnitude = rhs.magnitude
if magnitude > 1e-10:
raise RuntimeError(
"Could not solve the Poisson problem. One possible reason for this is "
"that only periodic or Neumann conditions are applied although the "
f"magnitude of the field is {magnitude} and thus non-zero."
)
else:
raise  # another error occurred

return result

[docs]@fill_in_docstring
def solve_laplace_equation(
grid: GridBase, bc: "BoundariesData", label: str = "Solution to Laplace's equation"
) -> ScalarField:
"""Solve Laplace's equation on a given grid.

This is implemented by calling :func:solve_poisson_equation with a
vanishing right hand side.

Args:
grid (:class:~pde.grids.base.GridBase):
The grid on which the equation is solved
bc:
The boundary conditions applied to the field.
{ARG_BOUNDARIES}
label (str):
The label of the returned field.

Returns:
:class:~pde.fields.scalar.ScalarField: The field that solves the
equation. This field will be defined on the given grid.
"""
rhs = ScalarField(grid, data=0)
return solve_poisson_equation(rhs, bc=bc, label=label)