First steps

We here collect simple examples for using the package to demonstrate some of its functionality.

Basic PDE simulation

A simple simulation of a partial differential equation (PDE) can be run as follows.

"""
Simple PDE
==========

Demonstrates a minimal example involving the diffusion equation
"""

from pde import DiffusionPDE, ScalarField, UnitGrid

import emulsim

# set up state
field = ScalarField.random_uniform(UnitGrid([32, 32], periodic=True))
element = emulsim.ScalarFieldElement.from_field(field)
state = emulsim.State({"field": element})

# set up simulation
simulation = emulsim.Simulation(state)
eq = DiffusionPDE(diffusivity=0.1)
simulation.add_actor("field", emulsim.ScalarPDEActor(eq))

# run simulation
result = simulation.run(t_range=10, dt=0.1)

result.plot()

General, each simulation is split in three parts: First, the simulation elements are combined to a simulation state. This defines what can be evolved in time. Second, the actors are added to a simulation. This determines how the elements change in time. Finally, the simulation is actually run and results are collected. Here, result is a State class like state, but with updated data.

Droplets simulation

The package becomes useful, when multiple elements interact. A simple example are droplets exchange material via a background phase:

"""
Simple droplet dynamics
=======================

Minimal examples of passive droplets interacting in a common background.
"""

from droplets import SphericalDroplet
from pde import ScalarField, UnitGrid

import emulsim

# set up state
grid = UnitGrid([32, 32], periodic=True)
background = emulsim.ScalarFieldElement.from_field(ScalarField(grid, 0.1))
droplet_data = [SphericalDroplet(grid.get_random_point(), 0.5) for _ in range(10)]
droplets = emulsim.SphericalDropletsElement.from_droplets(droplet_data)
state = emulsim.State({"background": background, "droplets": droplets})

# set up simulation
simulation = emulsim.Simulation(state)
simulation.add_actor("background", emulsim.DiffusionActor())
simulation.add_actor(("droplets", "background"), emulsim.SphericalDropletActor())

# run simulation
result = simulation.run(t_range=10)

result.plot()

Custom actor

One strength of the package is that actors can be simply defined, as shown below

"""
Custom Brownian motion class
============================

Demonstrates the custom implementation of Brownian motion.
"""

import numpy as np

import emulsim


class BrownianParticlesActor(emulsim.ActorBase):
    diffusivity = 1

    def evolve(self, elements, t, dt):
        """Evolve the particles in time."""
        (particles,) = elements
        scale = np.sqrt(dt) * self.diffusivity
        size = particles.positions.shape
        particles.positions[...] += scale * np.random.normal(size=size)


# set up state
particle_data = np.random.uniform(0, 100, size=(10, 2))
particles = emulsim.PointsElement(particle_data)
state = emulsim.State({"particles": particles})

# set up simulation
simulation = emulsim.Simulation(state)
simulation.add_actor("particles", BrownianParticlesActor())

# run simulation
result = simulation.run(t_range=10)

result.plot()

Here, we define an actor that moves points like Brownian particle, i.e., they simply diffuse around and do not interact with each other.