예제 #1
0
def get_performance_data(periodic=False):
    """obtain the data used in the performance plot

    Args:
        periodic (bool): The boundary conditions of the underlying grid

    Returns:
        dict: The durations of calculating the Laplacian on different grids
        using different methods
    """
    sizes = 2**np.arange(3, 13)

    statistics = {}
    for size in display_progress(sizes):
        data = {}
        grid = UnitGrid([size] * 2, periodic=periodic)
        field = ScalarField.random_normal(grid)
        field.set_ghost_cells(bc="auto_periodic_neumann")

        for backend in ["numba", "scipy"]:
            op = grid.make_operator("laplace",
                                    bc="auto_periodic_neumann",
                                    backend=backend)
            data[backend] = time_function(op, field.data)

        op = grid.make_operator_no_bc("laplace", backend="numba")
        data["numba_no_bc"] = time_function(op, field._data_full, use_out=True)

        if opencv_laplace:
            data["opencv"] = time_function(opencv_laplace, field.data)

        statistics[int(size)] = data

    return statistics
예제 #2
0
def main():
    """ main routine testing the performance """
    print("Reports calls-per-second (larger is better)\n")

    # Cartesian grid with different shapes and boundary conditions
    for size in [32, 512]:
        grid = UnitGrid((size, size), periodic=False)
        print(grid)

        field = ScalarField.random_normal(grid)
        bc_value = np.ones(size)
        result = field.laplace(bc={"value": 1}).data

        for bc in ["scalar", "array", "linked"]:
            if bc == "scalar":
                bcs = {"value": 1}
            elif bc == "array":
                bcs = {"value": bc_value}
            elif bc == "linked":
                bcs = Boundaries.from_data(grid, {"value": bc_value}, rank=0)
                for ax, upper in grid._iter_boundaries():
                    bcs[ax][upper].link_value(bc_value)
            # result = field.laplace(bc=bcs).data
            laplace = grid.get_operator("laplace", bc=bcs)
            # call once to pre-compile and test result
            np.testing.assert_allclose(laplace(field.data), result)
            speed = estimate_computation_speed(laplace, field.data)
            print(f"{bc:>6s}:{int(speed):>9d}")

        print()
예제 #3
0
def test_pde_bcs_error(bc):
    """test PDE with wrong boundary conditions"""
    eq = PDE({"u": "laplace(u)"}, bc=bc)
    grid = grids.UnitGrid([8, 8])
    field = ScalarField.random_normal(grid)

    for backend in ["numpy", "numba"]:
        with pytest.raises(BCDataError):
            eq.solve(field, t_range=1, dt=0.01, backend=backend, tracker=None)
예제 #4
0
def test_pde_scalar():
    """test PDE with a single scalar field"""
    eq = PDE({"u": "laplace(u) + exp(-t) + sin(t)"})
    assert eq.explicit_time_dependence
    assert not eq.complex_valued
    grid = grids.UnitGrid([8])
    field = ScalarField.random_normal(grid)

    res_a = eq.solve(field, t_range=1, dt=0.01, backend="numpy", tracker=None)
    res_b = eq.solve(field, t_range=1, dt=0.01, backend="numba", tracker=None)

    res_a.assert_field_compatible(res_b)
    np.testing.assert_allclose(res_a.data, res_b.data)
예제 #5
0
def test_pde_bcs(bc):
    """test PDE with boundary conditions"""
    eq = PDE({"u": "laplace(u)"}, bc=bc)
    assert not eq.explicit_time_dependence
    assert not eq.complex_valued
    grid = grids.UnitGrid([8])
    field = ScalarField.random_normal(grid)

    res_a = eq.solve(field, t_range=1, dt=0.01, backend="numpy", tracker=None)
    res_b = eq.solve(field, t_range=1, dt=0.01, backend="numba", tracker=None)

    res_a.assert_field_compatible(res_b)
    np.testing.assert_allclose(res_a.data, res_b.data)
예제 #6
0
def main():
    """main routine testing the performance"""
    print("Reports calls-per-second (larger is better)\n")

    # Cartesian grid with different shapes and boundary conditions
    for size in [32, 512]:
        grid = UnitGrid([size, size], periodic=False)
        print(grid)

        field = ScalarField.random_normal(grid)
        bc_value = np.ones(size)
        result = field.laplace(bc={"value": 1}).data

        for bc in ["scalar", "array", "function", "time-dependent", "linked"]:
            if bc == "scalar":
                bcs = {"value": 1}
            elif bc == "array":
                bcs = {"value": bc_value}
            elif bc == "function":
                bcs = grid.get_boundary_conditions(
                    {"virtual_point": "2 - value"})
            elif bc == "time-dependent":
                bcs = grid.get_boundary_conditions({"value_expression": "t"})
            elif bc == "linked":
                bcs = grid.get_boundary_conditions({"value": bc_value})
                for ax, upper in grid._iter_boundaries():
                    bcs[ax][upper].link_value(bc_value)
            else:
                raise RuntimeError

            # create the operator with these conditions
            laplace = grid.make_operator("laplace", bc=bcs)
            if bc == "time-dependent":
                args = numba_dict({"t": 1})
                # call once to pre-compile and test result
                np.testing.assert_allclose(laplace(field.data, args=args),
                                           result)
                # estimate the speed
                speed = estimate_computation_speed(laplace,
                                                   field.data,
                                                   args=args)

            else:
                # call once to pre-compile and test result
                np.testing.assert_allclose(laplace(field.data), result)
                # estimate the speed
                speed = estimate_computation_speed(laplace, field.data)

            print(f"{bc:>14s}:{int(speed):>9d}")

        print()
예제 #7
0
def test_custom_operators():
    """ test using a custom operator """
    grid = grids.UnitGrid([32])
    field = ScalarField.random_normal(grid)
    eq = PDE({"u": "undefined(u)"})

    with pytest.raises(NameError):
        eq.evolution_rate(field)

    def make_op(state):
        return lambda state: state

    grids.UnitGrid.register_operator("undefined", make_op)

    eq._cache = {}  # reset cache
    res = eq.evolution_rate(field)
    np.testing.assert_allclose(field.data, res.data)

    del grids.UnitGrid._operators["undefined"]  # reset original state
"""
Visualizing a 3d field interactively
====================================

This example demonstrates how to display 3d data interactively using the
`napari <https://napari.org>`_ viewer.
"""

import numpy as np
from pde import CartesianGrid, ScalarField

# create a scalar field with some noise
grid = CartesianGrid([[0, 2 * np.pi]] * 3, 64)
data = ScalarField.from_expression(
    grid, "(cos(2 * x) * sin(3 * y) + cos(2 *  z))**2")
data += ScalarField.random_normal(grid, std=0.01)
data.label = "3D Field"

data.plot_interactive()
예제 #9
0
def main():
    """main routine testing the performance"""
    print("Reports calls-per-second (larger is better)")
    print("  The `CUSTOM` method implemented by hand is the baseline case.")
    print("  The `FLEXIBLE` method is a serial implementation using the "
          "boundary conditions from the package.")
    print("  The other methods use the functions supplied by the package.\n")

    # Cartesian grid with different shapes and boundary conditions
    for shape in [(32, 32), (512, 512)]:
        for periodic in [True, False]:
            grid = UnitGrid(shape, periodic=periodic)
            print(grid)
            field = ScalarField.random_normal(grid)
            bcs = grid.get_boundary_conditions("natural", rank=0)
            expected = field.laplace("natural")

            for method in [
                    "CUSTOM", "FLEXIBLE", "OPTIMIZED", "numba", "scipy"
            ]:
                if method == "CUSTOM":
                    laplace = custom_laplace_2d(shape, periodic=periodic)
                elif method == "FLEXIBLE":
                    laplace = flexible_laplace_2d(bcs)
                elif method == "OPTIMIZED":
                    laplace = optimized_laplace_2d(bcs)
                elif method in {"numba", "scipy"}:
                    laplace = grid.make_operator("laplace",
                                                 bc=bcs,
                                                 backend=method)
                else:
                    raise ValueError(f"Unknown method `{method}`")

                # call once to pre-compile and test result
                if method == "OPTIMIZED":
                    result = laplace(field._data_all)
                    np.testing.assert_allclose(result, expected.data)
                    speed = estimate_computation_speed(laplace,
                                                       field._data_all)
                else:
                    np.testing.assert_allclose(laplace(field.data),
                                               expected.data)
                    speed = estimate_computation_speed(laplace, field.data)
                print(f"{method:>9s}: {int(speed):>9d}")
            print()

    # Cylindrical grid with different shapes
    for shape in [(32, 64), (512, 512)]:
        grid = CylindricalSymGrid(shape[0], [0, shape[1]], shape)
        print(f"Cylindrical grid, shape={shape}")
        field = ScalarField.random_normal(grid)
        bcs = Boundaries.from_data(grid, "derivative")
        expected = field.laplace(bcs)

        for method in ["CUSTOM", "numba"]:
            if method == "CUSTOM":
                laplace = custom_laplace_cyl_neumann(shape)
            elif method == "numba":
                laplace = grid.make_operator("laplace", bc=bcs)
            else:
                raise ValueError(f"Unknown method `{method}`")
            # call once to pre-compile and test result
            np.testing.assert_allclose(laplace(field.data), expected.data)
            speed = estimate_computation_speed(laplace, field.data)
            print(f"{method:>8s}: {int(speed):>9d}")
        print()

    # Spherical grid with different shapes
    for shape in [32, 512]:
        grid = SphericalSymGrid(shape, shape)
        print(grid)
        field = ScalarField.random_normal(grid)
        bcs = Boundaries.from_data(grid, "derivative")

        for conservative in [True, False]:
            laplace = grid.make_operator("laplace",
                                         bcs,
                                         conservative=conservative)
            laplace(field.data)  # call once to pre-compile
            speed = estimate_computation_speed(laplace, field.data)
            print(
                f" numba (conservative={str(conservative):<5}): {int(speed):>9d}"
            )
        print()
예제 #10
0
"""
Visualizing a scalar field
==========================

This example displays methods for visualizing scalar fields.
"""

import numpy as np
import matplotlib.pyplot as plt
from pde import CylindricalGrid, ScalarField

# create a scalar field with some noise
grid = CylindricalGrid(7, [0, 4 * np.pi], 64)
data = ScalarField.from_expression(grid, 'sin(z) * exp(-r / 3)')
data += 0.05 * ScalarField.random_normal(grid)

# manipulate the field
smoothed = data.smooth()  # Gaussian smoothing to get rid of the noise
projected = data.project('r')  # integrate along the radial direction
sliced = smoothed.slice({'z': 1})  # slice the smoothed data

# create four plots of the field and the modifications
fig, axes = plt.subplots(nrows=2, ncols=2)
data.plot(ax=axes[0, 0], title='Original field')
smoothed.plot(ax=axes[1, 0], title='Smoothed field')
projected.plot(ax=axes[0, 1], title='Projection on axial coordinate')
sliced.plot(ax=axes[1, 1], title='Slice of smoothed field at $z=1$')
plt.subplots_adjust(hspace=0.8)
예제 #11
0
 def get_initial_state(self, grid):
     """ prepare a useful initial state """
     u = ScalarField(grid, self.a, label="Field $u$")
     v = self.b / self.a + 0.1 * ScalarField.random_normal(grid, label="Field $v$")
     return FieldCollection([u, v])
예제 #12
0
    
Here, :math:`D_0` and :math:`D_1` are the respective diffusivity and the
parameters :math:`a` and :math:`b` are related to reaction rates.

Note that the same result can also be achieved with a 
:doc:`full implementation of a custom class <pde_brusselator_class>`, which
allows for more flexibility at the cost of code complexity.
"""

from pde import PDE, FieldCollection, PlotTracker, ScalarField, UnitGrid

# define the PDE
a, b = 1, 3
d0, d1 = 1, 0.1
eq = PDE(
    {
        "u": f"{d0} * laplace(u) + {a} - ({b} + 1) * u + u**2 * v",
        "v": f"{d1} * laplace(v) + {b} * u - u**2 * v",
    }
)

# initialize state
grid = UnitGrid([64, 64])
u = ScalarField(grid, a, label="Field $u$")
v = b / a + 0.1 * ScalarField.random_normal(grid, label="Field $v$")
state = FieldCollection([u, v])

# simulate the pde
tracker = PlotTracker(interval=1, plot_args={"vmin": 0, "vmax": 5})
sol = eq.solve(state, t_range=20, dt=1e-3, tracker=tracker)