archeo 2.0.0.dev6__tar.gz → 2.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. {archeo-2.0.0.dev6 → archeo-2.0.1}/PKG-INFO +1 -1
  2. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/generic.py +6 -4
  3. archeo-2.0.1/archeo/constants/bayesian.py +3 -0
  4. archeo-2.0.1/archeo/constants/physics.py +61 -0
  5. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/physics/black_hole.py +2 -1
  6. archeo-2.0.1/archeo/postprocessing/eval_utils/bias.py +13 -0
  7. archeo-2.0.1/archeo/postprocessing/eval_utils/kl.py +26 -0
  8. archeo-2.0.1/archeo/postprocessing/evaluation.py +29 -0
  9. archeo-2.0.1/archeo/utils/__init__.py +0 -0
  10. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/visualization/base.py +2 -2
  11. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/visualization/estimation.py +5 -5
  12. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo.egg-info/PKG-INFO +1 -1
  13. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo.egg-info/SOURCES.txt +4 -0
  14. {archeo-2.0.0.dev6 → archeo-2.0.1}/pyproject.toml +2 -2
  15. archeo-2.0.0.dev6/archeo/constants/bayesian.py +0 -3
  16. archeo-2.0.0.dev6/archeo/constants/physics.py +0 -70
  17. {archeo-2.0.0.dev6 → archeo-2.0.1}/LICENSE +0 -0
  18. {archeo-2.0.0.dev6 → archeo-2.0.1}/README.md +0 -0
  19. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/__init__.py +0 -0
  20. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/__main__.py +0 -0
  21. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/__init__.py +0 -0
  22. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/ancestral_posterior.py +0 -0
  23. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/importance_sampling/__init__.py +0 -0
  24. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/importance_sampling/bayes_factor_curve.py +0 -0
  25. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/__init__.py +0 -0
  26. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/assume_independence.py +0 -0
  27. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/base.py +0 -0
  28. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/bayesian/importance_sampling/resampler/interface.py +0 -0
  29. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/constants/__init__.py +0 -0
  30. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/constants/enum.py +0 -0
  31. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/__init__.py +0 -0
  32. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/annotation.py +0 -0
  33. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/bayesian/bayes_factor.py +0 -0
  34. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/distribution.py +0 -0
  35. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/math.py +0 -0
  36. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/physics/__init__.py +0 -0
  37. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/physics/binary.py +0 -0
  38. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/physics/mahapatra.py +0 -0
  39. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/physics/simulation.py +0 -0
  40. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/data_structures/visualization.py +0 -0
  41. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/postprocessing/__init__.py +0 -0
  42. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/postprocessing/dataframe.py +0 -0
  43. {archeo-2.0.0.dev6/archeo/preset/forward → archeo-2.0.1/archeo/postprocessing/eval_utils}/__init__.py +0 -0
  44. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/preset/__init__.py +0 -0
  45. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/preset/cli.py +0 -0
  46. {archeo-2.0.0.dev6/archeo/simulation → archeo-2.0.1/archeo/preset/forward}/__init__.py +0 -0
  47. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/preset/forward/compute_bayes_factor_curve.py +0 -0
  48. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/preset/simulation/__init__.py +0 -0
  49. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/preset/simulation/agnostic.py +0 -0
  50. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/preset/simulation/n_generation.py +0 -0
  51. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/preset/simulation/second_generation.py +0 -0
  52. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/py.typed +0 -0
  53. {archeo-2.0.0.dev6/archeo/utils → archeo-2.0.1/archeo/simulation}/__init__.py +0 -0
  54. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/simulation/simulate_merger.py +0 -0
  55. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/utils/decorator.py +0 -0
  56. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/utils/env.py +0 -0
  57. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/utils/fs.py +0 -0
  58. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/utils/logger.py +0 -0
  59. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/utils/parallel.py +0 -0
  60. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/version.py +0 -0
  61. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/visualization/__init__.py +0 -0
  62. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/visualization/animation.py +0 -0
  63. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo/visualization/distribution.py +0 -0
  64. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo.egg-info/dependency_links.txt +0 -0
  65. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo.egg-info/requires.txt +0 -0
  66. {archeo-2.0.0.dev6 → archeo-2.0.1}/archeo.egg-info/top_level.txt +0 -0
  67. {archeo-2.0.0.dev6 → archeo-2.0.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: archeo
3
- Version: 2.0.0.dev6
3
+ Version: 2.0.1
4
4
  Summary: Bayesian framework for inferring natal kick, ancestral masses and spins of black holes.
5
5
  Author-email: wyhwong <wyhwong@link.cuhk.edu.hk>
6
6
  License-Expression: MIT
@@ -48,8 +48,9 @@ class ISDataGeneric(ImportanceSamplingDataBase):
48
48
  weights_matrix = self._safe_divide(1.0, prior_hist)
49
49
 
50
50
  def _get_pdf(row: pd.Series):
51
- idx = tuple(np.searchsorted(edges[col], row[col], side="right") - 1 for col in self.common_columns)
52
- return weights_matrix[idx]
51
+ indices = tuple(np.searchsorted(edges[col], row[col], side="right") - 1 for col in self.common_columns)
52
+ indices = tuple(max(0, min(weights_matrix.shape[i] - 1, idx)) for i, idx in enumerate(indices))
53
+ return weights_matrix[indices]
53
54
 
54
55
  weights = self.posterior_samples.apply(_get_pdf, axis=1)
55
56
 
@@ -73,8 +74,9 @@ class ISDataGeneric(ImportanceSamplingDataBase):
73
74
  weights_matrix = self._safe_divide(new_prior_hist, prior_hist)
74
75
 
75
76
  def _get_pdf(row: pd.Series):
76
- idx = tuple(np.searchsorted(edges[col], row[col], side="right") - 1 for col in self.common_columns)
77
- return weights_matrix[idx]
77
+ indices = tuple(np.searchsorted(edges[col], row[col], side="right") - 1 for col in self.common_columns)
78
+ indices = tuple(max(0, min(weights_matrix.shape[i] - 1, idx)) for i, idx in enumerate(indices))
79
+ return weights_matrix[indices]
78
80
 
79
81
  return self.posterior_samples.apply(_get_pdf, axis=1)
80
82
 
@@ -0,0 +1,3 @@
1
+ DEFAULT_BINSIZE_MASS: float = 2.0
2
+ DEFAULT_BINSIZE_SPIN: float = 0.1
3
+ ASSUME_PARAMETER_INDEPENDENCE: bool = False
@@ -0,0 +1,61 @@
1
+ import enum
2
+ import os
3
+
4
+ import pandas as pd
5
+ from pydantic import BaseModel, NonNegativeFloat
6
+
7
+
8
+ SPEED_OF_LIGHT = 299792.458 # km/s
9
+ BH_MASS_LB: float = float(os.environ.get("BH_MASS_LB", 5.0)) # Solar masses
10
+ PISN_LB: float = float(os.environ.get("PISN_LB", 65.0)) # Solar masses
11
+ PISN_UB: float = float(os.environ.get("PISN_UB", 130.0)) # Solar masses
12
+
13
+
14
+ class _TypicalHostEscapeVelocityMeta(BaseModel):
15
+ v_esc: NonNegativeFloat # km s^-1
16
+ short: str
17
+ latex: str
18
+
19
+
20
+ class TypicalHostEscapeVelocity(enum.Enum):
21
+ """Escape velocity (units: km s^-1)."""
22
+
23
+ GLOBULAR_CLUSTER = _TypicalHostEscapeVelocityMeta(v_esc=50.0, short="GC", latex=r"$v_{esc, GC}$")
24
+ MILKY_WAY = _TypicalHostEscapeVelocityMeta(v_esc=600.0, short="MW", latex=r"$v_{esc, MW}$")
25
+ NUCLEAR_STAR_CLUSTER = _TypicalHostEscapeVelocityMeta(v_esc=1500.0, short="NSC", latex=r"$v_{esc, NSC}$")
26
+ ELLIPTICAL_GALAXY = _TypicalHostEscapeVelocityMeta(v_esc=2500.0, short="EG", latex=r"$v_{esc, EG}$")
27
+
28
+ @property
29
+ def v_esc(self) -> float:
30
+ return self.value.v_esc
31
+
32
+ @property
33
+ def short(self) -> str:
34
+ return self.value.short
35
+
36
+ @property
37
+ def latex(self) -> str:
38
+ return self.value.latex
39
+
40
+ def compute_p2g(
41
+ self,
42
+ df: pd.DataFrame,
43
+ kf_col: str = "k_f",
44
+ m1_col: str = "m_1",
45
+ m2_col: str = "m_2",
46
+ ) -> float:
47
+ """Probability of being 2nd-generation under this escape velocity."""
48
+
49
+ if df.empty:
50
+ return 0.0
51
+
52
+ mask = (df[kf_col] <= self.v_esc) & (df[m1_col] <= PISN_LB) & (df[m2_col] <= PISN_LB)
53
+ return mask.mean() * 100.0
54
+
55
+ @classmethod
56
+ def latex_to_values(cls) -> dict[str, float]:
57
+ return {m.latex: m.v_esc for m in cls}
58
+
59
+ @classmethod
60
+ def short_to_values(cls) -> dict[str, float]:
61
+ return {m.short: m.v_esc for m in cls}
@@ -4,6 +4,7 @@ import numpy as np
4
4
  import pandas as pd
5
5
  from pydantic import BaseModel, NonNegativeFloat, PositiveFloat, field_validator
6
6
 
7
+ from archeo.constants.physics import BH_MASS_LB, PISN_LB
7
8
  from archeo.data_structures.annotation import Distribution
8
9
  from archeo.data_structures.distribution import Uniform
9
10
 
@@ -35,7 +36,7 @@ BlackHoles: TypeAlias = list[BlackHole]
35
36
  class BlackHoleGenerator(BaseModel, frozen=True):
36
37
  """Black hole generator data class."""
37
38
 
38
- mass_distribution: Distribution = Uniform(low=5, high=65)
39
+ mass_distribution: Distribution = Uniform(low=BH_MASS_LB, high=PISN_LB)
39
40
  spin_magnitude_distribution: Distribution = Uniform(low=0, high=1)
40
41
  phi_distribution: Distribution = Uniform(low=0, high=2 * np.pi)
41
42
  theta_distribution: Distribution = Uniform(low=0, high=np.pi)
@@ -0,0 +1,13 @@
1
+ import pandas as pd
2
+
3
+
4
+ def compute_bias_for_remnant_spin(df_samples: pd.DataFrame) -> float:
5
+ """Compute the bias in remnant spin for a given set of samples."""
6
+
7
+ return (df_samples["a_f"] - df_samples["spin_measure"]).sum() / df_samples.shape[0]
8
+
9
+
10
+ def compute_bias_for_remnant_mass(df_samples: pd.DataFrame) -> float:
11
+ """Compute the bias in remnant mass for a given set of samples."""
12
+
13
+ return (df_samples["m_f"] - df_samples["mass_measure"]).sum() / df_samples.shape[0]
@@ -0,0 +1,26 @@
1
+ import numpy as np
2
+
3
+
4
+ def compute_kl_divergence_from_samples(X: np.ndarray, Y: np.ndarray) -> float:
5
+ """
6
+ Compute KL(N0 || N1) where N0 and N1 are multivariate Gaussians.
7
+ X and Y should be 2D arrays of shape (n, d) and (m, d) respectively.
8
+ """
9
+
10
+ if X.shape[1] != Y.shape[1]:
11
+ raise ValueError("X and Y must have the same dimension.")
12
+
13
+ k = X.shape[1]
14
+ mu0 = X.mean(axis=0)
15
+ mu1 = Y.mean(axis=0)
16
+ S0 = np.cov(X.T, bias=False)
17
+ S1 = np.cov(Y.T, bias=False)
18
+
19
+ invS1 = np.linalg.inv(S1)
20
+ diff = (mu1 - mu0).reshape(k, 1)
21
+
22
+ term_trace = np.trace(invS1 @ S0)
23
+ term_quad = float((diff.T @ invS1 @ diff)[0, 0])
24
+ term_logdet = np.log(np.linalg.det(S1) / np.linalg.det(S0))
25
+
26
+ return 0.5 * (term_trace + term_quad - k + term_logdet)
@@ -0,0 +1,29 @@
1
+ import pandas as pd
2
+
3
+ from archeo.postprocessing.eval_utils.bias import compute_bias_for_remnant_mass, compute_bias_for_remnant_spin
4
+ from archeo.postprocessing.eval_utils.kl import compute_kl_divergence_from_samples
5
+
6
+
7
+ def evaluate_ancestral_inference(df_samples: pd.DataFrame) -> dict[str, float]:
8
+ """Evaluate the validity of the estimated posterior samples by computing:
9
+ 1. Estimation bias: the average difference between the estimated parameters and the true parameters
10
+ 2. KL divergence between the Gaussian fitted to the estimated samples and the Gaussian fitted to
11
+ the true parameters.
12
+ """
13
+
14
+ bias_spin = compute_bias_for_remnant_spin(df_samples)
15
+ bias_mass = compute_bias_for_remnant_mass(df_samples)
16
+
17
+ mask = df_samples[["a_f", "m_f"]].notna().all(axis=1)
18
+ # Here we need to apply the mask to the inferred samples.
19
+ # The reason is that there could be no similar samples
20
+ # in the ancestral prior, resulting in NaN values.
21
+ # Those NaN values would cause the KL divergence computation to fail.
22
+ kl_div = compute_kl_divergence_from_samples(
23
+ df_samples.loc[mask, ["a_f", "m_f"]].values, df_samples[["spin_measure", "mass_measure"]].values
24
+ )
25
+ return {
26
+ "bias_spin": bias_spin,
27
+ "bias_mass": bias_mass,
28
+ "kl_divergence": kl_div,
29
+ }
File without changes
@@ -5,7 +5,7 @@ import matplotlib.pyplot as plt
5
5
  import numpy as np
6
6
  import pandas as pd
7
7
 
8
- from archeo.constants.physics import EscapeVelocity
8
+ from archeo.constants.physics import TypicalHostEscapeVelocity
9
9
  from archeo.data_structures.visualization import Labels, Padding
10
10
  from archeo.utils.fs import check_and_create_dir
11
11
  from archeo.utils.logger import get_logger
@@ -112,7 +112,7 @@ def add_escape_velocity(ax, v_max: float, y_max: float, log_xscale: bool = False
112
112
 
113
113
  colors = iter(mcolors.TABLEAU_COLORS.keys())
114
114
  # Plot vertical lines and labels (escape velocities)
115
- for label, v_esc in EscapeVelocity.to_vlines().items():
115
+ for label, v_esc in TypicalHostEscapeVelocity.latex_to_values().items():
116
116
  # Skip if out of scope
117
117
  if v_esc > v_max:
118
118
  return
@@ -8,7 +8,7 @@ import numpy as np
8
8
  import pandas as pd
9
9
  import seaborn as sns
10
10
 
11
- from archeo.constants.physics import EscapeVelocity
11
+ from archeo.constants.physics import PISN_LB, PISN_UB, TypicalHostEscapeVelocity
12
12
  from archeo.data_structures.visualization import Labels, Padding
13
13
  from archeo.utils.logger import get_logger
14
14
  from archeo.visualization import base
@@ -87,8 +87,8 @@ def _add_pisn_gap(ax, color: str) -> None:
87
87
  color (str): Color.
88
88
  """
89
89
 
90
- ax.axvline(65.0, color=color, linewidth=0.9, linestyle="--", label="PISN Gap")
91
- ax.axvline(130.0, color=color, linewidth=0.9, linestyle="--")
90
+ ax.axvline(PISN_LB, color=color, linewidth=0.9, linestyle="--", label="PISN Gap")
91
+ ax.axvline(PISN_UB, color=color, linewidth=0.9, linestyle="--")
92
92
 
93
93
 
94
94
  def corner_estimates( # pylint: disable=dangerous-default-value
@@ -235,7 +235,7 @@ def second_generation_probability_curve(
235
235
  # Calculate the CDF
236
236
  y = []
237
237
  for kick in x:
238
- df_samples = df.loc[(df["k_f"] <= kick) & (df["m_1"] <= 65) & (df["m_2"] <= 65)]
238
+ df_samples = df.loc[(df["k_f"] <= kick) & (df["m_1"] <= PISN_LB) & (df["m_2"] <= PISN_LB)]
239
239
  if df_samples.empty:
240
240
  y.append(0.0)
241
241
  else:
@@ -363,7 +363,7 @@ def table_estimates(
363
363
  data = {
364
364
  "": dfs.keys(),
365
365
  "Recovery Rate": [df["m_1"].notna().sum() / df.shape[0] for df in dfs.values()],
366
- **{f"p2g_{v_esc.short()}": [v_esc.compute_p2g(df) for df in dfs.values()] for v_esc in EscapeVelocity},
366
+ **{f"p2g_{v_esc.short}": [v_esc.compute_p2g(df) for df in dfs.values()] for v_esc in TypicalHostEscapeVelocity},
367
367
  }
368
368
 
369
369
  for col, name in col_to_names.items():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: archeo
3
- Version: 2.0.0.dev6
3
+ Version: 2.0.1
4
4
  Summary: Bayesian framework for inferring natal kick, ancestral masses and spins of black holes.
5
5
  Author-email: wyhwong <wyhwong@link.cuhk.edu.hk>
6
6
  License-Expression: MIT
@@ -36,6 +36,10 @@ archeo/data_structures/physics/mahapatra.py
36
36
  archeo/data_structures/physics/simulation.py
37
37
  archeo/postprocessing/__init__.py
38
38
  archeo/postprocessing/dataframe.py
39
+ archeo/postprocessing/evaluation.py
40
+ archeo/postprocessing/eval_utils/__init__.py
41
+ archeo/postprocessing/eval_utils/bias.py
42
+ archeo/postprocessing/eval_utils/kl.py
39
43
  archeo/preset/__init__.py
40
44
  archeo/preset/cli.py
41
45
  archeo/preset/forward/__init__.py
@@ -16,7 +16,7 @@ classifiers = [
16
16
  "Operating System :: OS Independent"
17
17
  ]
18
18
  keywords = ["black-holes", "gravitational-waves", "black-hole-archeology"]
19
- version = "2.0.0.dev6"
19
+ version = "2.0.1"
20
20
  readme = "README.md"
21
21
  requires-python = ">=3.11,<3.14"
22
22
  dependencies = [
@@ -43,7 +43,7 @@ documentation = "https://wyhwong.github.io/archeo/"
43
43
 
44
44
  [dependency-groups]
45
45
  dev = [
46
- "ipykernel>=6.9.2, <7.0.0",
46
+ "ipykernel>=7.2.0",
47
47
  "pre-commit>=4.5.1",
48
48
  "pyarrow>=23.0.1",
49
49
  "pyinstrument>=5.1.2",
@@ -1,3 +0,0 @@
1
- DEFAULT_BINSIZE_MASS = 2.0
2
- DEFAULT_BINSIZE_SPIN = 0.1
3
- ASSUME_PARAMETER_INDEPENDENCE = False
@@ -1,70 +0,0 @@
1
- import enum
2
-
3
- import pandas as pd
4
-
5
-
6
- SPEED_OF_LIGHT = 299792.458 # km/s
7
-
8
-
9
- class EscapeVelocity(enum.Enum):
10
- """Escape velocity (Unit in km s^-1)"""
11
-
12
- GLOBULAR_CLUSTER = 50.0
13
- MILKY_WAY = 600.0
14
- NUCLEAR_STAR_CLUSTER = 1500.0
15
- ELLIPTICAL_GALAXY = 2500.0
16
-
17
- def label(self) -> str:
18
- """Return the escape velocity label"""
19
-
20
- if self is EscapeVelocity.GLOBULAR_CLUSTER:
21
- return "$v_{esc, GC}$"
22
-
23
- if self is EscapeVelocity.MILKY_WAY:
24
- return "$v_{esc, MW}$"
25
-
26
- if self is EscapeVelocity.NUCLEAR_STAR_CLUSTER:
27
- return "$v_{esc, NSC}$"
28
-
29
- if self is EscapeVelocity.ELLIPTICAL_GALAXY:
30
- return "$v_{esc, EG}$"
31
-
32
- raise ValueError(f"Unknown escape velocity {self}")
33
-
34
- def short(self) -> str:
35
- """Return the short name of the escape velocity"""
36
-
37
- if self is EscapeVelocity.GLOBULAR_CLUSTER:
38
- return "GC"
39
-
40
- if self is EscapeVelocity.MILKY_WAY:
41
- return "MW"
42
-
43
- if self is EscapeVelocity.NUCLEAR_STAR_CLUSTER:
44
- return "NSC"
45
-
46
- if self is EscapeVelocity.ELLIPTICAL_GALAXY:
47
- return "EG"
48
-
49
- raise ValueError(f"Unknown escape velocity {self}")
50
-
51
- def compute_p2g(self, df: pd.DataFrame) -> float:
52
- """Return the probability of
53
- the black hole being a 2nd generation black hole
54
- under different escape velocity conditions."""
55
-
56
- if df.empty:
57
- return 0.0
58
-
59
- mask = (df["k_f"] <= self.value) & (df["m_1"] <= 65) & (df["m_2"] <= 65)
60
- return mask.sum() / len(df) * 100.0
61
-
62
- @classmethod
63
- def to_vlines(cls) -> dict[str, float]:
64
- """Return a dictionary for vlines plotting
65
-
66
- Returns:
67
- vlines (Dict[str, float]): The escape velocity vlines.
68
- """
69
-
70
- return {esc_vel.label(): esc_vel.value for esc_vel in cls}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes