Skip to content

Commit d4aa703

Browse files
committed
fix: refactor src
1 parent aa74560 commit d4aa703

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+17774
-1
lines changed

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ commit_subject = "chore(release): {version}"
8787
commit_message = "chore(release): {version}"
8888
tag_format = "v{version}"
8989
no_local_version = true
90-
branch = "dev"
9190

9291
[project.urls]
9392
"Homepage" = "https://github.com/TEAMCompas/COMPAS"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
__all__ = []
3+
4+
__author__ = "Team COMPAS"
5+
__email__ = "compas email"
6+
__uri__ = "https://github.com/TeamCOMPAS/COMPAS"
7+
__license__ = "MIT"
8+
__description__ = "COMPAS"
9+
__copyright__ = "Copyright 2022 COMPAS developers"
10+
__contributors__ = "https://github.com/TeamCOMPAS/COMPAS/graphs/contributors"
11+
__version__ = "0.0.1"
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
#!/usr/bin/env python3
2+
import numpy as np
3+
import h5py as h5
4+
import os
5+
from . import totalMassEvolvedPerZ as MPZ
6+
7+
8+
class COMPASData(object):
9+
def __init__(
10+
self,
11+
path=None,
12+
lazyData=True,
13+
Mlower=None,
14+
Mupper=None,
15+
m2_min=None,
16+
binaryFraction=None,
17+
suppress_reminder=False,
18+
):
19+
self.path = path
20+
if self.path is None:
21+
print("Template COMPASData object created with no data path")
22+
elif not os.path.isfile(path):
23+
raise ValueError( "h5 file not found. Wrong path given? {}".format(path))
24+
25+
# Crucial values to be able to calculate MSSFR
26+
self.metallicityGrid = None
27+
self.metallicitySystems = None
28+
self.delayTimes = None # Myr
29+
# Crucial values I need for selection effects
30+
self.mass1 = None # Msun
31+
self.mass2 = None # Msun
32+
self.DCOmask = None
33+
self.allTypesMask = None
34+
self.BBHmask = None
35+
self.DNSmask = None
36+
self.BHNSmask = None
37+
self.CHE_mask = None
38+
self.CHE_BBHmask = None
39+
self.NonCHE_BBHmask = None
40+
self.initialZ = None
41+
self.sw_weights = None
42+
self.n_systems = None
43+
44+
# Additional arrays that might be nice to store
45+
# to more quickly make some plots.
46+
# If you need more memory might help a tiny bit to not do
47+
self.lazyData = lazyData
48+
self.mChirp = None # Msun
49+
self.q = None
50+
self.optimisticmask = None
51+
52+
# Needed to recover true solar mass evolved
53+
self.Mlower = Mlower # Msun
54+
self.Mupper = Mupper # Msun
55+
self.m2_min = m2_min # Msun
56+
self.binaryFraction = binaryFraction
57+
self.totalMassEvolvedPerZ = None # Msun
58+
self.mass_evolved_per_binary = None # Msun
59+
60+
if not suppress_reminder:
61+
print("ClassCOMPAS: Remember to self.setCOMPASDCOmask()")
62+
print(" then self.setCOMPASData()")
63+
print(" and optionally self.setGridAndMassEvolved() if using a metallicity grid")
64+
65+
def setCOMPASDCOmask(
66+
self, types="BBH", withinHubbleTime=True, pessimistic=True, noRLOFafterCEE=True
67+
):
68+
# By default, we mask for BBHs that merge within a Hubble time, assuming
69+
# the pessimistic CEE prescription (HG donors cannot survive a CEE) and
70+
# not allowing immediate RLOF post-CEE
71+
72+
stellar_type_1, stellar_type_2, hubble_flag, dco_seeds = \
73+
self.get_COMPAS_variables("BSE_Double_Compact_Objects", ["Stellar_Type(1)", "Stellar_Type(2)", "Merges_Hubble_Time", "SEED"])
74+
dco_seeds = dco_seeds.flatten()
75+
76+
if types == "CHE_BBH" or types == "NON_CHE_BBH":
77+
stellar_type_1_zams, stellar_type_2_zams, che_ms_1, che_ms_2, sys_seeds = \
78+
self.get_COMPAS_variables("BSE_System_Parameters", ["Stellar_Type@ZAMS(1)", "Stellar_Type@ZAMS(2)", "CH_on_MS(1)", "CH_on_MS(2)", "SEED"])
79+
80+
che_mask = np.logical_and.reduce((stellar_type_1_zams == 16, stellar_type_2_zams == 16, che_ms_1 == True, che_ms_2 == True))
81+
che_seeds = sys_seeds[()][che_mask]
82+
83+
self.CHE_mask = np.in1d(dco_seeds, che_seeds) if types == "CHE_BBH" or types == "NON_CHE_BBH" else np.repeat(False, len(dco_seeds))
84+
85+
# if user wants to mask on Hubble time use the flag, otherwise just set all to True, use astype(bool) to set masks to bool type
86+
hubble_mask = hubble_flag.astype(bool) if withinHubbleTime else np.repeat(True, len(dco_seeds))
87+
88+
# mask on stellar types (where 14=BH and 13=NS), BHNS can be BHNS or NSBH
89+
type_masks = {
90+
"all": np.repeat(True, len(dco_seeds)),
91+
"BBH": np.logical_and(stellar_type_1 == 14, stellar_type_2 == 14),
92+
"BHNS": np.logical_or(np.logical_and(stellar_type_1 == 14, stellar_type_2 == 13), np.logical_and(stellar_type_1 == 13, stellar_type_2 == 14)),
93+
"BNS": np.logical_and(stellar_type_1 == 13, stellar_type_2 == 13),
94+
}
95+
type_masks["CHE_BBH"] = np.logical_and(self.CHE_mask, type_masks["BBH"]) if types == "CHE_BBH" else np.repeat(False, len(dco_seeds))
96+
type_masks["NON_CHE_BBH"] = np.logical_and(np.logical_not(self.CHE_mask), type_masks["BBH"]) if types == "NON_CHE_BBH" else np.repeat(True, len(dco_seeds))
97+
98+
# if the user wants to make RLOF or optimistic CEs
99+
if noRLOFafterCEE or pessimistic:
100+
101+
# get the flags and unique seeds from the Common Envelopes file
102+
ce_seeds = self.get_COMPAS_variables("BSE_Common_Envelopes", "SEED")
103+
dco_from_ce = np.in1d(ce_seeds, dco_seeds)
104+
dco_ce_seeds = ce_seeds[dco_from_ce]
105+
106+
# if masking on RLOF, get flag and match seeds to dco seeds
107+
if noRLOFafterCEE:
108+
rlof_flag = self.get_COMPAS_variables("BSE_Common_Envelopes", "Immediate_RLOF>CE")[dco_from_ce].astype(bool)
109+
rlof_seeds = np.unique(dco_ce_seeds[rlof_flag])
110+
rlof_mask = np.logical_not(np.in1d(dco_seeds, rlof_seeds))
111+
else:
112+
rlof_mask = np.repeat(True, len(dco_seeds))
113+
114+
# if masking on pessimistic CE, get flag and match seeds to dco seeds
115+
if pessimistic:
116+
pessimistic_flag = self.get_COMPAS_variables("BSE_Common_Envelopes", "Optimistic_CE")[dco_from_ce].astype(bool)
117+
pessimistic_seeds = np.unique(dco_ce_seeds[pessimistic_flag])
118+
pessimistic_mask = np.logical_not(np.in1d(dco_seeds, pessimistic_seeds))
119+
else:
120+
pessimistic_mask = np.repeat(True, len(dco_seeds))
121+
else:
122+
rlof_mask = np.repeat(True, len(dco_seeds))
123+
pessimistic_mask = np.repeat(True, len(dco_seeds))
124+
125+
# create a mask for each dco type supplied
126+
self.DCOmask = type_masks[types] * hubble_mask * rlof_mask * pessimistic_mask
127+
self.BBHmask = type_masks["BBH"] * hubble_mask * rlof_mask * pessimistic_mask
128+
self.BHNSmask = type_masks["BHNS"] * hubble_mask * rlof_mask * pessimistic_mask
129+
self.DNSmask = type_masks["BNS"] * hubble_mask * rlof_mask * pessimistic_mask
130+
self.CHE_BBHmask = type_masks["CHE_BBH"] * hubble_mask * rlof_mask * pessimistic_mask
131+
self.NonCHE_BBHmask = type_masks["NON_CHE_BBH"] * hubble_mask * rlof_mask * pessimistic_mask
132+
self.allTypesMask = type_masks["all"] * hubble_mask * rlof_mask * pessimistic_mask
133+
self.optimisticmask = pessimistic_mask
134+
135+
def setGridAndMassEvolved(self):
136+
# The COMPAS simulation does not evolve all stars
137+
# give me the correction factor for the total mass evolved
138+
# I assume each metallicity has the same limits, and does correction
139+
# factor, but the total mass evolved might be different.
140+
# This does not change when we change types and other masks this is
141+
# general to the entire simulation so calculate once
142+
_, self.totalMassEvolvedPerZ = MPZ.totalMassEvolvedPerZ(
143+
path=self.path,
144+
Mlower=self.Mlower,
145+
Mupper=self.Mupper,
146+
binaryFraction=self.binaryFraction,
147+
)
148+
# Want to recover entire metallicity grid, assume that every metallicity
149+
# evolved shows in all systems again should not change within same run
150+
# so dont redo if we reset the data
151+
Data = h5.File(self.path, "r")
152+
if self.initialZ is None:
153+
self.initialZ = Data["BSE_System_Parameters"]["Metallicity@ZAMS(1)"][()]
154+
self.metallicityGrid = np.unique(self.initialZ)
155+
Data.close()
156+
157+
def setCOMPASData(self):
158+
159+
primary_masses, secondary_masses, formation_times, coalescence_times, dco_seeds = \
160+
self.get_COMPAS_variables("BSE_Double_Compact_Objects", ["Mass(1)", "Mass(2)", "Time", "Coalescence_Time", "SEED"])
161+
162+
initial_seeds, initial_Z = self.get_COMPAS_variables("BSE_System_Parameters", ["SEED", "Metallicity@ZAMS(1)"])
163+
164+
# Get metallicity grid of DCOs
165+
self.seedsDCO = dco_seeds[self.DCOmask]
166+
if self.initialZ is None:
167+
self.initialZ = initial_Z
168+
maskMetallicity = np.in1d(initial_seeds, self.seedsDCO)
169+
self.metallicitySystems = self.initialZ[maskMetallicity]
170+
self.n_systems = len(initial_seeds)
171+
172+
self.delayTimes = np.add(formation_times[self.DCOmask], coalescence_times[self.DCOmask])
173+
self.mass1 = primary_masses[self.DCOmask]
174+
self.mass2 = secondary_masses[self.DCOmask]
175+
176+
# Stuff of data I dont need for integral
177+
# but I might be to laze to read in myself
178+
# and often use. Might turn it of for memory efficiency
179+
if self.lazyData:
180+
self.q = np.divide(self.mass2, self.mass1)
181+
boolq = self.mass2 > self.mass1
182+
self.q[boolq] = np.divide(self.mass1[boolq], self.mass2[boolq])
183+
self.mChirp = np.divide(
184+
(np.multiply(self.mass2, self.mass1) ** (3.0 / 5.0)),
185+
(np.add(self.mass2, self.mass1) ** (1.0 / 5.0)),
186+
)
187+
self.Hubble = self.get_COMPAS_variables("BSE_Double_Compact_Objects", "Merges_Hubble_Time")[self.DCOmask]
188+
189+
def recalculateTrueSolarMassEvolved(self, Mlower, Mupper, binaryFraction):
190+
# Possibility to test assumptions of True solar mass evolved
191+
self.Mlower = Mlower
192+
self.Mupper = Mupper
193+
self.binaryFraction = binaryFraction
194+
_, self.totalMassEvolvedPerZ = MPZ.totalMassEvolvedPerZ(
195+
pathCOMPASh5=self.path,
196+
Mlower=self.Mlower,
197+
Mupper=self.Mupper,
198+
binaryFraction=self.binaryFraction,
199+
)
200+
201+
def get_COMPAS_variables(self, hdf5_file, var_names):
202+
"""
203+
Get a variable or variables from a COMPAS file
204+
205+
Args:
206+
hdf5_file --> [string] Name of HDF5 subfile (e.g. "BSE_Double_Compact_Objects")
207+
var_names --> [string or string list] A variable name or list of variables names to return
208+
209+
Returns:
210+
var_list --> [list of lists] A list of variables (or a single variable if only one name supplied)
211+
"""
212+
# open the COMPAS file
213+
with h5.File(self.path, "r") as compas_file:
214+
# if the list is only a string (i.e. one variable) then don't return a list
215+
if isinstance(var_names, str):
216+
return compas_file[hdf5_file][var_names][...].squeeze()
217+
# else return each variable in a list
218+
else:
219+
return [compas_file[hdf5_file][var_name][...].squeeze() for var_name in var_names]
220+
221+
def set_sw_weights(self, column_name):
222+
""" Set STROOPWAFEL adaptive sampling weights given a column name in the BSE_Double_Compact_Objects file """
223+
if column_name is not None:
224+
self.sw_weights = self.get_COMPAS_variables("BSE_Double_Compact_Objects", column_name)[self.DCOmask]
225+
226+
def find_star_forming_mass_per_binary_sampling(self):
227+
self.mass_evolved_per_binary = MPZ.analytical_star_forming_mass_per_binary_using_kroupa_imf(
228+
m1_max=self.Mupper.value,
229+
m1_min=self.Mlower.value,
230+
m2_min=self.m2_min.value,
231+
fbin=self.binaryFraction,
232+
)
233+
234+

0 commit comments

Comments
 (0)