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