Beispiel #1
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)
Beispiel #2
0
def test_pde_time_dependent_bcs(backend):
    """test PDE with time-dependent BCs"""
    field = ScalarField(grids.UnitGrid([3]))

    eq = PDE({"c": "laplace(c)"}, bc={"value_expression": "Heaviside(t - 1.5)"})

    storage = MemoryStorage()
    eq.solve(field, t_range=10, dt=1e-2, backend=backend, tracker=storage.tracker(1))

    np.testing.assert_allclose(storage[1].data, 0)
    np.testing.assert_allclose(storage[-1].data, 1, rtol=1e-3)
Beispiel #3
0
def test_pde_complex():
    """test complex valued PDE"""
    eq = PDE({"p": "I * laplace(p)"})
    assert not eq.explicit_time_dependence
    assert eq.complex_valued

    field = ScalarField.random_uniform(grids.UnitGrid([4]))
    assert not field.is_complex
    res1 = eq.solve(field, t_range=1, dt=0.1, backend="numpy", tracker=None)
    assert res1.is_complex
    res2 = eq.solve(field, t_range=1, dt=0.1, backend="numpy", tracker=None)
    assert res2.is_complex

    np.testing.assert_allclose(res1.data, res2.data)
Beispiel #4
0
def test_pde_critical_input():
    """test some wrong input and edge cases"""
    # test whether reserved symbols can be used as variables
    grid = grids.UnitGrid([4])
    eq = PDE({"E": 1})
    res = eq.solve(ScalarField(grid), t_range=2)
    np.testing.assert_allclose(res.data, 2)

    with pytest.raises(ValueError):
        PDE({"t": 1})

    eq = PDE({"u": 1})
    assert eq.expressions == {"u": "1.0"}
    with pytest.raises(ValueError):
        eq.evolution_rate(FieldCollection.scalar_random_uniform(2, grid))

    eq = PDE({"u": 1, "v": 2})
    assert eq.expressions == {"u": "1.0", "v": "2.0"}
    with pytest.raises(ValueError):
        eq.evolution_rate(ScalarField.random_uniform(grid))

    eq = PDE({"u": "a"})
    with pytest.raises(RuntimeError):
        eq.evolution_rate(ScalarField.random_uniform(grid))

    eq = PDE({"x": "x"})
    with pytest.raises(ValueError):
        eq.evolution_rate(ScalarField(grid))
Beispiel #5
0
def test_pde_noise(backend):
    """test noise operator on PDE class"""
    grid = grids.UnitGrid([64, 64])
    state = FieldCollection([ScalarField(grid), ScalarField(grid)])

    eq = PDE({"a": 0, "b": 0}, noise=0.5)
    res = eq.solve(state, t_range=1, backend=backend, dt=1, tracker=None)
    assert res.data.std() == pytest.approx(0.5, rel=0.1)

    eq = PDE({"a": 0, "b": 0}, noise=[0.01, 2.0])
    res = eq.solve(state, t_range=1, backend=backend, dt=1)
    assert res.data[0].std() == pytest.approx(0.01, rel=0.1)
    assert res.data[1].std() == pytest.approx(2.0, rel=0.1)

    with pytest.raises(ValueError):
        eq = PDE({"a": 0}, noise=[0.01, 2.0])
        eq.solve(ScalarField(grid), t_range=1, backend=backend, dt=1, tracker=None)
Beispiel #6
0
def test_anti_periodic_bcs():
    """test a simulation with anti-periodic BCs"""
    grid = grids.CartesianGrid([[-10, 10]], 32, periodic=True)
    field = ScalarField.from_expression(grid, "0.01 * x**2")
    field -= field.average

    # test normal periodic BCs
    eq1 = PDE({"c": "laplace(c) + c - c**3"}, bc="periodic")
    res1 = eq1.solve(field, t_range=1e5, dt=1e-1)
    assert np.allclose(np.abs(res1.data), 1)
    assert res1.fluctuations == pytest.approx(0)

    # test normal anti-periodic BCs
    eq2 = PDE({"c": "laplace(c) + c - c**3"}, bc="anti-periodic")
    res2 = eq2.solve(field, t_range=1e3, dt=1e-3)
    assert np.all(np.abs(res2.data) <= 1)
    assert res2.fluctuations > 0.1
Beispiel #7
0
def test_pde_product_operators():
    """test inner and outer products"""
    eq = PDE(
        {"p": "gradient(dot(p, p) + inner(p, p)) + tensor_divergence(outer(p, p))"}
    )
    assert not eq.explicit_time_dependence
    assert not eq.complex_valued
    field = VectorField(grids.UnitGrid([4]), 1)
    res = eq.solve(field, t_range=1, dt=0.1, backend="numpy", tracker=None)
    np.testing.assert_allclose(res.data, field.data)
Beispiel #8
0
def test_pde_2scalar():
    """test PDE with two scalar fields"""
    eq = PDE({"u": "laplace(u) - u", "v": "- u * v"})
    assert not eq.explicit_time_dependence
    assert not eq.complex_valued
    grid = grids.UnitGrid([8])
    field = FieldCollection.scalar_random_uniform(2, 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)
Beispiel #9
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)
Beispiel #10
0
def test_pde_vector():
    """test PDE with a single vector field"""
    eq = PDE({"u": "vector_laplace(u) + exp(-t)"})
    assert eq.explicit_time_dependence
    assert not eq.complex_valued
    grid = grids.UnitGrid([8, 8])
    field = VectorField.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)
Beispiel #11
0
def test_pde_integral(backend):
    """test PDE with integral"""
    grid = grids.UnitGrid([16])
    field = ScalarField.random_uniform(grid)
    eq = PDE({"c": "-integral(c)"})

    # test rhs
    rhs = eq.make_pde_rhs(field, backend=backend)
    np.testing.assert_allclose(rhs(field.data, 0), -field.integral)

    # test evolution
    for method in ["scipy", "explicit"]:
        res = eq.solve(field, t_range=1000, method=method, tracker=None)
        assert res.integral == pytest.approx(0, abs=1e-2)
        np.testing.assert_allclose(res.data, field.data - field.magnitude, atol=1e-3)
Beispiel #12
0
def test_pde_vector_scalar():
    """test PDE with a vector and a scalar field"""
    eq = PDE({"u": "vector_laplace(u) - u + gradient(v)", "v": "- divergence(u)"})
    assert not eq.explicit_time_dependence
    assert not eq.complex_valued
    grid = grids.UnitGrid([8, 8])
    field = FieldCollection(
        [VectorField.random_uniform(grid), ScalarField.random_uniform(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)
Beispiel #13
0
def test_solvers_complex(backend):
    """test solvers with a complex PDE"""
    r = FieldCollection.scalar_random_uniform(2, UnitGrid([3]), labels=["a", "b"])
    c = r["a"] + 1j * r["b"]
    assert c.is_complex

    # assume c = a + i * b
    eq_c = PDE({"c": "-I * laplace(c)"})
    eq_r = PDE({"a": "laplace(b)", "b": "-laplace(a)"})
    res_r = eq_r.solve(r, t_range=1e-2, dt=1e-3, backend="numpy", tracker=None)
    exp_c = res_r[0].data + 1j * res_r[1].data

    for solver_class in [ExplicitSolver, ImplicitSolver, ScipySolver]:
        solver = solver_class(eq_c, backend=backend)
        controller = Controller(solver, t_range=1e-2, tracker=None)
        res_c = controller.run(c, dt=1e-3)
        np.testing.assert_allclose(res_c.data, exp_c, rtol=1e-3, atol=1e-3)
Beispiel #14
0
def test_compare_swift_hohenberg(grid):
    """compare custom class to swift-Hohenberg"""
    rate, kc2, delta = np.random.uniform(0.5, 2, size=3)
    eq1 = SwiftHohenbergPDE(rate=rate, kc2=kc2, delta=delta)
    eq2 = PDE({
        "u":
        f"({rate} - {kc2}**2) * u - 2 * {kc2} * laplace(u) "
        f"- laplace(laplace(u)) + {delta} * u**2 - u**3"
    })
    assert eq1.explicit_time_dependence == eq2.explicit_time_dependence
    assert eq1.complex_valued == eq2.complex_valued

    field = ScalarField.random_uniform(grid)
    res1 = eq1.solve(field, t_range=1, dt=0.01, backend="numpy", tracker=None)
    res2 = eq2.solve(field, t_range=1, dt=0.01, backend="numpy", tracker=None)

    res1.assert_field_compatible(res1)
    np.testing.assert_allclose(res1.data, res2.data)
Beispiel #15
0
grid = CartesianGrid([[0, x_max]], [int(xstep)])
state = ScalarField(grid=grid, data=T_origin + T_Kelvin)

eq = PDE(
    rhs={'c': f'{alpha}*laplace(c)'},
    bc={
        'type': 'mixed',
        'value': -1 * h / llambda,
        'const': bc_T
    },
)
#bc_ops={})

storage = MemoryStorage()
eq.solve(state, t_range=(tmin, tmax), dt=1e-3, tracker=storage.tracker(tstep))


def temp_curve():
    p = []
    for i in range(int((tmax - tmin) / tstep)):
        p.append(storage.data[i][-1])

    q = np.asarray(p)
    plt.plot(np.linspace(tmin, tmax, int((tmax - tmin) / tstep), endpoint=0),
             q,
             label='Temperature')
    plt.xlabel('Time')
    plt.ylabel('Temprature')
    plt.show()
    (195+8*tt+273.15,And(233<tt,tt<=238)), \
    (235+273.15,And(238<tt,tt<=268.5)), \
    (235+4*tt+273.15,And(268.5<tt,tt<=273.5)), \
    (255+273.15,And(273.5<tt,tt<=344.5)), \
    (255-(230/91)*tt+273.15,And(344.5<tt,tt<=435.5)), \
    (25+273.15,And(435.5<tt,tt<=500))).subs(tt,t))'

grid = CartesianGrid([[0, x_max]], [int(xstep)])
state=ScalarField(grid=grid,data=T_origin+T_Kelvin)

eq = PDE(rhs={'c': 'a*laplace(c)'}, 
    bc={'value_expression': bc_T},
    consts={'a':alpha})

storage = MemoryStorage()
eq.solve(state, t_range=tmax, tracker=storage.tracker(tstep))

def kymograph_pic():
    plot_kymograph(storage)

def temp_curve():
    p=[]
    for i in range(int(tmax/tstep)):
        p.append(storage.data[i][-1])
    
    q=np.asarray(p)
    plt.plot(np.linspace(0,tmax,int(tmax/tstep),endpoint=1),q,label='Temperature')
    plt.xlabel('Time')
    plt.ylabel('Temprature')
    plt.show()
Beispiel #17
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)
Beispiel #18
0
==============================

This example implements a PDE that is only defined in one dimension.
Here, we chose the `Korteweg-de Vries equation
<https://en.wikipedia.org/wiki/Korteweg–de_Vries_equation>`_, given by

.. math::
    \partial_t \phi = 6 \phi \partial_x \phi - \partial_x^3 \phi
    
which we implement using the :class:`~pde.pdes.pde.PDE`.
"""

from math import pi

from pde import PDE, CartesianGrid, MemoryStorage, ScalarField, plot_kymograph

# initialize the equation and the space
eq = PDE(
    {"φ": "6 * φ * get_x(gradient(φ)) - laplace(get_x(gradient(φ)))"},
    user_funcs={"get_x": lambda arr: arr[0]},
)
grid = CartesianGrid([[0, 2 * pi]], [32], periodic=True)
state = ScalarField.from_expression(grid, "sin(x)")

# solve the equation and store the trajectory
storage = MemoryStorage()
eq.solve(state, t_range=3, tracker=storage.tracker(0.1))

# plot the trajectory as a space-time plot
plot_kymograph(storage)
Beispiel #19
0
This example implements a complex PDE using the :class:`~pde.pdes.pde.PDE`. We here
chose the `Schrödinger equation <https://en.wikipedia.org/wiki/Schrödinger_equation>`_ 
without a spatial potential in non-dimensional form:

.. math::
    i \partial_t \psi = -\nabla^2 \psi
    
Note that the example imposes Neumann conditions at the wall, so the wave packet is
expected to reflect off the wall.
"""

from math import sqrt

from pde import PDE, CartesianGrid, MemoryStorage, ScalarField, plot_kymograph

grid = CartesianGrid([[0, 20]], 128, periodic=False)  # generate grid

# create a (normalized) wave packet with a certain form as an initial condition
initial_state = ScalarField.from_expression(
    grid, "exp(I * 5 * x) * exp(-(x - 10)**2)")
initial_state /= sqrt(initial_state.to_scalar("norm_squared").integral.real)

eq = PDE({"ψ": f"I * laplace(ψ)"})  # define the pde

# solve the pde and store intermediate data
storage = MemoryStorage()
eq.solve(initial_state, t_range=2.5, dt=1e-5, tracker=[storage.tracker(0.02)])

# visualize the results as a space-time plot
plot_kymograph(storage, scalar="norm_squared")
Beispiel #20
0
"""
Time-dependent boundary conditions
==================================

This example solves a simple diffusion equation in one dimensions with time-dependent
boundary conditions.
"""

from pde import PDE, CartesianGrid, MemoryStorage, ScalarField, plot_kymograph

grid = CartesianGrid([[0, 10]], [64])  # generate grid
state = ScalarField(grid)  # generate initial condition

eq = PDE({"c": "laplace(c)"}, bc={"value_expression": "sin(t)"})

storage = MemoryStorage()
eq.solve(state, t_range=20, dt=1e-4, tracker=storage.tracker(0.1))

# plot the trajectory as a space-time plot
plot_kymograph(storage)
Beispiel #21
0
r"""
Kuramoto-Sivashinsky - Using `PDE` class
========================================

This example implements a scalar PDE using the :class:`~pde.pdes.pde.PDE`. We here
consider the `Kuramoto–Sivashinsky equation
<https://en.wikipedia.org/wiki/Kuramoto–Sivashinsky_equation>`_, which for instance 
describes the dynamics of flame fronts:

.. math::
    \partial_t u = -\frac12 |\nabla u|^2 - \nabla^2 u - \nabla^4 u
"""

from pde import PDE, ScalarField, UnitGrid

grid = UnitGrid([32, 32])  # generate grid
state = ScalarField.random_uniform(grid)  # generate initial condition

eq = PDE({"u": "-gradient_squared(u) / 2 - laplace(u + laplace(u))"})  # define the pde
result = eq.solve(state, t_range=10, dt=0.01)
result.plot()
using the :class:`~pde.pdes.pde.PDE` class. In particular, we consider
:math:`D(x) = 1.01 + \tanh(x)`, which gives a low diffusivity on the left side of the
domain.

Note that the naive implementation,
:code:`PDE({"c": "divergence((1.01 + tanh(x)) * gradient(c))"})`, has numerical
instabilities. This is because two finite difference approximations are nested. To
arrive at a more stable numerical scheme, it is advisable to expand the divergence, 

.. math::
    \partial_t c = D \nabla^2 c + \nabla D . \nabla c
"""

from pde import PDE, CartesianGrid, MemoryStorage, ScalarField, plot_kymograph

# Expanded definition of the PDE
diffusivity = "1.01 + tanh(x)"
term_1 = f"({diffusivity}) * laplace(c)"
term_2 = f"dot(gradient({diffusivity}), gradient(c))"
eq = PDE({"c": f"{term_1} + {term_2}"}, bc={"value": 0})

grid = CartesianGrid([[-5, 5]], 64)  # generate grid
field = ScalarField(grid, 1)  # generate initial condition

storage = MemoryStorage()  # store intermediate information of the simulation
res = eq.solve(field, 100, dt=1e-3,
               tracker=storage.tracker(1))  # solve the PDE

plot_kymograph(storage)  # visualize the result in a space-time plot