cogsworth 0.0__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.
cogsworth-0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Tom Wagg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
cogsworth-0.0/PKG-INFO ADDED
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.1
2
+ Name: cogsworth
3
+ Version: 0.0
4
+ Summary: A framework for performing self-consistent population synthesis and orbital integration
5
+ Home-page: https://github.com/TomWagg/cogsworth
6
+ Author: Tom Wagg
7
+ Author-email: tomjwagg@gmail.com
8
+ License: MIT
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Provides-Extra: test
12
+ Provides-Extra: docs
13
+ License-File: LICENSE
14
+
15
+
16
+ <h1 align="center">Cogsworth</h1>
17
+ <p align="center">A Python package for self-consistently performing population synthesis and orbital integration</p>
18
+ <p align="center">
19
+ <a href='https://cogsworth.readthedocs.io/en/latest/?badge=latest'>
20
+ <img src='https://readthedocs.org/projects/cogsworth/badge/?version=latest' alt='Documentation Status' />
21
+ </a>
22
+ <br>
23
+ <img width=500 src="https://user-images.githubusercontent.com/21990332/194442782-49afc013-2c53-4638-bcb7-c0970319c2bd.png">
24
+ </p>
@@ -0,0 +1,10 @@
1
+
2
+ <h1 align="center">Cogsworth</h1>
3
+ <p align="center">A Python package for self-consistently performing population synthesis and orbital integration</p>
4
+ <p align="center">
5
+ <a href='https://cogsworth.readthedocs.io/en/latest/?badge=latest'>
6
+ <img src='https://readthedocs.org/projects/cogsworth/badge/?version=latest' alt='Documentation Status' />
7
+ </a>
8
+ <br>
9
+ <img width=500 src="https://user-images.githubusercontent.com/21990332/194442782-49afc013-2c53-4638-bcb7-c0970319c2bd.png">
10
+ </p>
@@ -0,0 +1,2 @@
1
+ from . import galaxy, kicks, pop, events, classify, observables
2
+ from ._version import __version__
@@ -0,0 +1 @@
1
+ __version__ = "0.0"
@@ -0,0 +1,369 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ import astropy.units as u
4
+ import astropy.constants as const
5
+ import gala.dynamics as gd
6
+
7
+ __all__ = ["determine_final_classes", "list_classes", "get_eddington_rate", "get_eddington_lum",
8
+ "get_schwarzchild_radius", "get_x_ray_lum"]
9
+
10
+
11
+ def determine_final_classes(population=None, bpp=None, bcm=None, kick_info=None, orbits=None, potential=None):
12
+ """Determine the classes of each member of a population at the last point in the evolution (usually
13
+ present day).
14
+
15
+ Either supply a Population class or each individual table separately
16
+
17
+ Parameters
18
+ ----------
19
+ population : :class:`~cogsworth.pop.Population`, optional
20
+ A full population class created from the pop module, by default None
21
+ bpp : :class:`~pandas.DataFrame`
22
+ Evolutionary history of each binary
23
+ bcm : :class:`~pandas.DataFrame`
24
+ Final state of each binary
25
+ initC : :class:`~pandas.DataFrame`
26
+ Initial conditions for each binary
27
+ kick_info : :class:`~pandas.DataFrame`
28
+ Information about the kicks that occur for each binary
29
+ orbits : `list` of :class:`~gala.dynamics.Orbit`
30
+ The orbits of each binary within the galaxy. Disrupted binaries should have two entries
31
+ (for both stars).
32
+ galactic_potential : :class:`~gala.potential.potential.PotentialBase`, optional
33
+ Galactic potential to use for evolving the orbits of binaries
34
+
35
+ Returns
36
+ -------
37
+ classes : :class:`~pandas.DataFrame`
38
+ A DataFrame with a boolean column for each class (see :meth:`list_classes`) and a row for each binary
39
+
40
+ Raises
41
+ ------
42
+ ValueError
43
+ If either `population` is None OR any another parameter is None
44
+ """
45
+ # ensure that there's enough input
46
+ if population is None and (bpp is None or bcm is None or kick_info is None
47
+ or orbits is None or potential is None):
48
+ raise ValueError("Either `population` must be supplied or all other parameters")
49
+
50
+ # split up the input so that I can use a single interface
51
+ if population is not None:
52
+ bpp, bcm, kick_info, orbits, potential = population.bpp, population.bcm, population.kick_info,\
53
+ population.orbits, population.galactic_potential
54
+
55
+ # get the binary indices and also reduce the tables to just the final row in each
56
+ final_bpp = bpp[~bpp.index.duplicated(keep="last")]
57
+ final_bcm = bcm[~bcm.index.duplicated(keep="last")]
58
+ final_kick_info = kick_info.sort_values(["bin_num", "star"]).drop_duplicates(subset="bin_num",
59
+ keep="last")
60
+
61
+ # set up an empty dataframe
62
+ columns = ["dco", "co-1", "co-2", "xrb", "walkaway-t-1", "walkaway-t-2", "runaway-t-1", "runaway-t-2",
63
+ "walkaway-o-1", "walkaway-o-2", "runaway-o-1", "runaway-o-2", "widow-1", "widow-2",
64
+ "stellar-merger-co-1", "stellar-merger-co-2", "pisn-1", "pisn-2"]
65
+ data = np.zeros(shape=(len(final_bpp), len(columns))).astype(bool)
66
+ classes = pd.DataFrame(data=data, columns=columns)
67
+
68
+ # match the index to the final bpp bin_nums
69
+ classes.index = final_bpp["bin_num"].index
70
+
71
+ # check the state of the binary
72
+ bound = final_bpp["sep"] > 0.0
73
+ merger = final_bpp["sep"] == 0.0
74
+
75
+ # HACK: three different markers and only assume it is disrupted if ALL are True
76
+ disrupted = (final_bpp["bin_num"].isin(kick_info[kick_info["disrupted"] == 1.0]["bin_num"].unique())
77
+ & (final_bpp["sep"] < 0.0)
78
+ & final_bpp["bin_num"].isin(bpp[bpp["evol_type"] == 11.0]["bin_num"]))
79
+
80
+ # set some flags for the end conditions of each component
81
+ primary_is_star = final_bpp["kstar_1"] <= 9
82
+ secondary_is_star = final_bpp["kstar_2"] <= 9
83
+ primary_is_bh_ns = final_bpp["kstar_1"].isin([13, 14])
84
+ secondary_is_bh_ns = final_bpp["kstar_2"].isin([13, 14])
85
+
86
+ # check if there was ever a bound CO
87
+ primary_ever_bound_bh_ns = final_bpp["bin_num"].isin(bpp[bpp["kstar_1"].isin([13, 14])
88
+ & bpp["sep"] > 0.0]["bin_num"].unique())
89
+ secondary_ever_bound_bh_ns = final_bpp["bin_num"].isin(bpp[bpp["kstar_2"].isin([13, 14])
90
+ & bpp["sep"] > 0.0]["bin_num"].unique())
91
+
92
+ # create masks based on conditions listed in `list_classes`
93
+ classes["dco"] = bound & primary_is_bh_ns & secondary_is_bh_ns
94
+ classes["co-1"] = ~merger & primary_is_bh_ns
95
+ classes["co-2"] = ~merger & secondary_is_bh_ns
96
+ classes["stellar-merger-co-1"] = merger & primary_is_bh_ns
97
+ classes["stellar-merger-co-2"] = merger & secondary_is_bh_ns
98
+ classes["xrb"] = bound & ((primary_is_bh_ns & secondary_is_star) | (secondary_is_bh_ns & primary_is_star))
99
+
100
+ classes["widow-1"] = ~merger & primary_is_star & secondary_ever_bound_bh_ns
101
+ classes["widow-2"] = ~merger & secondary_is_star & primary_ever_bound_bh_ns
102
+
103
+ classes["pisn-1"] = final_bcm["SN_1"].isin([6, 7])
104
+ classes["pisn-2"] = final_bcm["SN_2"].isin([6, 7])
105
+
106
+ classes["walkaway-t-1"] = disrupted & (final_kick_info["vsys_1_total"] < 30.0) & primary_is_star
107
+ classes["walkaway-t-2"] = disrupted & (final_kick_info["vsys_2_total"] < 30.0) & secondary_is_star
108
+ classes["runaway-t-1"] = disrupted & (final_kick_info["vsys_1_total"] >= 30.0) & primary_is_star
109
+ classes["runaway-t-2"] = disrupted & (final_kick_info["vsys_2_total"] >= 30.0) & secondary_is_star
110
+
111
+ # calculate relative speeds for observed walk/runaways
112
+ if disrupted.any():
113
+ rel_speed_1 = _get_rel_speed(orbits=orbits[disrupted], potential=potential, which_star=0)
114
+ rel_speed_2 = _get_rel_speed(orbits=orbits[disrupted], potential=potential, which_star=1)
115
+
116
+ # set the classes based on the relative speeds (non-disrupted as all left as False by default)
117
+ classes.loc[disrupted, "walkaway-o-1"] = (rel_speed_1 < 30.0) & primary_is_star[disrupted]
118
+ classes.loc[disrupted, "walkaway-o-2"] = (rel_speed_2 < 30.0) & secondary_is_star[disrupted]
119
+ classes.loc[disrupted, "runaway-o-1"] = (rel_speed_1 >= 30.0) & primary_is_star[disrupted]
120
+ classes.loc[disrupted, "runaway-o-2"] = (rel_speed_2 >= 30.0) & secondary_is_star[disrupted]
121
+
122
+ return classes
123
+
124
+
125
+ def _get_rel_speed(orbits, potential, which_star):
126
+ """Calculate the relative speed of a set of stars at the end of their orbits compared to the circular
127
+ velocity at their final positions (given a galactic potential)
128
+
129
+ Parameters
130
+ ----------
131
+ orbits : `list`
132
+ List of gala Orbits
133
+ potential : :class:`gala.potential.potential.PotentialBase`
134
+ The galactic potential used for finding the circular velocity
135
+ which_star : `int`
136
+ Index of which star to consider, either 0 or 1
137
+
138
+ Returns
139
+ -------
140
+ rel_speed : `float`
141
+ Relative speed in km / s
142
+ """
143
+ # get final positions and velocities
144
+ posf = [None for _ in range(len(orbits))]
145
+ velf = [None for _ in range(len(orbits))]
146
+ for i, orbit in enumerate(orbits):
147
+ posf[i] = orbit[which_star][-1].pos.xyz.to(u.kpc).value
148
+ velf[i] = orbit[which_star][-1].vel.d_xyz.to(u.km / u.s)
149
+ posf *= u.kpc
150
+ velf *= u.km / u.s
151
+
152
+ # create gala phase space positions based on them
153
+ wf = gd.PhaseSpacePosition(pos=posf.T, vel=velf.T)
154
+
155
+ # calculate the circular velocities at those locations
156
+ v_circ = potential.circular_velocity(q=wf.pos.xyz)
157
+
158
+ # get the cylindrical velocities and calculate the relative speeds compared to the circular velocity
159
+ v_R = wf.represent_as("cylindrical").vel.d_rho
160
+ with u.set_enabled_equivalencies(u.dimensionless_angles()):
161
+ v_T = wf.represent_as("cylindrical").vel.d_phi.to(1/u.Myr) * wf.represent_as("cylindrical").rho
162
+ v_z = wf.represent_as("cylindrical").vel.d_z
163
+ rel_speed = (((v_R - v_circ)**2 + v_T**2 + v_z**2)**(0.5)).to(u.km / u.s).value
164
+
165
+ return rel_speed
166
+
167
+
168
+ def list_classes():
169
+ """List out the available classes that are used in other functions"""
170
+ classes = [
171
+ {
172
+ "name": "runaway-t",
173
+ "full_name": "Theory Runaway",
174
+ "condition": ("Any star from a disrupted binary that has an instantaneous velocity > 30 km/s in "
175
+ "the frame of the binary")
176
+ },
177
+ {
178
+ "name": "runaway-o",
179
+ "full_name": "Observation runaway",
180
+ "condition": ("Any star from a disrupted binary that is moving with a Galactocentric velocity "
181
+ "> 30km/s relative to the local circular velocity at its location")
182
+ },
183
+ {
184
+ "name": "walkaway-t",
185
+ "full_name": "Theory Runaway",
186
+ "condition": ("Any star from a disrupted binary that has an instantaneous velocity < 30 km/s in "
187
+ "the frame of the binary")
188
+ },
189
+ {
190
+ "name": "walkaway-o",
191
+ "full_name": "Observation walkaway",
192
+ "condition": ("Any star from a disrupted binary that is moving with a Galactocentric velocity "
193
+ "< 30km/s relative to the local circular velocity at its location")
194
+ },
195
+ {
196
+ "name": "widow",
197
+ "full_name": "Widowed Star",
198
+ "condition": ("Any star, or binary containing a star, that is/was a companion to a compact "
199
+ "object")
200
+ },
201
+ {
202
+ "name": "xrb",
203
+ "full_name": "X-ray binary",
204
+ "condition": ("Any binary with a star that is a companion to a compact object")
205
+ },
206
+ {
207
+ "name": "co",
208
+ "full_name": "Compact object",
209
+ "condition": ("Any compact object or binary containing a compact object")
210
+ },
211
+ {
212
+ "name": "stellar-merger-co",
213
+ "full_name": "Compact object from merger",
214
+ "condition": ("Any compact object resulting from a stellar merger")
215
+ },
216
+ {
217
+ "name": "dco",
218
+ "full_name": "Double compact object",
219
+ "condition": ("Any bound binary of two compact objects")
220
+ },
221
+ {
222
+ "name": "sdIa",
223
+ "full_name": "Single degenerate type Ia",
224
+ "condition": ("Any disrupted binary that contains a massless remnant that was once a white dwarf")
225
+ },
226
+ {
227
+ "name": "pisn",
228
+ "full_name": "Pair Instability Supernova",
229
+ "condition": ("Any binary that had a star with a pair instability supernova")
230
+ },
231
+ ]
232
+
233
+ print("Any class with a suffix '-1' or '-2' applies to only the primary or secondary")
234
+ print("Available classes")
235
+ print("-----------------")
236
+ for c in classes:
237
+ print(f'{c["full_name"]} ({c["name"]})')
238
+ print(f' {c["condition"]}\n')
239
+
240
+
241
+ def get_eddington_rate(mass, radiative_efficiency=None, radius=None):
242
+ """Get the eddington accretion rate
243
+
244
+ One of either radiative efficiency or radius must be provided.
245
+
246
+ Parameters
247
+ ----------
248
+ mass : :class:`~astropy.units.Quantity` [mass]
249
+ Mass
250
+ radiative_efficiency : :class:`~numpy.ndarray`, optional
251
+ Fraction of radiated mass that is accreted, by default None
252
+ radius : :class:`~astropy.units.Quantity` [length], optional
253
+ Radius, by default None
254
+
255
+ Returns
256
+ -------
257
+ eddington_rate : :class:`~astropy.units.Quantity` [mass / time]
258
+ Eddington accretion rate
259
+
260
+ Raises
261
+ ------
262
+ ValueError
263
+ If neither radiative efficiency or radius are provided
264
+ """
265
+ if radiative_efficiency is None and radius is None:
266
+ raise ValueError("Either `radiative_efficiency` or `radius` must be provided")
267
+ elif radiative_efficiency is None:
268
+ radiative_efficiency = (const.G * mass / (radius * const.c**2)).decompose()
269
+ return 4 * np.pi * const.G * mass * const.m_p / (radiative_efficiency * const.c * const.sigma_T)
270
+
271
+
272
+ def get_eddington_lum(mass):
273
+ """Get the eddington luminosity for a given mass
274
+
275
+ Parameters
276
+ ----------
277
+ mass : :class:`~astropy.units.Quantity` [mass]
278
+ Mass
279
+
280
+ Returns
281
+ -------
282
+ eddington_lum : :class:`~astropy.units.Quantity` [energy / time]
283
+ Eddington luminosity
284
+ """
285
+ return 4 * np.pi * const.G * mass * const.c * const.m_p / const.sigma_T
286
+
287
+
288
+ def get_schwarzchild_radius(mass):
289
+ """Get the Schwarzchild radius for a given black hole mass
290
+
291
+ Parameters
292
+ ----------
293
+ mass : :class:`~astropy.units.Quantity` [mass]
294
+ Mass
295
+
296
+ Returns
297
+ -------
298
+ r_s
299
+ Schwarzchild radius
300
+ """
301
+ return 2 * const.G * mass / const.c**2
302
+
303
+
304
+ def get_x_ray_lum(m_acc, r_acc, m_acc_dot, porb, kstar, m_don, RRLO_don):
305
+ """Estimate the X-ray luminosity for a given binary
306
+
307
+ We use the prescription from
308
+ `Misra+2022 <https://ui.adsabs.harvard.edu/abs/2022arXiv220905505M/abstract>`_ to estimate the X-ray
309
+ luminosity for both RLOF/wind-fed XRBs and Be-XRBs.
310
+
311
+ Parameters
312
+ ----------
313
+ m_acc : :class:`~astropy.units.Quantity` [mass]
314
+ Accretor mass
315
+ r_acc : :class:`~astropy.units.Quantity` [length]
316
+ Accretor radius
317
+ m_acc_dot : :class:`~astropy.units.Quantity` [mass / time]
318
+ Accretion rate
319
+ porb : :class:`~astropy.units.Quantity` [time]
320
+ Orbital period
321
+ kstar : `int`
322
+ Stellar type
323
+ m_don : :class:`~astropy.units.Quantity` [mass]
324
+ Donor mass
325
+ RRLO_don : :class:`~numpy.ndarray`
326
+ Donor radius in units of Roche Lobe radius
327
+
328
+ Returns
329
+ -------
330
+ rlof_wind_fed : :class:`~astropy.units.Quantity` [erg / s]
331
+ Luminosity for a RLOF/wind fed x-ray binary
332
+ be_xrb : :class:`~astropy.units.Quantity` [erg / s]
333
+ Luminosity for Be-XRB
334
+ """
335
+ # use 3 R_S as the radius for black holes
336
+ r_acc[kstar == 14] = 3 * get_schwarzchild_radius(m_acc[kstar == 14])
337
+
338
+ # compute radiative efficiency and eddington limit for masses
339
+ radiative_efficiency = (const.G * m_acc / (r_acc * const.c**2)).decompose()
340
+ eddington_rate = get_eddington_rate(m_acc, radiative_efficiency=radiative_efficiency)
341
+ eddington_ratio = (m_acc_dot / eddington_rate).decompose()
342
+
343
+ # first calculate for RLOF/wind fed XRBs
344
+ rlof_wind_fed = np.zeros(len(m_acc)) * u.erg / u.s
345
+
346
+ # handle sub eddington accretion
347
+ regular = eddington_ratio < 1.0
348
+ rlof_wind_fed[regular] = radiative_efficiency[regular] * m_acc_dot[regular] * const.c**2
349
+
350
+ # handle super Eddington
351
+ super_edd = (eddington_ratio >= 1.0) & (eddington_ratio < 8.5)
352
+ rlof_wind_fed[super_edd] = get_eddington_lum(m_acc[super_edd]) * (1 + np.log(eddington_ratio[super_edd]))
353
+
354
+ # account for beaming effect in extreme super Eddington
355
+ super_duper_edd = eddington_ratio >= 8.5
356
+ b = 73 / eddington_ratio[super_duper_edd]**2
357
+ b[b < 3.2e-3] = 3.2e-3
358
+ rlof_wind_fed[super_duper_edd] = get_eddington_lum(m_acc[super_duper_edd])\
359
+ * (1 + np.log(eddington_ratio[super_duper_edd])) / b
360
+
361
+ # next calculate for Be=XRBs
362
+ be_xrb = np.zeros(len(m_acc)) * u.erg / u.s
363
+
364
+ # we only want to consider certain systems as potential Be-XRBs based on Misra+2022
365
+ be_xrb_mask = (porb > 10 * u.day) & (porb < 300 * u.day) & (m_don >= 6 * u.Msun)\
366
+ & (RRLO_don * 100 > 1.0) & (kstar == 13)
367
+ be_xrb[be_xrb_mask] = 10**(4.53 - 1.5 * np.log10(porb[be_xrb_mask].to(u.day).value)) * 1e35 * u.erg / u.s
368
+
369
+ return rlof_wind_fed, be_xrb
@@ -0,0 +1,90 @@
1
+ import astropy.units as u
2
+
3
+ __all__ = ["identify_events"]
4
+
5
+
6
+ def identify_events(full_bpp, full_kick_info):
7
+ """Identify any events that occur in the stellar evolution that would affect the galactic evolution
8
+
9
+ .. note::
10
+ This function currently only considers supernovae when identifying events
11
+
12
+ Parameters
13
+ ----------
14
+ full_bpp : :class:`~pandas.DataFrame`
15
+ Table of evolution phase events from COSMIC
16
+ full_kick_info : :class:`~pandas.DataFrame`
17
+ Table of information about kicks from COSMIC
18
+
19
+ Returns
20
+ -------
21
+ events : `list`
22
+ A list of events for each binary. `None` is returned if there are no pertinent events, a list of
23
+ dicts for a bound system and two lists for a disrupted system (to track each component)
24
+ """
25
+ bin_nums = full_bpp["bin_num"].unique()
26
+ # remove anything that doesn't get a kick
27
+ full_kick_info = full_kick_info[full_kick_info["star"] > 0.0]
28
+
29
+ # mask for the rows that contain supernova events
30
+ full_bpp = full_bpp[full_bpp["evol_type"].isin([15, 16])]
31
+
32
+ # reduce to just the supernova rows and ensure we have the same length in each table
33
+ assert len(full_kick_info) == len(full_bpp)
34
+
35
+ events_list = [None for _ in range(len(bin_nums))]
36
+ for i, bin_num in enumerate(bin_nums):
37
+ if bin_num not in full_bpp.index:
38
+ continue
39
+
40
+ bpp = full_bpp.loc[[bin_num]]
41
+ kick_info = full_kick_info.loc[[bin_num]]
42
+
43
+ # check if the the binary is going to disrupt at any point
44
+ it_will_disrupt = (kick_info["disrupted"] == 1.0).any()
45
+
46
+ # iterate over the kick file and store the relevant information (for both if disruption will occur)
47
+ if it_will_disrupt:
48
+ events_1, events_2 = [], []
49
+ for j in range(len(kick_info)):
50
+ default_event = {
51
+ "time": bpp.iloc[j]["tphys"] * u.Myr,
52
+ "m_1": bpp.iloc[j]["mass_1"] * u.Msun,
53
+ "m_2": bpp.iloc[j]["mass_2"] * u.Msun,
54
+ "a": bpp.iloc[j]["sep"] * u.Rsun,
55
+ "ecc": bpp.iloc[j]["ecc"],
56
+ "delta_v_sys_xyz": [kick_info.iloc[j]["delta_vsysx_1"],
57
+ kick_info.iloc[j]["delta_vsysy_1"],
58
+ kick_info.iloc[j]["delta_vsysz_1"]] * u.km / u.s
59
+ }
60
+ events_1.append(default_event)
61
+
62
+ # for rows including the disruption or after it then the secondary component needs editing
63
+ if kick_info.iloc[j]["disrupted"] == 1.0:
64
+ events_2.append({
65
+ "time": bpp.iloc[j]["tphys"] * u.Myr,
66
+ "m_1": bpp.iloc[j]["mass_1"] * u.Msun,
67
+ "m_2": bpp.iloc[j]["mass_2"] * u.Msun,
68
+ "a": bpp.iloc[j]["sep"] * u.Rsun,
69
+ "ecc": bpp.iloc[j]["ecc"],
70
+ "delta_v_sys_xyz": [kick_info.iloc[j]["delta_vsysx_2"],
71
+ kick_info.iloc[j]["delta_vsysy_2"],
72
+ kick_info.iloc[j]["delta_vsysz_2"]] * u.km / u.s
73
+ })
74
+ else:
75
+ # otherwise just append the regular stuff
76
+ events_2.append(default_event)
77
+ events_list[i] = [events_1, events_2]
78
+ else:
79
+ # for bound binaries we just need a single list
80
+ events_list[i] = [{
81
+ "time": bpp.iloc[j]["tphys"] * u.Myr,
82
+ "m_1": bpp.iloc[j]["mass_1"] * u.Msun,
83
+ "m_2": bpp.iloc[j]["mass_2"] * u.Msun,
84
+ "a": bpp.iloc[j]["sep"] * u.Rsun,
85
+ "ecc": bpp.iloc[j]["ecc"],
86
+ "delta_v_sys_xyz": [kick_info.iloc[j]["delta_vsysx_1"],
87
+ kick_info.iloc[j]["delta_vsysy_1"],
88
+ kick_info.iloc[j]["delta_vsysz_1"]] * u.km / u.s
89
+ } for j in range(len(kick_info))]
90
+ return events_list