starting on the presentation
This commit is contained in:
parent
9d54e9743e
commit
7db6480e51
BIN
nbody/presentation/main.pdf
Normal file
BIN
nbody/presentation/main.pdf
Normal file
Binary file not shown.
148
nbody/presentation/main.typ
Normal file
148
nbody/presentation/main.typ
Normal file
@ -0,0 +1,148 @@
|
||||
#import "@preview/diatypst:0.2.0": *
|
||||
#import "@preview/based:0.2.0": base64
|
||||
|
||||
#set text(font: "Cantarell")
|
||||
// #set heading(numbering: (..nums)=>"")
|
||||
|
||||
#show: slides.with(
|
||||
title: "N-Body project ",
|
||||
subtitle: "Computational Astrophysics, HS24",
|
||||
date: "04.02.2024",
|
||||
authors: ("Rémy Moll"),
|
||||
toc: false,
|
||||
// layout: "large",
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Helpers for code block displaying
|
||||
|
||||
#let cell_matcher(cell, cell_tag) = {
|
||||
if cell.cell_type != "code" {
|
||||
return false
|
||||
}
|
||||
let metadata = cell.metadata
|
||||
if metadata.keys().contains("tags") == false {
|
||||
return false
|
||||
}
|
||||
|
||||
return cell.metadata.tags.contains(cell_tag)
|
||||
}
|
||||
|
||||
#let code_cell(fcontent, cell_tag) = {
|
||||
let cells = fcontent.cells
|
||||
let matching_cell = cells.find(x => cell_matcher(x, cell_tag))
|
||||
|
||||
let cell_content = matching_cell.source
|
||||
// format the cell content
|
||||
let single_line = cell_content.fold("", (acc, x) => acc + x)
|
||||
|
||||
text(
|
||||
raw(
|
||||
single_line,
|
||||
lang: "python",
|
||||
block: true
|
||||
),
|
||||
size: 0.8em
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
#let image_cell(fcontent, cell_tag) = {
|
||||
let cells = fcontent.cells
|
||||
let matching_cell = cells.find(x => cell_matcher(x, cell_tag))
|
||||
|
||||
let outputs = matching_cell.outputs
|
||||
for output in outputs {
|
||||
let image_data = output.at("data", default: (:)).at("image/png", default: none)
|
||||
if image_data != none {
|
||||
align(
|
||||
center,
|
||||
image.decode(
|
||||
base64.decode(image_data),
|
||||
// format: "png",
|
||||
height: 70%
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Where is the code?
|
||||
#let t1 = json("../task1.ipynb")
|
||||
#let t2 = json("../task2-particle-mesh.ipynb")
|
||||
|
||||
|
||||
|
||||
// Content
|
||||
= N-body forces and analytical solutions
|
||||
|
||||
== Objective
|
||||
Implement naive N-body force computation and get an intuition of the challenges:
|
||||
- accuracy
|
||||
- computation time
|
||||
- stability
|
||||
|
||||
$=>$ still useful to compute basic quantities of the system, but too limited for large systems or the dynamical evolution of the system
|
||||
|
||||
|
||||
== Overview - the system
|
||||
Get a feel for the particles and their distribution. [Code at @task1:plot_particle_distribution[]]
|
||||
#image_cell(t1, "plot_particle_distribution")
|
||||
|
||||
#code_cell(t1, "plotting")
|
||||
|
||||
== Inspecting the data
|
||||
#code_cell(t2, "plotting")
|
||||
|
||||
#image_cell(t2, "plotting")
|
||||
|
||||
|
||||
== Density
|
||||
Some images about the density
|
||||
|
||||
== N Body and variations
|
||||
sdsd
|
||||
|
||||
== Relaxation
|
||||
sd
|
||||
|
||||
|
||||
= Default Styling in diatypst
|
||||
|
||||
== Terms, Code, Lists
|
||||
|
||||
_diatypst_ defines some default styling for elements, e.g Terms created with ```typc / Term: Definition``` will look like this
|
||||
|
||||
/ *Term*: Definition
|
||||
|
||||
A code block like this
|
||||
|
||||
```python
|
||||
// Example Code
|
||||
print("Hello World!")
|
||||
```
|
||||
|
||||
Lists have their marker respect the `title-color`
|
||||
|
||||
#columns(2)[
|
||||
- A
|
||||
- AAA
|
||||
- B
|
||||
#colbreak()
|
||||
1. AAA
|
||||
2. BBB
|
||||
3. CCC
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
= Appendix - Code
|
||||
== Code
|
||||
<task1:plot_particle_distribution>
|
||||
#code_cell(t1, "plot_particle_distribution")
|
File diff suppressed because one or more lines are too long
@ -258,7 +258,11 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"tags": [
|
||||
"plotting"
|
||||
]
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
@ -385,7 +389,11 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"tags": [
|
||||
"kjhfsdf"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def integrate(method: str, force_function: callable, p0: np.ndarray, t_range: np.ndarray) -> np.ndarray:\n",
|
||||
|
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
|
Loading…
x
Reference in New Issue
Block a user