Re-running binaries¶
COSMIC allows you to re-run a binary from a previous evolution from a COSMIC generated initC table. This can also allow you to modify binary physics assumptions to see how they affect the later evolution of a binary with otherwise identical initial conditions.
Re-run an identical binary¶
You can re-run a binary from a COSMIC generated initC table, that’s all you need! This means you can send your favourite initial conditions around to your friends and they will be able to reproduce the exact same evolution (assuming they use the same COSMIC version).
First, let’s evolve a binary and save the initC table.
In [1]: from cosmic.sample.initialbinarytable import InitialBinaryTable
In [2]: from cosmic.evolve import Evolve
In [3]: BSEDict = {'xi': 1.0, 'bhflag': 1, 'neta': 0.5, 'windflag': 3, 'wdflag': 1, 'alpha1': 1.0,
...: 'pts1': 0.001, 'pts3': 0.02, 'pts2': 0.01, 'epsnov': 0.001, 'hewind': 0.5, 'ck': 1000,
...: 'bwind': 0.0, 'lambdaf': 0.0, 'mxns': 3.0, 'beta': -1.0, 'tflag': 1, 'acc2': 1.5,
...: 'grflag' : 1, 'remnantflag': 4, 'ceflag': 0, 'eddfac': 1.0, 'ifflag': 0,
...: 'bconst': 3000, 'sigma': 265.0, 'gamma': -2.0, 'pisn': 45.0,
...: 'natal_kick_array' : [[-100.0,-100.0,-100.0,-100.0,0.0], [-100.0,-100.0,-100.0,-100.0,0.0]],
...: 'bhsigmafrac' : 1.0, 'polar_kick_angle' : 90,
...: 'qcrit_array' : [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],
...: 'cekickflag' : 2, 'cehestarflag' : 0, 'cemergeflag' : 0, 'ecsn' : 2.25,
...: 'ecsn_mlow' : 1.6, 'aic' : 1, 'ussn' : 0, 'sigmadiv' :-20.0, 'qcflag' : 1, 'eddlimflag' : 0,
...: 'fprimc_array' : [2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0],
...: 'bhspinflag' : 0, 'bhspinmag' : 0.0, 'rejuv_fac' : 1.0, 'rejuvflag' : 0, 'htpmb' : 1,
...: 'ST_cr' : 1, 'ST_tide' : 1, 'bdecayfac' : 1, 'rembar_massloss' : 0.5, 'kickflag' : 1,
...: 'zsun' : 0.014, 'bhms_coll_flag' : 0, 'don_lim' : -1, 'acc_lim' : -1, 'rtmsflag' : 0,
...: 'wd_mass_lim': 1}
...:
In [4]: binary = InitialBinaryTable.InitialBinaries(
...: m1=20, m2=15, porb=100, ecc=0.1, tphysf=13700.0, kstar1=1, kstar2=1, metallicity=0.02
...: )
...:
In [5]: bpp, bcm, initC, kick_info = Evolve.evolve(initialbinarytable=binary, BSEDict=BSEDict)
We can check some of the output for this binary so that we can see it’s identical after we re-run it.
In [6]: print(bpp)
tphys mass_1 mass_2 ... bhspin_1 bhspin_2 bin_num
0 0.000000 20.000000 15.000000 ... 0.0 0.0 0
0 8.299073 18.851403 14.922740 ... 0.0 0.0 0
0 8.306134 18.843319 14.923134 ... 0.0 0.0 0
0 8.309411 18.807419 14.954781 ... 0.0 0.0 0
0 8.309411 18.807419 14.954781 ... 0.0 0.0 0
0 8.309411 27.946501 14.954781 ... 0.0 0.0 0
0 8.312153 27.883063 0.000000 ... 0.0 0.0 0
0 8.701370 10.228449 0.000000 ... 0.0 0.0 0
0 8.982919 7.798860 0.000000 ... 0.0 0.0 0
0 9.019456 7.443100 0.000000 ... 0.0 0.0 0
0 9.019456 3.090176 0.000000 ... 0.0 0.0 0
0 13700.000000 3.090176 0.000000 ... 0.0 0.0 0
[12 rows x 46 columns]
In [7]: print(kick_info)
star disrupted natal_kick ... psi_euler randomseed bin_num
0 1.0 1.0 256.688837 ... 0.0 -1.592283e+09 0.0
0 0.0 0.0 0.000000 ... 0.0 0.000000e+00 0.0
[2 rows x 19 columns]
Now we can re-run this exact same binary by passing in the initC table we just generated instead of the initial binary table. We also do not need to pass in the BSEDict again, as the physics assumptions are stored in the initC table. In fact, you should not pass in a BSEDict when re-running from an initC since things may get inconsistent (and confusing!).
In [8]: bpp_rerun, bcm_rerun, initC_rerun, kick_info_rerun = Evolve.evolve(initialbinarytable=initC)
In [9]: print(bpp_rerun)
tphys mass_1 mass_2 ... bhspin_1 bhspin_2 bin_num
0 0.000000 20.000000 15.000000 ... 0.0 0.0 0
0 8.299073 18.851403 14.922740 ... 0.0 0.0 0
0 8.306134 18.843319 14.923134 ... 0.0 0.0 0
0 8.309411 18.807419 14.954781 ... 0.0 0.0 0
0 8.309411 18.807419 14.954781 ... 0.0 0.0 0
0 8.309411 27.946501 14.954781 ... 0.0 0.0 0
0 8.312153 27.883063 0.000000 ... 0.0 0.0 0
0 8.701370 10.228449 0.000000 ... 0.0 0.0 0
0 8.982919 7.798860 0.000000 ... 0.0 0.0 0
0 9.019456 7.443100 0.000000 ... 0.0 0.0 0
0 9.019456 3.090176 0.000000 ... 0.0 0.0 0
0 13700.000000 3.090176 0.000000 ... 0.0 0.0 0
[12 rows x 46 columns]
In [10]: print(kick_info_rerun)
star disrupted natal_kick ... psi_euler randomseed bin_num
0 1.0 1.0 256.688837 ... 0.0 -1.592283e+09 0.0
0 0.0 0.0 0.000000 ... 0.0 0.000000e+00 0.0
[2 rows x 19 columns]
so overall, we can see that these outputs are exactly identical, even down to the kick information.
Warning
When re-running from an initC table, the BSEDict is no longer necessary since the physics assumptions are stored in the initC table. In fact, you should not pass in a BSEDict when re-running from an initC since things may get inconsistent (and confusing!).
Re-run with different physics¶
You can also re-run a binary from a COSMIC generated initC table but modify the physics assumptions. This allows you to see how different physics assumptions affect the later evolution of a binary with otherwise identical initial conditions.
Let’s re-run the same binary as before, but this time we will modify the common envelope efficiency parameter, alpha1, to be 10 instead of 1.0.
To do this, we modify the initC column corresponding to alpha1 before re-running the binary. We do not pass in a BSEDict this time, since we are modifying the physics assumptions directly in the initC table.
# modify alpha1 in the initC table
In [11]: initC_modified = initC.copy()
In [12]: initC_modified['alpha1'] = 10
# re-run the binary with modified physics
In [13]: bpp_rerun_mod, bcm_rerun_mod, initC_rerun_mod, kick_info_rerun_mod = Evolve.evolve(
....: initialbinarytable=initC_modified
....: )
....:
In [14]: print(bpp_rerun_mod)
tphys mass_1 mass_2 ... bhspin_1 bhspin_2 bin_num
0 0.000000 20.000000 15.000000 ... 0.0 0.0 0
0 8.299073 18.851403 14.922740 ... 0.0 0.0 0
0 8.306134 18.843319 14.923134 ... 0.0 0.0 0
0 8.309411 18.807419 14.954781 ... 0.0 0.0 0
0 8.309411 18.807419 14.954781 ... 0.0 0.0 0
0 8.309411 5.136602 14.954781 ... 0.0 0.0 0
0 8.309411 5.136602 14.954781 ... 0.0 0.0 0
0 9.581295 4.086627 14.947479 ... 0.0 0.0 0
0 9.689485 3.902209 14.960042 ... 0.0 0.0 0
0 9.689485 1.406181 14.960042 ... 0.0 0.0 0
0 12.016484 1.406181 14.441141 ... 0.0 0.0 0
0 12.042140 1.406181 14.414958 ... 0.0 0.0 0
0 13.461405 1.406181 8.606520 ... 0.0 0.0 0
0 13.521348 1.406181 4.682423 ... 0.0 0.0 0
0 13.521348 1.406181 1.593143 ... 0.0 0.0 0
0 13700.000000 1.406181 1.593143 ... 0.0 0.0 0
[16 rows x 46 columns]
In [15]: print(kick_info_rerun_mod)
star disrupted natal_kick ... psi_euler randomseed bin_num
0 1.0 1.0 256.688837 ... 247.930591 -1.592283e+09 0.0
0 2.0 1.0 527.944689 ... 0.000000 1.067690e+09 0.0
[2 rows x 19 columns]
Now as we can see, this output is very different from the original evolution, since the common envelope now proceeds quite differently with the higher efficiency parameter! The system goes from a merging binary that produces a black hole, to an unbound binary that produces two neutron stars.
Ensuring natal kicks account for modified physics¶
When re-running a binary with modified physics, you may want to ensure that the natal kicks are re-sampled appropriately given the new physics assumptions. For example, if you change the remnant mass prescription, you may want to re-sample the natal kicks since the remnant masses will be different. If you change the natal kick prescription itself, you will definitely want to re-sample the natal kicks.
In order to re-sample the natal kicks when re-running a binary, you need to erase the existing kick information from the initC table before re-running. This is done by setting the kick columns to -100.0, which is the flag value indicating that no kick has been assigned yet.
Let’s try this out by re-running our system with higher alpha, but this time re-sampling the natal kicks as well with a very small sigma value for both regular core-collapse supernovae and electron-capture supernovae.
# modify alpha1 in the initC table
In [16]: initC_modified_kick = initC.copy()
In [17]: initC_modified_kick['alpha1'] = 10
In [18]: initC_modified_kick['sigma'] = 1.0
In [19]: initC_modified_kick['sigmadiv'] = -1.0
In [20]: initC_modified_kick['kickflag'] = 1
# erase existing kick information by setting to -100.0
In [21]: kick_columns = [
....: 'natal_kick_1', 'natal_kick_2', 'phi_1', 'phi_2',
....: 'theta_1', 'theta_2', 'mean_anomaly_1', 'mean_anomaly_2'
....: ]
....:
In [22]: for col in kick_columns:
....: initC_modified_kick[col] = -100.0
....:
# re-run the binary with modified physics and re-sampled kicks
In [23]: bpp_rerun_mod_kick, bcm_rerun_mod_kick, initC_rerun_mod_kick, kick_info_rerun_mod_kick = Evolve.evolve(
....: initialbinarytable=initC_modified_kick,
....: )
....:
In [24]: print(bpp_rerun_mod_kick)
tphys mass_1 mass_2 ... bhspin_1 bhspin_2 bin_num
0 0.000000 20.000000 15.000000 ... 0.0 0.0 0
0 8.299073 18.851403 14.922740 ... 0.0 0.0 0
0 8.306134 18.843319 14.923134 ... 0.0 0.0 0
0 8.309411 18.807419 14.954781 ... 0.0 0.0 0
0 8.309411 18.807419 14.954781 ... 0.0 0.0 0
0 8.309411 5.136602 14.954781 ... 0.0 0.0 0
0 8.309411 5.136602 14.954781 ... 0.0 0.0 0
0 9.581295 4.086627 14.947479 ... 0.0 0.0 0
0 9.689485 3.902209 14.960042 ... 0.0 0.0 0
0 9.689485 1.406181 14.960042 ... 0.0 0.0 0
0 12.016484 1.406196 14.441141 ... 0.0 0.0 0
0 12.023027 1.406203 14.438592 ... 0.0 0.0 0
0 12.023027 1.406203 14.438592 ... 0.0 0.0 0
0 12.023027 1.406203 3.554457 ... 0.0 0.0 0
0 12.023027 1.406203 3.554457 ... 0.0 0.0 0
0 13.958374 1.421502 3.088799 ... 0.0 0.0 0
0 14.061607 1.423039 3.042581 ... 0.0 0.0 0
0 14.061607 1.423039 3.042581 ... 0.0 0.0 0
0 14.061607 1.423039 1.816577 ... 0.0 0.0 0
0 14.061607 1.423039 1.277584 ... 0.0 0.0 0
0 14.061607 1.423039 1.277584 ... 0.0 0.0 0
0 89.249977 1.423039 1.277584 ... 0.0 0.0 0
0 89.249977 2.700622 0.000000 ... 0.0 0.0 0
0 13700.000000 2.700622 0.000000 ... 0.0 0.0 0
[24 rows x 46 columns]
In [25]: print(kick_info_rerun_mod_kick)
star disrupted natal_kick ... psi_euler randomseed bin_num
0 1.0 0.0 1.544394 ... -178.950656 -1.592283e+09 0.0
0 2.0 0.0 1.992244 ... -179.579222 1.067690e+09 0.0
[2 rows x 19 columns]
As one might expect, the binary no longer becomes unbound since the natal kicks are now very small! The system instead produces two neutron stars in a bound system which eventually inspirals due to gravitational wave emission and produces a NS-NS merger.