A Python framework for solving diffusion equations (heat equation) with agent-based sources using multiple numerical methods in 1D, 2D, and 3D.
- Multiple numerical methods: Explicit Euler, Implicit Euler, and Crank-Nicolson schemes
- Multi-dimensional support: 1D, 2D, and 3D spatial domains
- Flexible boundary conditions: Dirichlet, Neumann, Periodic, and Robin (mixed) BCs
- Agent-based sources: Substrate-secreting agents with configurable positions and rates
- Rich initial conditions: Gaussian, uniform, step function, checkerboard, spherical, and more
- Configurable parameters: Diffusion coefficient, decay rate, time step, grid resolution
git clone git@github.com:bsc-life/agent-based-prototyping.git
cd agent-based-prototyping
pip install -e .pip install -e ".[dev]"import numpy as np
import matplotlib.pyplot as plt
from diffusion_schemas import ExplicitEulerSchema
from diffusion_schemas.utils import gaussian
# Create a 1D diffusion schema
schema = ExplicitEulerSchema(
domain_size=1.0, # Domain: [0, 1]
grid_points=100, # 100 grid points
dt=0.0001, # Time step
diffusion_coefficient=0.1,
decay_rate=0.0
)
# Set initial condition: Gaussian at center
ic = gaussian(center=0.5, amplitude=1.0, width=0.05)
schema.set_initial_condition(ic)
# Solve for 0.1 time units
schema.solve(t_final=0.1)
# Get and plot result
u = schema.get_state()
x = np.linspace(0, 1, 100)
plt.plot(x, u)
plt.show()from diffusion_schemas import CrankNicolsonSchema
from diffusion_schemas.utils import Agent, DirichletBC
# Create 2D schema
schema = CrankNicolsonSchema(
domain_size=(1.0, 1.0),
grid_points=(50, 50),
dt=0.001,
diffusion_coefficient=0.05
)
# Set zero initial condition
schema.set_initial_condition(0.0)
# Add substrate-secreting agents
agent1 = Agent(position=(0.3, 0.3), secretion_rate=10.0, kernel_width=0.05)
agent2 = Agent(position=(0.7, 0.7), secretion_rate=5.0, kernel_width=0.05)
schema.add_agent(agent1)
schema.add_agent(agent2)
# Set boundary conditions (fixed at zero)
schema.set_boundary_conditions(DirichletBC(value=0.0))
# Solve
history = schema.solve(t_final=1.0, store_history=True)
# Visualize
import matplotlib.pyplot as plt
plt.imshow(schema.get_state(), origin='lower', extent=[0, 1, 0, 1])
plt.colorbar(label='Concentration')
plt.show()from diffusion_schemas import ExplicitEulerSchema, ImplicitEulerSchema, CrankNicolsonSchema
# Create same problem with different methods
common_params = {
'domain_size': 1.0,
'grid_points': 100,
'dt': 0.001,
'diffusion_coefficient': 0.1
}
explicit = ExplicitEulerSchema(**common_params, check_stability=False)
implicit = ImplicitEulerSchema(**common_params)
crank_nicolson = CrankNicolsonSchema(**common_params)
# Set identical initial conditions
ic = gaussian(center=0.5, amplitude=1.0, width=0.1)
for schema in [explicit, implicit, crank_nicolson]:
schema.set_initial_condition(ic)
# Solve and compare
for schema in [explicit, implicit, crank_nicolson]:
schema.solve(t_final=0.5)
# Plot or analyze results...The framework solves the diffusion equation with decay and sources:
where:
-
$u$ is the concentration/temperature field -
$D$ is the diffusion coefficient -
$\lambda$ is the decay rate -
$S(x, t)$ is the source term from agents
-
Stability: Conditionally stable, requires
$\Delta t \leq \Delta x^2 / (2dD)$ - Accuracy: First-order in time, second-order in space
- Performance: Fast per step, small time steps required
- Stability: Unconditionally stable
- Accuracy: First-order in time, second-order in space
- Performance: Requires solving linear system, allows larger time steps
- Stability: Unconditionally stable
- Accuracy: Second-order in time and space
- Performance: Best accuracy, requires solving linear system
All schema classes inherit from the abstract Schema base class and provide:
set_initial_condition(ic): Set initial concentration fieldset_diffusion_coefficient(D): Set diffusion coefficientset_decay_rate(λ): Set decay rateset_boundary_conditions(bc): Set boundary conditionsadd_agent(agent): Add substrate-secreting agentstep(): Perform single time stepsolve(t_final, store_history=False): Solve to final timeget_state(): Get current concentration fieldreset(): Reset to zero initial condition
-
DirichletBC(value): Fixed value at boundaries -
NeumannBC(flux): Fixed flux at boundaries (zero flux = insulating) -
PeriodicBC(): Periodic wrapping -
RobinBC(alpha, beta, gamma): Mixed condition$\alpha u + \beta \frac{\partial u}{\partial n} = \gamma$
Agent(
position=(x, y, z), # Agent location
secretion_rate=1.0, # Secretion rate (or callable f(t))
kernel_width=None, # Gaussian smoothing width (None = point source)
name="Agent_1" # Optional name
)Helper functions in diffusion_schemas.utils:
gaussian(center, amplitude, width): Gaussian distributionuniform(value): Constant valuestep_function(position, value_left, value_right): Discontinuous stepcheckerboard(spacing, value_on, value_off): 2D patternsphere(center, radius, value_inside, value_outside): Spherical/circular regionradial_gradient(center, max_value, max_radius, decay_type): Radial decaysum_conditions(*conditions): Combine multiple conditions
See the examples/ directory for complete working examples:
example_1d_diffusion.py: Basic 1D diffusionexample_2d_agents.py: 2D with multiple agentsexample_method_comparison.py: Compare numerical methodsexample_3d_sphere.py: 3D diffusion from sphere
pytest tests/With coverage:
pytest tests/ --cov=diffusion_schemas --cov-report=htmlMIT License - See LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.