starting on the presentation
This commit is contained in:
		
							
								
								
									
										
											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 | ||||
		Reference in New Issue
	
	Block a user