cleanup for the presentation
This commit is contained in:
parent
7db6480e51
commit
37a2687ffe
82
nbody/presentation/helpers.typ
Normal file
82
nbody/presentation/helpers.typ
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Helpers for code block displaying
|
||||||
|
|
||||||
|
#import "@preview/based:0.2.0": base64
|
||||||
|
|
||||||
|
#let code_font_scale = 0.6em
|
||||||
|
|
||||||
|
#let cell_matcher(cell, cell_tag) = {
|
||||||
|
// Matching function to check if a cell has a specific 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) = {
|
||||||
|
// Extract the content of a cell and display it as a code block
|
||||||
|
let cells = fcontent.cells
|
||||||
|
let matching_cell = cells.find(x => cell_matcher(x, cell_tag))
|
||||||
|
|
||||||
|
let cell_content = matching_cell.source
|
||||||
|
let single_line = cell_content.fold("", (acc, x) => acc + x)
|
||||||
|
|
||||||
|
text(
|
||||||
|
raw(
|
||||||
|
single_line,
|
||||||
|
lang: "python",
|
||||||
|
block: true
|
||||||
|
),
|
||||||
|
size: code_font_scale
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#let image_cell(fcontent, cell_tag) = {
|
||||||
|
// Extract the output (image) of a cell and display it as an image
|
||||||
|
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),
|
||||||
|
// height: 70% // the height should be set by the caller. This gives the flexibility to adjust the height of the image
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#let code_reference_cell(fcontent, cell_tag) = {
|
||||||
|
// Extract the output (text) of a cell and display it as a code block
|
||||||
|
// This is useful for showing the code of imported functions
|
||||||
|
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 cell_output = output.at("text", default: (:))
|
||||||
|
if cell_output != none {
|
||||||
|
let single_line = cell_output.join("")
|
||||||
|
text(
|
||||||
|
raw(
|
||||||
|
single_line,
|
||||||
|
lang: "python",
|
||||||
|
block: true
|
||||||
|
),
|
||||||
|
size: code_font_scale
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
@ -1,7 +1,6 @@
|
|||||||
#import "@preview/diatypst:0.2.0": *
|
#import "@preview/diatypst:0.2.0": *
|
||||||
#import "@preview/based:0.2.0": base64
|
|
||||||
|
|
||||||
#set text(font: "Cantarell")
|
// #set text(font: "Cantarell")
|
||||||
// #set heading(numbering: (..nums)=>"")
|
// #set heading(numbering: (..nums)=>"")
|
||||||
|
|
||||||
#show: slides.with(
|
#show: slides.with(
|
||||||
@ -11,74 +10,25 @@
|
|||||||
authors: ("Rémy Moll"),
|
authors: ("Rémy Moll"),
|
||||||
toc: false,
|
toc: false,
|
||||||
// layout: "large",
|
// layout: "large",
|
||||||
|
// ratio: 16/9,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#import "helpers.typ"
|
||||||
|
|
||||||
|
// KINDA COOL:
|
||||||
|
// _diatypst_ defines some default styling for elements, e.g Terms created with ```typc / Term: Definition``` will look like this
|
||||||
|
|
||||||
|
// / *Term*: Definition
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Helpers for code block displaying
|
// Setup of code location
|
||||||
|
|
||||||
#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 t1 = json("../task1.ipynb")
|
||||||
#let t2 = json("../task2-particle-mesh.ipynb")
|
#let t2 = json("../task2-particle-mesh.ipynb")
|
||||||
|
|
||||||
|
|
||||||
|
// Finally - The real content
|
||||||
// Content
|
|
||||||
= N-body forces and analytical solutions
|
= N-body forces and analytical solutions
|
||||||
|
|
||||||
== Objective
|
== Objective
|
||||||
@ -87,62 +37,188 @@ Implement naive N-body force computation and get an intuition of the challenges:
|
|||||||
- computation time
|
- computation time
|
||||||
- stability
|
- stability
|
||||||
|
|
||||||
$=>$ still useful to compute basic quantities of the system, but too limited for large systems or the dynamical evolution of the system
|
$==>$ 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
|
== Overview - the system
|
||||||
Get a feel for the particles and their distribution. [Code at @task1:plot_particle_distribution[]]
|
Get a feel for the particles and their distribution. [#link(<task1:plot_particle_distribution>)[code]]
|
||||||
#image_cell(t1, "plot_particle_distribution")
|
|
||||||
|
|
||||||
#code_cell(t1, "plotting")
|
|
||||||
|
|
||||||
== Inspecting the data
|
|
||||||
#code_cell(t2, "plotting")
|
|
||||||
|
|
||||||
#image_cell(t2, "plotting")
|
#columns(2)[
|
||||||
|
#helpers.image_cell(t1, "plot_particle_distribution")
|
||||||
|
Note: for visibility the outer particles are not shown.
|
||||||
|
#colbreak()
|
||||||
|
The system at hand is characterized by:
|
||||||
|
- $N ~ 10^4$ stars
|
||||||
|
- a _spherical_ distribution
|
||||||
|
|
||||||
|
$==>$ treat the system as a *globular cluster*
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
== Density
|
== Density
|
||||||
Some images about the density
|
We compare the computed density with the analytical model provided by the _Hernquist_ model:
|
||||||
|
|
||||||
|
#grid(
|
||||||
|
columns: (1fr, 2fr),
|
||||||
|
inset: 0.5em,
|
||||||
|
block[
|
||||||
|
$
|
||||||
|
rho(r) = M/(2 pi) a / (r dot (r + a)^3)
|
||||||
|
$
|
||||||
|
where we infer $a$ from the half-mass radius:
|
||||||
|
$
|
||||||
|
r_"hm" = (1 + sqrt(2)) dot a
|
||||||
|
$
|
||||||
|
|
||||||
|
#text(size: 0.6em)[
|
||||||
|
Density sampling [#link(<task1:function_density_distribution>)[code]];
|
||||||
|
]
|
||||||
|
],
|
||||||
|
block[
|
||||||
|
#helpers.image_cell(t1, "plot_density_distribution")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#block(
|
||||||
|
height: 1fr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
== Force computation
|
||||||
|
// N Body and variations
|
||||||
|
#grid(
|
||||||
|
columns: (2fr, 1fr),
|
||||||
|
inset: 0.5em,
|
||||||
|
block[
|
||||||
|
#helpers.image_cell(t1, "plot_force_radial")
|
||||||
|
// The radial force is computed as the sum of the forces of all particles in the system.
|
||||||
|
#text(size: 0.6em)[
|
||||||
|
Analytical force [#link(<task1:function_analytical_forces>)[code]];
|
||||||
|
$N^2$ force [#link(<task1:function_n2_forces>)[code]];
|
||||||
|
$epsilon$ computation [#link(<task1:function_interparticle_distance>)[code]];
|
||||||
|
]
|
||||||
|
],
|
||||||
|
block[
|
||||||
|
Discussion:
|
||||||
|
- the analytical method replicates the behavior accurately
|
||||||
|
- at small softenings the $N^2$ method has noisy artifacts
|
||||||
|
- a $1 dot epsilon$ softening is a good compromise between accuracy and stability
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
== N Body and variations
|
|
||||||
sdsd
|
|
||||||
|
|
||||||
== Relaxation
|
== Relaxation
|
||||||
sd
|
Relaxation [#link(<task1:compute_relaxation_time>)[code]]:
|
||||||
|
// #helpers.code_cell(t1, "compute_relaxation_time")
|
||||||
|
|
||||||
|
|
||||||
= Default Styling in diatypst
|
Discussion!
|
||||||
|
|
||||||
== 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
|
= Particle Mesh
|
||||||
|
|
||||||
```python
|
== Overview - the system
|
||||||
// Example Code
|
#page(
|
||||||
print("Hello World!")
|
columns: 2
|
||||||
```
|
)[
|
||||||
|
#helpers.image_cell(t2, "plot_particle_distribution")
|
||||||
Lists have their marker respect the `title-color`
|
|
||||||
|
|
||||||
#columns(2)[
|
|
||||||
- A
|
|
||||||
- AAA
|
|
||||||
- B
|
|
||||||
#colbreak()
|
|
||||||
1. AAA
|
|
||||||
2. BBB
|
|
||||||
3. CCC
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
== Force computation
|
||||||
|
#helpers.code_reference_cell(t2, "function_mesh_force")
|
||||||
|
|
||||||
|
#helpers.image_cell(t2, "plot_force_radial")
|
||||||
|
|
||||||
|
#grid(
|
||||||
|
columns: (2fr, 1fr),
|
||||||
|
inset: 0.5em,
|
||||||
|
block[
|
||||||
|
#helpers.image_cell(t2, "plot_force_radial_single")
|
||||||
|
// The radial force is computed as the sum of the forces of all particles in the system.
|
||||||
|
#text(size: 0.6em)[
|
||||||
|
$N^2$ force [#link(<task1:function_n2_forces>)[code]];
|
||||||
|
$epsilon$ computation [#link(<task1:function_interparticle_distance>)[code]];
|
||||||
|
Mesh force [#link(<task2:function_mesh_force>)[code]];
|
||||||
|
]
|
||||||
|
],
|
||||||
|
block[
|
||||||
|
Discussion:
|
||||||
|
- using the (established) baseline of $N^2$ with $1 dot epsilon$ softening
|
||||||
|
- small grids are stable but inaccurate at the center
|
||||||
|
- very large grids have issues with overdiscretization
|
||||||
|
|
||||||
|
$==> 75 times 75 times 75$ as a good compromise
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
== Time integration
|
||||||
|
=== Runge-Kutta
|
||||||
|
#helpers.code_reference_cell(t2, "function_runge_kutta")
|
||||||
|
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
=== Results
|
||||||
|
#align(center, block(
|
||||||
|
height: 1fr,
|
||||||
|
)[
|
||||||
|
#helpers.image_cell(t2, "plot_system_evolution")
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
== Particle mesh solver
|
||||||
|
sdlsd
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
= Appendix - Code <appendix>
|
||||||
|
|
||||||
= Appendix - Code
|
|
||||||
== Code
|
== Code
|
||||||
|
#helpers.code_cell(t1, "plot_particle_distribution")
|
||||||
<task1:plot_particle_distribution>
|
<task1:plot_particle_distribution>
|
||||||
#code_cell(t1, "plot_particle_distribution")
|
|
||||||
|
#pagebreak(weak: true)
|
||||||
|
|
||||||
|
#helpers.code_reference_cell(t1, "function_density_distribution")
|
||||||
|
<task1:function_density_distribution>
|
||||||
|
|
||||||
|
#pagebreak(weak: true)
|
||||||
|
|
||||||
|
#helpers.code_reference_cell(t1, "function_analytical_forces")
|
||||||
|
<task1:function_analytical_forces>
|
||||||
|
|
||||||
|
#pagebreak(weak: true)
|
||||||
|
|
||||||
|
#helpers.code_reference_cell(t1, "function_n2_forces")
|
||||||
|
<task1:function_n2_forces>
|
||||||
|
|
||||||
|
#pagebreak(weak: true)
|
||||||
|
|
||||||
|
#helpers.code_reference_cell(t1, "function_interparticle_distance")
|
||||||
|
<task1:function_interparticle_distance>
|
||||||
|
|
||||||
|
#pagebreak(weak: true)
|
||||||
|
|
||||||
|
#helpers.code_cell(t1, "compute_relaxation_time")
|
||||||
|
<task1:compute_relaxation_time>
|
||||||
|
|
||||||
|
#pagebreak(weak: true)
|
||||||
|
|
||||||
|
#helpers.code_reference_cell(t2, "function_mesh_force")
|
||||||
|
<task2:function_mesh_force>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#context {
|
||||||
|
counter(page).update(locate(<appendix>).page())
|
||||||
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -29,7 +29,8 @@ def n_body_forces(particles: np.ndarray, G: float, softening: float = 0):
|
|||||||
|
|
||||||
# add softening to the denominator
|
# add softening to the denominator
|
||||||
r_adjusted = r**2 + softening**2
|
r_adjusted = r**2 + softening**2
|
||||||
# usually with a square root: r' = sqrt(r^2 + softening^2) and then cubed, but we combine that below
|
# usually with a square root: r' = sqrt(r^2 + softening^2)
|
||||||
|
# and then cubed, but we combine that below
|
||||||
|
|
||||||
# the numerator is tricky:
|
# the numerator is tricky:
|
||||||
# m is a list of scalars and r_vec is a list of vectors (2D array)
|
# m is a list of scalars and r_vec is a list of vectors (2D array)
|
||||||
|
@ -81,9 +81,12 @@ def to_particles_3d(y: np.ndarray) -> np.ndarray:
|
|||||||
return y
|
return y
|
||||||
|
|
||||||
|
|
||||||
def runge_kutta_4(y0 : np.ndarray, t : float, f, dt : float):
|
def runge_kutta_4(y: np.ndarray, t: float, f: callable, dt: float):
|
||||||
k1 = f(y0, t)
|
"""
|
||||||
k2 = f(y0 + k1/2 * dt, t + dt/2)
|
Runge-Kutta 4th order integrator.
|
||||||
k3 = f(y0 + k2/2 * dt, t + dt/2)
|
"""
|
||||||
k4 = f(y0 + k3 * dt, t + dt)
|
k1 = f(y, t)
|
||||||
return y0 + (k1 + 2*k2 + 2*k3 + k4)/6 * dt
|
k2 = f(y + k1/2 * dt, t + dt/2)
|
||||||
|
k3 = f(y + k2/2 * dt, t + dt/2)
|
||||||
|
k4 = f(y + k3 * dt, t + dt)
|
||||||
|
return y + (k1 + 2*k2 + 2*k3 + k4)/6 * dt
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from astropy import units as u
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
def density_distribution(r_bins: np.ndarray, particles: np.ndarray, ret_error: bool = False):
|
def density_distribution(r_bins: np.ndarray, particles: np.ndarray, ret_error: bool = False):
|
||||||
"""
|
"""
|
||||||
@ -146,7 +149,8 @@ def total_energy(particles: np.ndarray):
|
|||||||
return ke + pe
|
return ke + pe
|
||||||
|
|
||||||
|
|
||||||
def particles_plot_3d(particles: np.ndarray, title: str = "Particle distribution (3D)"):
|
|
||||||
|
def particles_plot_3d(positions: np.ndarray, masses: np.ndarray, title: str = "Particle distribution (3D)"):
|
||||||
"""
|
"""
|
||||||
Plots a 3D scatter plot of a set of particles.
|
Plots a 3D scatter plot of a set of particles.
|
||||||
Assumes that the particles array has the shape:
|
Assumes that the particles array has the shape:
|
||||||
@ -154,48 +158,54 @@ def particles_plot_3d(particles: np.ndarray, title: str = "Particle distribution
|
|||||||
- or 7 columns: x, y, z, vx, vy, vz, m
|
- or 7 columns: x, y, z, vx, vy, vz, m
|
||||||
Colormap is the mass of the particles.
|
Colormap is the mass of the particles.
|
||||||
"""
|
"""
|
||||||
if particles.shape[1] == 4:
|
x, y, z = positions[:, 0], positions[:, 1], positions[:, 2]
|
||||||
x, y, z, m = particles[:, 0], particles[:, 1], particles[:, 2], particles[:, 3]
|
|
||||||
c = m
|
|
||||||
elif particles.shape[1] == 7:
|
|
||||||
x, y, z, m = particles[:, 0], particles[:, 1], particles[:, 2], particles[:, 6]
|
|
||||||
c = m
|
|
||||||
else:
|
|
||||||
raise ValueError("Particles array must have 4 or 7 columns")
|
|
||||||
|
|
||||||
fig = plt.figure()
|
fig = plt.figure()
|
||||||
plt.title(title)
|
fig.suptitle(title)
|
||||||
ax = fig.add_subplot(111, projection='3d')
|
ax = fig.add_subplot(111, projection='3d')
|
||||||
ax.scatter(particles[:,0], particles[:,1], particles[:,2], cmap='viridis', c=particles[:,3])
|
sc = ax.scatter(x, y, z, cmap='coolwarm', c=masses)
|
||||||
|
cbar = plt.colorbar(sc, ax=ax, pad=0.1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cbar.set_label(f'Mass [{masses.unit:latex}]')
|
||||||
|
ax.set_xlabel(f'x [{x.unit:latex}]')
|
||||||
|
ax.set_ylabel(f'y [{x.unit:latex}]')
|
||||||
|
ax.set_zlabel(f'z [{x.unit:latex}]')
|
||||||
|
except AttributeError:
|
||||||
|
cbar.set_label('Mass')
|
||||||
|
ax.set_xlabel('x')
|
||||||
|
ax.set_ylabel('y')
|
||||||
|
ax.set_zlabel('z')
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
logger.debug("3D scatter plot with mass colormap")
|
logger.debug("3D scatter plot with mass colormap")
|
||||||
|
|
||||||
|
|
||||||
def particles_plot_2d(particles: np.ndarray, title: str = "Flattened distribution (along z)"):
|
|
||||||
|
def particles_plot_2d(particles: np.ndarray, title: str = "Flattened distribution (along z)", ax = None):
|
||||||
"""
|
"""
|
||||||
Plots a 2 colormap of a set of particles, flattened in the z direction.
|
Plots a 2 colormap of a set of particles, flattened in the z direction.
|
||||||
Assumes that the particles array has the shape:
|
Assumes that the particles array has the shape:
|
||||||
- either 4 columns: x, y, z, m
|
- either 3 columns: x, y, z
|
||||||
|
- or 4 columns: x, y, z, m
|
||||||
- or 7 columns: x, y, z, vx, vy, vz, m
|
- or 7 columns: x, y, z, vx, vy, vz, m
|
||||||
"""
|
"""
|
||||||
if particles.shape[1] == 4:
|
if particles.shape[1] == 3:
|
||||||
|
x, y, z = particles[:, 0], particles[:, 1], particles[:, 2]
|
||||||
|
elif particles.shape[1] == 4:
|
||||||
x, y, z, m = particles[:, 0], particles[:, 1], particles[:, 2], particles[:, 3]
|
x, y, z, m = particles[:, 0], particles[:, 1], particles[:, 2], particles[:, 3]
|
||||||
c = m
|
|
||||||
elif particles.shape[1] == 7:
|
elif particles.shape[1] == 7:
|
||||||
x, y, z, m = particles[:, 0], particles[:, 1], particles[:, 2], particles[:, 6]
|
x, y, z, m = particles[:, 0], particles[:, 1], particles[:, 2], particles[:, 6]
|
||||||
c = m
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("Particles array must have 4 or 7 columns")
|
raise ValueError("Particles array must have 3, 4 or 7 columns")
|
||||||
|
|
||||||
# plt.figure()
|
if ax is None:
|
||||||
# plt.title(title)
|
plt.figure()
|
||||||
# plt.scatter(x, y, c=range(particles.shape[0]))
|
plt.title(title)
|
||||||
# plt.colorbar()
|
plt.hist2d(x, y, bins=100, cmap='coolwarm')
|
||||||
# plt.show()
|
cbar = plt.colorbar()
|
||||||
|
cbar.set_label(f'Particle count')
|
||||||
|
|
||||||
# or as a discrete heatmap
|
plt.show()
|
||||||
plt.figure()
|
else:
|
||||||
plt.title(title)
|
ax.hist2d(x, y, bins=100, cmap='coolwarm')
|
||||||
plt.hist2d(x, y, bins=100, cmap='viridis')
|
|
||||||
plt.colorbar()
|
|
||||||
plt.show()
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user