probably faulty but running at least
This commit is contained in:
		| @@ -1,18 +1,18 @@ | |||||||
| # N-Body project - Checklist | # N-Body project - Checklist | ||||||
|  |  | ||||||
| ### Task 1 | ### Task 1 | ||||||
| - [ ] Compute characteristic quantities/scales | - [x] Compute characteristic quantities/scales | ||||||
| - [x] Compare analytical model and particle density distribution | - [x] Compare analytical model and particle density distribution | ||||||
| - [ ] Compute forces through nbody simulation | - [x] Compute forces through nbody simulation | ||||||
| - [x] vary softening length and compare results | - [x] vary softening length and compare results | ||||||
| - [x] compare with the analytical expectation from Newtons 2nd law | - [x] compare with the analytical expectation from Newtons 2nd law | ||||||
| - [ ] compute the relaxation time | - [x] compute the relaxation time | ||||||
|  |  | ||||||
| ### Task 2 (particle mesh) | ### Task 2 (particle mesh) | ||||||
| - [ ] Choose reasonable units | - [ ] Choose reasonable units | ||||||
| - [ ] Implement force computation on mesh | - [~x] Implement force computation on mesh | ||||||
| - [ ] Find optimal mesh size | - [x] Find optimal mesh size | ||||||
| - [ ] Compare with direct nbody simulation | - [x] Compare with direct nbody simulation | ||||||
| - [ ] Time integration for direct method AND mesh method | - [ ] Time integration for direct method AND mesh method | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										483
									
								
								nbody/copy.ipynb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								nbody/copy.ipynb
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -42,7 +42,7 @@ | |||||||
|      "name": "stderr", |      "name": "stderr", | ||||||
|      "output_type": "stream", |      "output_type": "stream", | ||||||
|      "text": [ |      "text": [ | ||||||
|       "16:03:58 - utils.load - Loaded 50010 rows and 10 columns from data/data.txt\n" |       "08:59:02 - utils.load - Loaded 50010 rows and 10 columns from data/data.txt\n" | ||||||
|      ] |      ] | ||||||
|     } |     } | ||||||
|    ], |    ], | ||||||
| @@ -104,8 +104,8 @@ | |||||||
|      "name": "stderr", |      "name": "stderr", | ||||||
|      "output_type": "stream", |      "output_type": "stream", | ||||||
|      "text": [ |      "text": [ | ||||||
|       "16:04:00 - task1 - Considering a globular cluster - total mass of particles: 4622219.258999999, maximum radius of particles: 724.689657812915\n", |       "08:59:04 - task1 - Considering a globular cluster - total mass of particles: 4622219.258999999, maximum radius of particles: 724.689657812915\n", | ||||||
|       "16:04:00 - utils.units - Set scales: M_SCALE = 0.022 solMass, R_SCALE = 0.028 pc\n" |       "08:59:04 - utils.units - Set scales: M_SCALE = 0.022 solMass, R_SCALE = 0.028 pc\n" | ||||||
|      ] |      ] | ||||||
|     } |     } | ||||||
|    ], |    ], | ||||||
| @@ -206,7 +206,7 @@ | |||||||
|      "name": "stderr", |      "name": "stderr", | ||||||
|      "output_type": "stream", |      "output_type": "stream", | ||||||
|      "text": [ |      "text": [ | ||||||
|       "16:04:00 - utils.particles - Found mean interparticle distance: 0.010402746349924056\n" |       "08:59:05 - utils.particles - Found mean interparticle distance: 0.010402746349924056\n" | ||||||
|      ] |      ] | ||||||
|     } |     } | ||||||
|    ], |    ], | ||||||
| @@ -235,56 +235,6 @@ | |||||||
|    "cell_type": "code", |    "cell_type": "code", | ||||||
|    "execution_count": 8, |    "execution_count": 8, | ||||||
|    "metadata": {}, |    "metadata": {}, | ||||||
|    "outputs": [ |  | ||||||
|     { |  | ||||||
|      "data": { |  | ||||||
|       "text/plain": [ |  | ||||||
|        "\"## compare the two force calculations\\n# since the forces were computed for each particle, rather than comparing them directly we compare the relative error in the magnitude and direction of the forces\\n\\n\\n# f_diff = f_nsquare_1e - f_analytical\\nf_diff = f_nsquare_2e - f_analytical\\ndiff_mag = np.linalg.norm(f_diff, axis=1)\\n\\n\\n# plot the distribution of the error\\n# create 4 stacked histograms, sharing the same x axis\\nfig, ax = plt.subplots(4, sharex=True)\\nax[0].hist(diff_mag, bins=NBINS)\\nax[0].set_title('Magnitude of the force difference')\\nax[0].set_yscale('log')\\n\\nax[1].hist(f_diff[:,0], bins=NBINS)\\nax[1].set_title('X component of the force difference')\\nax[1].set_yscale('log')\\n\\nax[2].hist(f_diff[:,1], bins=NBINS)\\nax[2].set_title('Y component of the force difference')\\nax[2].set_yscale('log')\\n\\nax[3].hist(f_diff[:,2], bins=NBINS)\\nax[3].set_title('Z component of the force difference')\\nax[3].set_yscale('log')\\n\\nplt.title('Error in forces')\\nplt.show()\\n\"" |  | ||||||
|       ] |  | ||||||
|      }, |  | ||||||
|      "execution_count": 8, |  | ||||||
|      "metadata": {}, |  | ||||||
|      "output_type": "execute_result" |  | ||||||
|     } |  | ||||||
|    ], |  | ||||||
|    "source": [ |  | ||||||
|     "\"\"\"## compare the two force calculations\n", |  | ||||||
|     "# since the forces were computed for each particle, rather than comparing them directly we compare the relative error in the magnitude and direction of the forces\n", |  | ||||||
|     "\n", |  | ||||||
|     "\n", |  | ||||||
|     "# f_diff = f_nsquare_1e - f_analytical\n", |  | ||||||
|     "f_diff = f_nsquare_2e - f_analytical\n", |  | ||||||
|     "diff_mag = np.linalg.norm(f_diff, axis=1)\n", |  | ||||||
|     "\n", |  | ||||||
|     "\n", |  | ||||||
|     "# plot the distribution of the error\n", |  | ||||||
|     "# create 4 stacked histograms, sharing the same x axis\n", |  | ||||||
|     "fig, ax = plt.subplots(4, sharex=True)\n", |  | ||||||
|     "ax[0].hist(diff_mag, bins=NBINS)\n", |  | ||||||
|     "ax[0].set_title('Magnitude of the force difference')\n", |  | ||||||
|     "ax[0].set_yscale('log')\n", |  | ||||||
|     "\n", |  | ||||||
|     "ax[1].hist(f_diff[:,0], bins=NBINS)\n", |  | ||||||
|     "ax[1].set_title('X component of the force difference')\n", |  | ||||||
|     "ax[1].set_yscale('log')\n", |  | ||||||
|     "\n", |  | ||||||
|     "ax[2].hist(f_diff[:,1], bins=NBINS)\n", |  | ||||||
|     "ax[2].set_title('Y component of the force difference')\n", |  | ||||||
|     "ax[2].set_yscale('log')\n", |  | ||||||
|     "\n", |  | ||||||
|     "ax[3].hist(f_diff[:,2], bins=NBINS)\n", |  | ||||||
|     "ax[3].set_title('Z component of the force difference')\n", |  | ||||||
|     "ax[3].set_yscale('log')\n", |  | ||||||
|     "\n", |  | ||||||
|     "plt.title('Error in forces')\n", |  | ||||||
|     "plt.show()\n", |  | ||||||
|     "\"\"\"" |  | ||||||
|    ] |  | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|    "cell_type": "code", |  | ||||||
|    "execution_count": null, |  | ||||||
|    "metadata": {}, |  | ||||||
|    "outputs": [ |    "outputs": [ | ||||||
|     { |     { | ||||||
|      "data": { |      "data": { | ||||||
| @@ -347,15 +297,15 @@ | |||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|    "cell_type": "code", |    "cell_type": "code", | ||||||
|    "execution_count": 10, |    "execution_count": 9, | ||||||
|    "metadata": {}, |    "metadata": {}, | ||||||
|    "outputs": [ |    "outputs": [ | ||||||
|     { |     { | ||||||
|      "name": "stderr", |      "name": "stderr", | ||||||
|      "output_type": "stream", |      "output_type": "stream", | ||||||
|      "text": [ |      "text": [ | ||||||
|       "16:04:17 - task1 - Crossing time for half mass system: 1.7e-06 pc(3/2) / solMass(1/2)\n", |       "08:59:24 - task1 - Crossing time for half mass system: 1.7e-06 pc(3/2) / solMass(1/2)\n", | ||||||
|       "16:04:17 - task1 - Direct estimate of the relaxation timescale: 0.00078 pc(3/2) / solMass(1/2)\n" |       "08:59:24 - task1 - Direct estimate of the relaxation timescale: 0.00078 pc(3/2) / solMass(1/2)\n" | ||||||
|      ] |      ] | ||||||
|     } |     } | ||||||
|    ], |    ], | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -39,7 +39,7 @@ def n_body_forces(particles: np.ndarray, G: float, softening: float = 0): | |||||||
|         num[i] = 0 |         num[i] = 0 | ||||||
|  |  | ||||||
|         f = np.sum((num.T / r_adjusted**1.5).T, axis=0) * m_current |         f = np.sum((num.T / r_adjusted**1.5).T, axis=0) * m_current | ||||||
|         forces[i] = -f |         forces[i] = - f | ||||||
|  |  | ||||||
|         if i % 5000 == 0: |         if i % 5000 == 0: | ||||||
|             logger.debug(f"Particle {i} done") |             logger.debug(f"Particle {i} done") | ||||||
| @@ -47,6 +47,24 @@ def n_body_forces(particles: np.ndarray, G: float, softening: float = 0): | |||||||
|     return forces |     return forces | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def n_body_forces_basic(particles: np.ndarray, G: float, softening: float = 0): | ||||||
|  |     if particles.shape[1] != 4: | ||||||
|  |         raise ValueError("Particles array must have 4 columns: x, y, z, m") | ||||||
|  |  | ||||||
|  |     x_vec = particles[:, 0:3] | ||||||
|  |     masses = particles[:, 3] | ||||||
|  |     n = particles.shape[0] | ||||||
|  |     forces = np.zeros((n, 3)) | ||||||
|  |     for i in range(n): | ||||||
|  |         for j in range(n): | ||||||
|  |             if i == j: | ||||||
|  |                 continue # keep the value at zero | ||||||
|  |             r_vec = x_vec[j] - x_vec[i] | ||||||
|  |             r = np.linalg.norm(r_vec) | ||||||
|  |             f = - G * masses[i] * masses[j] * r_vec / (r**3 + softening**3) | ||||||
|  |             forces[i] += f | ||||||
|  |  | ||||||
|  |     return forces | ||||||
|  |  | ||||||
| def analytical_forces(particles: np.ndarray): | def analytical_forces(particles: np.ndarray): | ||||||
|     """ |     """ | ||||||
|   | |||||||
| @@ -75,7 +75,7 @@ def mesh_forces_v2(particles: np.ndarray, G: float, n_grid: int, mapping: callab | |||||||
|     if logger.level >= logging.DEBUG: |     if logger.level >= logging.DEBUG: | ||||||
|         show_mesh_information(phi, "Potential mesh") |         show_mesh_information(phi, "Potential mesh") | ||||||
|         show_mesh_information(phi_grad[0], "Potential gradient") |         show_mesh_information(phi_grad[0], "Potential gradient") | ||||||
|     logger.debug(f"Got phi_grad with: {phi_grad.shape}, {np.max(phi_grad)}") |         logger.debug(f"Got phi_grad with: {phi_grad.shape}, {np.max(phi_grad)}") | ||||||
|      |      | ||||||
|     # compute the particle forces from the mesh potential |     # compute the particle forces from the mesh potential | ||||||
|     forces = np.zeros_like(particles[:, :3]) |     forces = np.zeros_like(particles[:, :3]) | ||||||
|   | |||||||
| @@ -16,10 +16,9 @@ def ode_setup(particles: np.ndarray, force_function: callable) -> tuple[np.ndarr | |||||||
|     if particles.shape[1] != 7: |     if particles.shape[1] != 7: | ||||||
|         raise ValueError("Particles array must have 7 columns: x, y, z, vx, vy, vz, m") |         raise ValueError("Particles array must have 7 columns: x, y, z, vx, vy, vz, m") | ||||||
|      |      | ||||||
|     # for scipy integrators we need to flatten array which contains 7 columns for now |     # for the integrators we need to flatten array which contains 7 columns for now | ||||||
|     # we don't really care how we reshape as long as we unflatten consistently afterwards |     # we don't really care how we reshape as long as we unflatten consistently | ||||||
|     particles = particles.reshape(-1, copy=False, order='A') |     particles = particles.reshape(-1, copy=False, order='A') | ||||||
|     # this is consistent with the unflattening in to_particles()! |  | ||||||
|     logger.debug(f"Reshaped 7 columns into {particles.shape=}") |     logger.debug(f"Reshaped 7 columns into {particles.shape=}") | ||||||
|  |  | ||||||
|     def f(y, t): |     def f(y, t): | ||||||
| @@ -30,32 +29,34 @@ def ode_setup(particles: np.ndarray, force_function: callable) -> tuple[np.ndarr | |||||||
|         y = to_particles(y) |         y = to_particles(y) | ||||||
|         # now y has shape (n, 7), with columns x, y, z, vx, vy, vz, m |         # now y has shape (n, 7), with columns x, y, z, vx, vy, vz, m | ||||||
|  |  | ||||||
|  |  | ||||||
|         forces = force_function(y[:, [0, 1, 2, -1]]) |         forces = force_function(y[:, [0, 1, 2, -1]]) | ||||||
|  |  | ||||||
|         # compute the accelerations |         # compute the accelerations | ||||||
|         masses = y[:, -1] |         masses = y[:, -1] | ||||||
|         a = forces / masses[:, None] |         a = forces / masses[:, None] | ||||||
|         # the [:, None] is to force broadcasting in order to divide each row of forces by the corresponding mass |         # the [:, None] is to force broadcasting in order to divide each row of forces by the corresponding mass | ||||||
|         # a.flatten() |  | ||||||
|  |  | ||||||
|         # replace some values in y: |         dydt = np.zeros_like(y) | ||||||
|         # the position columns become the velocities |         # the position columns become the velocities | ||||||
|         # the velocity columns become the accelerations |         # the velocity columns become the accelerations | ||||||
|         y[:, 0:3] = y[:, 3:6] |         dydt[:, 0:3] = y[:, 3:6] | ||||||
|         y[:, 3:6] = a |         dydt[:, 3:6] = a | ||||||
|         # the masses remain unchanged |         # the masses remain unchanged | ||||||
|  |         dydt[:, -1] = masses | ||||||
|  |  | ||||||
|         # flatten the array again |         # flatten the array again | ||||||
|         y = y.reshape(-1, copy=False, order='A') |         # logger.debug(f"As particles: {y}") | ||||||
|         return y |         dydt = dydt.reshape(-1, copy=False, order='A') | ||||||
|  |          | ||||||
|  |         # logger.debug(f"As column: {y}") | ||||||
|  |         return dydt | ||||||
|  |  | ||||||
|     return particles, f |     return particles, f | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_particles(y: np.ndarray) -> np.ndarray: | def to_particles(y: np.ndarray) -> np.ndarray: | ||||||
|     """ |     """ | ||||||
|     Converts the 1D array y into a 2D array IN PLACE |     Converts the 1D array y into a 2D array | ||||||
|     The new shape is (n, 7) where n is the number of particles. |     The new shape is (n, 7) where n is the number of particles. | ||||||
|     The columns are x, y, z, vx, vy, vz, m |     The columns are x, y, z, vx, vy, vz, m | ||||||
|     """ |     """ | ||||||
| @@ -64,10 +65,21 @@ def to_particles(y: np.ndarray) -> np.ndarray: | |||||||
|      |      | ||||||
|     n = y.size // 7 |     n = y.size // 7 | ||||||
|     y = y.reshape((n, 7), copy=False, order='F') |     y = y.reshape((n, 7), copy=False, order='F') | ||||||
|     logger.debug(f"Unflattened array into {y.shape=}") |     # logger.debug(f"Unflattened array into {y.shape=}") | ||||||
|     return y |     return y | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_particles_3d(y: np.ndarray) -> np.ndarray: | ||||||
|  |     """ | ||||||
|  |     Converts the 2D sol array with one vector per timestep into a 3D array: | ||||||
|  |     2d particles (nx7) x nsteps | ||||||
|  |     """ | ||||||
|  |     n_steps = y.shape[0] | ||||||
|  |     n_particles = y.shape[1] // 7 | ||||||
|  |     y = y.reshape((n_steps, n_particles, 7), copy=False, order='F') | ||||||
|  |     # logger.debug(f"Unflattened array into {y.shape=}") | ||||||
|  |     return y | ||||||
|  |  | ||||||
|  |  | ||||||
| def runge_kutta_4(y0 : np.ndarray, t : float, f, dt : float): | def runge_kutta_4(y0 : np.ndarray, t : float, f, dt : float): | ||||||
|     k1 = f(y0, t) |     k1 = f(y0, t) | ||||||
|   | |||||||
| @@ -2,10 +2,10 @@ import logging | |||||||
|  |  | ||||||
| logging.basicConfig( | logging.basicConfig( | ||||||
|     ## set logging level |     ## set logging level | ||||||
|     # level=logging.INFO, |     level = logging.DEBUG, | ||||||
|     level=logging.INFO, |     # level = logging.INFO, | ||||||
|     format='%(asctime)s - %(name)s - %(message)s', |     format = '%(asctime)s - %(name)s - %(message)s', | ||||||
|     datefmt='%H:%M:%S' |     datefmt = '%H:%M:%S' | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user