| import numpy as np |
| from scipy.ndimage import map_coordinates, spline_filter |
| from scipy.sparse.linalg import factorized |
|
|
| from .numerical import difference, operator |
|
|
|
|
| class Fluid: |
| def __init__(self, shape, *quantities, pressure_order=1, advect_order=3): |
| self.shape = shape |
| self.dimensions = len(shape) |
|
|
| |
| |
| self.quantities = quantities |
| for q in quantities: |
| setattr(self, q, np.zeros(shape)) |
|
|
| self.indices = np.indices(shape) |
| self.velocity = np.zeros((self.dimensions, *shape)) |
|
|
| laplacian = operator(shape, difference(2, pressure_order)) |
| self.pressure_solver = factorized(laplacian) |
| |
| self.advect_order = advect_order |
|
|
| def step(self): |
| |
| advection_map = self.indices - self.velocity |
|
|
| |
| |
| |
| def advect(field, filter_epsilon=10e-2, mode='constant'): |
| filtered = spline_filter(field, order=self.advect_order, mode=mode) |
| field = filtered * (1 - filter_epsilon) + field * filter_epsilon |
| return map_coordinates(field, advection_map, prefilter=False, order=self.advect_order, mode=mode) |
|
|
| |
| |
| for d in range(self.dimensions): |
| self.velocity[d] = advect(self.velocity[d]) |
|
|
| for q in self.quantities: |
| setattr(self, q, advect(getattr(self, q))) |
|
|
| |
| |
| jacobian_shape = (self.dimensions,) * 2 |
| partials = tuple(np.gradient(d) for d in self.velocity) |
| jacobian = np.stack(partials).reshape(*jacobian_shape, *self.shape) |
|
|
| divergence = jacobian.trace() |
|
|
| |
| |
| |
| |
| |
| curl_mask = np.triu(np.ones(jacobian_shape, dtype=bool), k=1) |
| curl = (jacobian[curl_mask] - jacobian[curl_mask.T]).squeeze() |
|
|
| |
| pressure = self.pressure_solver(divergence.flatten()).reshape(self.shape) |
| self.velocity -= np.gradient(pressure) |
|
|
| return divergence, curl, pressure |