giovedì 6 novembre 2025

Diffusion Limited Aggregation

 Sto leggendo  Fractals and Chaos in Geology and Geophysics 2nd edition -- Donald Lawson Turcotte -- 2nd ed_, Cambridge, U_K, New York, England, 1997 

Molto interessante anche se un po' e' passato di moda come argomento (se ne parlava molto a cavallo anni 90/00). Questo tipo di frattali che assomiglia agli ossidi di manganese nelle fratture delle rocce sono comunque affascinanti

 

https://thumbs.dreamstime.com/b/minerali-dentritici-degli-ossidi-del-manganese-e-del-ferro-71827942.jpg

 

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from collections import deque

class DLA:
def __init__(self, size=400, num_particles=3000):
self.size = size
self.num_particles = num_particles
self.grid = np.zeros((size, size), dtype=bool)
self.center = size // 2
# Start with seed particle at center
self.grid[self.center, self.center] = True
# Store occupied positions for neighbor checking
self.occupied = {(self.center, self.center)}
# Maximum radius for spawning particles
self.spawn_radius = 10
self.max_radius = self.spawn_radius
def get_neighbors(self, x, y):
"""Get 4-connected neighbors"""
neighbors = []
for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
nx, ny = x + dx, y + dy
if 0 <= nx < self.size and 0 <= ny < self.size:
neighbors.append((nx, ny))
return neighbors
def is_adjacent_to_cluster(self, x, y):
"""Check if position is adjacent to existing cluster"""
for nx, ny in self.get_neighbors(x, y):
if (nx, ny) in self.occupied:
return True
return False
def random_walk_step(self, x, y):
"""Perform one random walk step"""
direction = np.random.randint(0, 4)
if direction == 0 and y < self.size - 1:
y += 1
elif direction == 1 and x < self.size - 1:
x += 1
elif direction == 2 and y > 0:
y -= 1
elif direction == 3 and x > 0:
x -= 1
return x, y
def spawn_particle(self):
"""Spawn particle at random position on circle around cluster"""
angle = np.random.uniform(0, 2 * np.pi)
r = self.spawn_radius
x = int(self.center + r * np.cos(angle))
y = int(self.center + r * np.sin(angle))
# Clamp to grid boundaries
x = np.clip(x, 0, self.size - 1)
y = np.clip(y, 0, self.size - 1)
return x, y
def add_particle(self):
"""Add one particle via random walk"""
x, y = self.spawn_particle()
max_steps = 100000
steps = 0
while steps < max_steps:
# Check if adjacent to cluster
if self.is_adjacent_to_cluster(x, y):
self.grid[x, y] = True
self.occupied.add((x, y))
# Update spawn radius
dist = np.sqrt((x - self.center)**2 + (y - self.center)**2)
if dist > self.max_radius:
self.max_radius = dist
self.spawn_radius = int(dist + 10)
return True
# Random walk
x, y = self.random_walk_step(x, y)
# If particle wanders too far, respawn
dist = np.sqrt((x - self.center)**2 + (y - self.center)**2)
if dist > self.spawn_radius + 20:
x, y = self.spawn_particle()
steps += 1
return False
def grow(self):
"""Grow the aggregate"""
for i in range(self.num_particles):
self.add_particle()
if (i + 1) % 100 == 0:
print(f"Added {i + 1}/{self.num_particles} particles")
def visualize(self):
"""Display the final aggregate"""
fig, ax = plt.subplots(figsize=(10, 10))
ax.imshow(self.grid, cmap='hot', interpolation='nearest')
ax.set_title(f'Diffusion Limited Aggregation\n{self.num_particles} particles')
ax.axis('off')
plt.tight_layout()
plt.show()

# Run simulation
print("Starting DLA simulation...")
dla = DLA(size=400, num_particles=3000)
dla.grow()
print("Simulation complete! Displaying result...")
dla.visualize()

Nessun commento:

Posta un commento

Kernel Panic QrCode

 In tanti anni ho visto qualche kernel panic, ma in questo formato non mi era mai successo    la cosa curiosa che al riavvio successivo ness...