starting on the presentation
This commit is contained in:
89
nbody/utils/solver_mesh.py
Normal file
89
nbody/utils/solver_mesh.py
Normal file
@@ -0,0 +1,89 @@
|
||||
## Implementation of a mesh based full solver with boundary conditions etc.
|
||||
import numpy as np
|
||||
from . import mesh_forces
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def mesh_solver(
|
||||
particles: np.ndarray,
|
||||
G: float,
|
||||
mapping: callable,
|
||||
n_grid: int,
|
||||
bounds: tuple = (-1, 1),
|
||||
boundary: str = "vanishing",
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Computes the gravitational force acting on a set of particles using a mesh-based approach. The mesh is of fixed size: n_grid x n_grid x n_grid spanning the given bounds. Particles reaching the boundary are treated according to the boundary condition.
|
||||
Args:
|
||||
- particles: np.ndarray, shape=(n, 4). Assumes that the particles array has the following columns: x, y, z, m.
|
||||
- G: float, the gravitational constant.
|
||||
- mapping: callable, the mapping function to use.
|
||||
- n_grid: int, the number of grid points in each direction.
|
||||
- bounds: tuple, the bounds of the mesh.
|
||||
- boundary: str, the boundary condition to apply.
|
||||
"""
|
||||
if particles.shape[1] != 4:
|
||||
raise ValueError("Particles array must have 4 columns: x, y, z, m")
|
||||
|
||||
logger.debug(f"Computing forces for {particles.shape[0]} particles using mesh [mapping={mapping.__name__}, {n_grid=}]")
|
||||
|
||||
# the function is fine, let's abuse it somewhat
|
||||
|
||||
axis = np.linspace(bounds[0], bounds[1], n_grid)
|
||||
mesh = np.zeros((n_grid, n_grid, n_grid))
|
||||
spacing = np.abs(axis[1] - axis[0])
|
||||
logger.debug(f"Using mesh spacing: {spacing}")
|
||||
|
||||
|
||||
# Check that the boundary condition is fullfilled
|
||||
if boundary == "periodic":
|
||||
raise NotImplementedError("Periodic boundary conditions are not implemented yet")
|
||||
elif boundary == "vanishing":
|
||||
# remove the particles that are outside the mesh
|
||||
outlier_mask = particles[:, :3] < bounds[0] | particles[:, :3] > bounds[1]
|
||||
|
||||
if np.any(outlier_mask):
|
||||
logger.warning(f"Removing {np.sum(outlier_mask)} particles that are outside the mesh")
|
||||
particles = particles[~outlier_mask]
|
||||
logger.debug(f"New particles shape: {particles.shape}")
|
||||
else:
|
||||
raise ValueError(f"Unknown boundary condition: {boundary}")
|
||||
|
||||
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
mesh_forces.show_mesh_information(mesh, "Density mesh")
|
||||
|
||||
# compute the potential and its gradient
|
||||
phi_grad = mesh_forces.mesh_poisson(rho, G, spacing)
|
||||
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug(f"Got phi_grad with: {phi_grad.shape}, {np.max(phi_grad)}")
|
||||
mesh_forces.show_mesh_information(phi_grad[0], "Potential gradient (x-direction)")
|
||||
|
||||
# compute the particle forces from the mesh potential
|
||||
forces = np.zeros_like(particles[:, :3])
|
||||
for i, p in enumerate(particles):
|
||||
ijk = np.digitize(p, axis) - 1
|
||||
logger.debug(f"Particle {p} maps to cell {ijk}")
|
||||
# this gives 4 entries since p[3] the mass is digitized as well -> this is meaningless and we discard it
|
||||
# logger.debug(f"Particle {p} maps to cell {ijk}")
|
||||
forces[i] = - p[3] * phi_grad[..., ijk[0], ijk[1], ijk[2]]
|
||||
|
||||
return forces
|
||||
|
||||
|
||||
def particles_to_mesh(particles: np.ndarray, mesh: np.ndarray, axis: np.ndarray, mapping: callable) -> None:
|
||||
"""
|
||||
Maps a list of particles to an existing mesh, filling it inplace
|
||||
"""
|
||||
if particles.shape[1] < 4:
|
||||
raise ValueError("Particles array must have at least 4 columns: x, y, z, m")
|
||||
|
||||
# axis provide an easy way to map the particles to the mesh
|
||||
for p in particles:
|
||||
m = p[-1]
|
||||
# spread the star onto cells through the shape function, taking into account the mass
|
||||
ijks, weights = mapping(p, axis)
|
||||
for ijk, weight in zip(ijks, weights):
|
||||
mesh[ijk[0], ijk[1], ijk[2]] += weight * m
|
Reference in New Issue
Block a user