cosmic-popsynth 3.6.2__cp313-cp313-macosx_14_0_arm64.whl
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.
- cosmic/.dylibs/libgcc_s.1.1.dylib +0 -0
- cosmic/.dylibs/libgfortran.5.dylib +0 -0
- cosmic/.dylibs/libquadmath.0.dylib +0 -0
- cosmic/Match.py +191 -0
- cosmic/__init__.py +32 -0
- cosmic/_commit_hash.py +1 -0
- cosmic/_evolvebin.cpython-313-darwin.so +0 -0
- cosmic/_version.py +1 -0
- cosmic/bse_utils/__init__.py +18 -0
- cosmic/bse_utils/zcnsts.py +570 -0
- cosmic/bse_utils/zdata.py +596 -0
- cosmic/checkstate.py +128 -0
- cosmic/data/cosmic-settings.json +1635 -0
- cosmic/evolve.py +607 -0
- cosmic/filter.py +214 -0
- cosmic/get_commit_hash.py +15 -0
- cosmic/output.py +466 -0
- cosmic/plotting.py +680 -0
- cosmic/sample/__init__.py +26 -0
- cosmic/sample/cmc/__init__.py +18 -0
- cosmic/sample/cmc/elson.py +411 -0
- cosmic/sample/cmc/king.py +260 -0
- cosmic/sample/initialbinarytable.py +251 -0
- cosmic/sample/initialcmctable.py +449 -0
- cosmic/sample/sampler/__init__.py +25 -0
- cosmic/sample/sampler/cmc.py +418 -0
- cosmic/sample/sampler/independent.py +1252 -0
- cosmic/sample/sampler/multidim.py +882 -0
- cosmic/sample/sampler/sampler.py +130 -0
- cosmic/test_evolve.py +108 -0
- cosmic/test_match.py +30 -0
- cosmic/test_sample.py +580 -0
- cosmic/test_utils.py +198 -0
- cosmic/utils.py +1574 -0
- cosmic_popsynth-3.6.2.data/scripts/cosmic-pop +544 -0
- cosmic_popsynth-3.6.2.dist-info/METADATA +55 -0
- cosmic_popsynth-3.6.2.dist-info/RECORD +38 -0
- cosmic_popsynth-3.6.2.dist-info/WHEEL +6 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright (C) Katie Breivik (2017 - 2020)
|
|
4
|
+
#
|
|
5
|
+
# This file is part of COSMIC
|
|
6
|
+
#
|
|
7
|
+
# COSMIC is free software: you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
# (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# COSMIC is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU General Public License
|
|
18
|
+
# along with COSMIC. If not, see <http://www.gnu.org/licenses/>
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
from . import ( # pylint: disable=unused-import
|
|
22
|
+
independent, # Independent parameter distribution sample
|
|
23
|
+
multidim, # Multidimensional parameter distribution sample`
|
|
24
|
+
cmc, # create initial conditions for CMC
|
|
25
|
+
)
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) Katelyn Breivik (2017 - 2021)
|
|
3
|
+
#
|
|
4
|
+
# This file is part of cosmic.
|
|
5
|
+
#
|
|
6
|
+
# cosmic is free software: you can redistribute it and/or modify
|
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
# (at your option) any later version.
|
|
10
|
+
#
|
|
11
|
+
# cosmic is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
# GNU General Public License for more details.
|
|
15
|
+
#
|
|
16
|
+
# You should have received a copy of the GNU General Public License
|
|
17
|
+
# along with cosmic. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
|
|
19
|
+
"""`cmc`
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
|
|
24
|
+
from .sampler import register_sampler
|
|
25
|
+
from .independent import Sample
|
|
26
|
+
from .. import InitialCMCTable, InitialBinaryTable
|
|
27
|
+
from ..cmc import elson, king
|
|
28
|
+
from ... import utils
|
|
29
|
+
|
|
30
|
+
__author__ = "Scott Coughlin <scott.coughlin@ligo.org>"
|
|
31
|
+
__credits__ = [
|
|
32
|
+
"Carl Rodriguez <carllouisrodriguez@gmail.com>",
|
|
33
|
+
"Newlin Weatherford <newlinweatherford2017@u.northwestern.edu>"
|
|
34
|
+
]
|
|
35
|
+
__all__ = ["get_cmc_sampler", "CMCSample"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_cmc_sampler(
|
|
39
|
+
cluster_profile, primary_model, ecc_model, porb_model, binfrac_model, met, size, **kwargs
|
|
40
|
+
):
|
|
41
|
+
"""Generates an initial cluster sample according to user specified models
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
cluster_profile : `str`
|
|
46
|
+
Model to use for the cluster profile (i.e. sampling of the placement of objects in the cluster and their velocity within the cluster)
|
|
47
|
+
Options include:
|
|
48
|
+
'plummer' : Standard Plummer sphere.
|
|
49
|
+
Additional parameters:
|
|
50
|
+
'r_max' : `float`
|
|
51
|
+
the maximum radius (in virial radii) to sample the clsuter
|
|
52
|
+
'elson' : EFF 1987 profile. Generalization of Plummer that better fits young massive clusters
|
|
53
|
+
Additional parameters:
|
|
54
|
+
'gamma' : `float`
|
|
55
|
+
steepness paramter for Elson profile; note that gamma=4 is same is Plummer
|
|
56
|
+
'r_max' : `float`
|
|
57
|
+
the maximum radius (in virial radii) to sample the clsuter
|
|
58
|
+
'king' : King profile
|
|
59
|
+
'w_0' : `float`
|
|
60
|
+
King concentration parameter
|
|
61
|
+
'r_max' : `float`
|
|
62
|
+
the maximum radius (in virial radii) to sample the clsuter
|
|
63
|
+
|
|
64
|
+
primary_model : `str`
|
|
65
|
+
Model to sample primary mass; choices include: kroupa93, kroupa01, salpeter55, custom
|
|
66
|
+
if 'custom' is selected, must also pass arguemts:
|
|
67
|
+
alphas : `array`
|
|
68
|
+
list of power law indicies
|
|
69
|
+
mcuts : `array`
|
|
70
|
+
breaks in the power laws.
|
|
71
|
+
e.g. alphas=[-1.3,-2.3,-2.3],mcuts=[0.08,0.5,1.0,150.] reproduces standard Kroupa2001 IMF
|
|
72
|
+
|
|
73
|
+
ecc_model : `str`
|
|
74
|
+
Model to sample eccentricity; choices include: thermal, uniform, sana12
|
|
75
|
+
|
|
76
|
+
porb_model : `str`
|
|
77
|
+
Model to sample orbital period; choices include: log_uniform, sana12, renzo19, raghavan10, moe19, martinez26
|
|
78
|
+
|
|
79
|
+
msort : `float`
|
|
80
|
+
Stars with M>msort can have different pairing and sampling of companions
|
|
81
|
+
|
|
82
|
+
pair : `float`
|
|
83
|
+
Sets the pairing of stars M>msort only with stars with M>msort
|
|
84
|
+
|
|
85
|
+
binfrac_model : `str or float`
|
|
86
|
+
Model for binary fraction; choices include: vanHaaften, offner23, or a fraction where 1.0 is 100% binaries
|
|
87
|
+
|
|
88
|
+
binfrac_model_msort : `str or float`
|
|
89
|
+
Same as binfrac_model for M>msort
|
|
90
|
+
|
|
91
|
+
qmin : `float`
|
|
92
|
+
kwarg which sets the minimum mass ratio for sampling the secondary
|
|
93
|
+
where the mass ratio distribution is flat in q
|
|
94
|
+
if q > 0, qmin sets the minimum mass ratio
|
|
95
|
+
q = -1, this limits the minimum mass ratio to be set such that
|
|
96
|
+
the pre-MS lifetime of the secondary is not longer than the full
|
|
97
|
+
lifetime of the primary if it were to evolve as a single star
|
|
98
|
+
|
|
99
|
+
m2_min : `float`
|
|
100
|
+
kwarg which sets the minimum secondary mass for sampling
|
|
101
|
+
the secondary as uniform in mass_2 between m2_min and mass_1
|
|
102
|
+
|
|
103
|
+
qmin_msort : `float`
|
|
104
|
+
Same as qmin for M>msort; only applies if qmin is supplied
|
|
105
|
+
|
|
106
|
+
met : `float`
|
|
107
|
+
Sets the metallicity of the binary population where solar metallicity is zsun
|
|
108
|
+
|
|
109
|
+
size : `int`
|
|
110
|
+
Size of the population to sample
|
|
111
|
+
|
|
112
|
+
zsun : `float`
|
|
113
|
+
optional kwarg for setting effective radii, default is 0.02
|
|
114
|
+
|
|
115
|
+
Optional Parameters
|
|
116
|
+
-------------------
|
|
117
|
+
virial_radius : `float`
|
|
118
|
+
the initial virial radius of the cluster, in parsecs
|
|
119
|
+
Default -- 1 pc
|
|
120
|
+
|
|
121
|
+
tidal_radius : `float`
|
|
122
|
+
the initial tidal radius of the cluster, in units of the virial_radius
|
|
123
|
+
Default -- 1e6 rvir
|
|
124
|
+
|
|
125
|
+
central_bh : `float`
|
|
126
|
+
Put a central massive black hole in the cluster
|
|
127
|
+
Default -- 0 MSUN
|
|
128
|
+
|
|
129
|
+
scale_with_central_bh : `bool`
|
|
130
|
+
If True, then the potential from the central_bh is included when scaling the radii.
|
|
131
|
+
Default -- False
|
|
132
|
+
|
|
133
|
+
seed : `float`
|
|
134
|
+
seed to the random number generator, for reproducability
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
Singles: `pandas.DataFrame`
|
|
139
|
+
DataFrame of Single objects in the format of the InitialCMCTable
|
|
140
|
+
|
|
141
|
+
Binaries: `pandas.DataFrame`
|
|
142
|
+
DataFrame of Single objects in the format of the InitialCMCTable
|
|
143
|
+
"""
|
|
144
|
+
initconditions = CMCSample()
|
|
145
|
+
|
|
146
|
+
# if RNG seed is provided, then use it globally
|
|
147
|
+
rng_seed = kwargs.pop("seed", 0)
|
|
148
|
+
if rng_seed != 0:
|
|
149
|
+
np.random.seed(rng_seed)
|
|
150
|
+
|
|
151
|
+
# get radii, radial and transverse velocities
|
|
152
|
+
r, vr, vt = initconditions.set_r_vr_vt(cluster_profile, N=size, **kwargs)
|
|
153
|
+
|
|
154
|
+
# track the mass in singles and the mass in binaries
|
|
155
|
+
mass_singles = 0.0
|
|
156
|
+
mass_binaries = 0.0
|
|
157
|
+
|
|
158
|
+
# track the total number of stars sampled
|
|
159
|
+
n_singles = 0
|
|
160
|
+
n_binaries = 0
|
|
161
|
+
|
|
162
|
+
mass1, total_mass1 = initconditions.sample_primary(
|
|
163
|
+
primary_model, size=size, **kwargs)
|
|
164
|
+
(
|
|
165
|
+
mass1_binaries,
|
|
166
|
+
mass_single,
|
|
167
|
+
binfrac_binaries,
|
|
168
|
+
binary_index,
|
|
169
|
+
) = initconditions.binary_select(mass1, binfrac_model=binfrac_model, **kwargs)
|
|
170
|
+
|
|
171
|
+
mass2_binaries = initconditions.sample_secondary(
|
|
172
|
+
mass1_binaries, **kwargs)
|
|
173
|
+
|
|
174
|
+
# track the mass sampled
|
|
175
|
+
mass_singles += np.sum(mass_single)
|
|
176
|
+
mass_binaries += np.sum(mass1_binaries)
|
|
177
|
+
mass_binaries += np.sum(mass2_binaries)
|
|
178
|
+
|
|
179
|
+
# set singles id
|
|
180
|
+
single_ids = np.arange(mass1.size)
|
|
181
|
+
binary_secondary_object_id = np.arange(mass1.size, mass1.size + mass2_binaries.size)
|
|
182
|
+
|
|
183
|
+
# set binind and correct masses of binaries
|
|
184
|
+
binind = np.zeros(mass1.size)
|
|
185
|
+
binind[binary_index] = np.arange(len(binary_index)) + 1
|
|
186
|
+
mass1[binary_index] += mass2_binaries
|
|
187
|
+
|
|
188
|
+
# track the total number sampled
|
|
189
|
+
n_singles += len(mass_single)
|
|
190
|
+
n_binaries += len(mass1_binaries)
|
|
191
|
+
|
|
192
|
+
# Obtain radii (technically this is done for the binaries in the independent sampler
|
|
193
|
+
# if set_radii_with_BSE is true, but that's not a huge amount of overhead)
|
|
194
|
+
zsun = kwargs.pop("zsun", 0.02)
|
|
195
|
+
|
|
196
|
+
Reff = initconditions.set_reff(mass1, metallicity=met, zsun=zsun)
|
|
197
|
+
Reff1 = Reff[binary_index]
|
|
198
|
+
Reff2 = initconditions.set_reff(mass2_binaries, metallicity=met, zsun=zsun)
|
|
199
|
+
|
|
200
|
+
# select out the primaries and secondaries that will produce the final kstars
|
|
201
|
+
porb_max = initconditions.calc_porb_max(mass1, vr, vt, binary_index, mass1_binaries, mass2_binaries, **kwargs)
|
|
202
|
+
|
|
203
|
+
porb,aRL_over_a = initconditions.sample_porb(
|
|
204
|
+
mass1_binaries, mass2_binaries, Reff1, Reff2, porb_model=porb_model, porb_max=porb_max, size=mass1_binaries.size
|
|
205
|
+
)
|
|
206
|
+
ecc = initconditions.sample_ecc(aRL_over_a, ecc_model, size=mass1_binaries.size)
|
|
207
|
+
|
|
208
|
+
sep = utils.a_from_p(porb, mass1_binaries, mass2_binaries)
|
|
209
|
+
kstar1 = initconditions.set_kstar(mass1_binaries)
|
|
210
|
+
kstar2 = initconditions.set_kstar(mass2_binaries)
|
|
211
|
+
|
|
212
|
+
singles_table = InitialCMCTable.InitialCMCSingles(
|
|
213
|
+
single_ids +
|
|
214
|
+
1, initconditions.set_kstar(mass1), mass1, Reff, r, vr, vt, binind
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
binaries_table = InitialCMCTable.InitialCMCBinaries(
|
|
218
|
+
np.arange(mass1_binaries.size) + 1,
|
|
219
|
+
single_ids[binary_index] + 1,
|
|
220
|
+
kstar1,
|
|
221
|
+
mass1_binaries,
|
|
222
|
+
Reff1,
|
|
223
|
+
binary_secondary_object_id + 1,
|
|
224
|
+
kstar2,
|
|
225
|
+
mass2_binaries,
|
|
226
|
+
Reff2,
|
|
227
|
+
sep,
|
|
228
|
+
ecc,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
singles_table.metallicity = met
|
|
232
|
+
binaries_table.metallicity = met
|
|
233
|
+
singles_table.virial_radius = kwargs.get("virial_radius",1)
|
|
234
|
+
singles_table.tidal_radius = kwargs.get("tidal_radius",1e6)
|
|
235
|
+
singles_table.central_bh = kwargs.get("central_bh",0)
|
|
236
|
+
singles_table.scale_with_central_bh = kwargs.get("scale_with_central_bh",False)
|
|
237
|
+
singles_table.mass_of_cluster = np.sum(singles_table["m"])
|
|
238
|
+
|
|
239
|
+
return singles_table, binaries_table
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
register_sampler(
|
|
243
|
+
"cmc",
|
|
244
|
+
InitialCMCTable,
|
|
245
|
+
get_cmc_sampler,
|
|
246
|
+
usage="primary_model ecc_model porb_model binfrac_model met size",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def get_cmc_point_mass_sampler(
|
|
250
|
+
cluster_profile, size, **kwargs
|
|
251
|
+
):
|
|
252
|
+
"""Generates an CMC cluster model according to user-specified model.
|
|
253
|
+
Note here that masses will all be unity (with total cluster normalized accordingly)
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
cluster_profile : `str`
|
|
258
|
+
Model to use for the cluster profile (i.e. sampling of the placement of objects in the cluster and their velocity within the cluster)
|
|
259
|
+
Options include:
|
|
260
|
+
'plummer' : Standard Plummer sphere.
|
|
261
|
+
Additional parameters:
|
|
262
|
+
'r_max' : `float`
|
|
263
|
+
the maximum radius (in virial radii) to sample the clsuter
|
|
264
|
+
'elson' : EFF 1987 profile. Generalization of Plummer that better fits young massive clusters
|
|
265
|
+
Additional parameters:
|
|
266
|
+
'gamma' : `float`
|
|
267
|
+
steepness paramter for Elson profile; note that gamma=4 is same is Plummer
|
|
268
|
+
'r_max' : `float`
|
|
269
|
+
the maximum radius (in virial radii) to sample the clsuter
|
|
270
|
+
'king' : King profile
|
|
271
|
+
'w_0' : `float`
|
|
272
|
+
King concentration parameter
|
|
273
|
+
'r_max' : `float`
|
|
274
|
+
the maximum radius (in virial radii) to sample the clsuter
|
|
275
|
+
|
|
276
|
+
size : `int`
|
|
277
|
+
Size of the population to sample
|
|
278
|
+
|
|
279
|
+
Optional Parameters
|
|
280
|
+
-------------------
|
|
281
|
+
virial_radius : `float`
|
|
282
|
+
the initial virial radius of the cluster, in parsecs
|
|
283
|
+
Default -- 1 pc
|
|
284
|
+
|
|
285
|
+
tidal_radius : `float`
|
|
286
|
+
the initial tidal radius of the cluster, in units of the virial_radius
|
|
287
|
+
Default -- 1e6 rvir
|
|
288
|
+
|
|
289
|
+
central_bh : `float`
|
|
290
|
+
Put a central massive black hole in the cluster. Note here units are
|
|
291
|
+
in code units where every particle has mass 1 (before normalization), e.g. if you
|
|
292
|
+
want a cluster with 10000 stars and a BH that is 10% of the total mass, central_bh=1000
|
|
293
|
+
Default -- 0
|
|
294
|
+
|
|
295
|
+
scale_with_central_bh : `bool`
|
|
296
|
+
If True, then the potential from the central_bh is included when scaling the radii.
|
|
297
|
+
Default -- False
|
|
298
|
+
|
|
299
|
+
seed : `float`
|
|
300
|
+
seed to the random number generator, for reproducability
|
|
301
|
+
|
|
302
|
+
Returns
|
|
303
|
+
-------
|
|
304
|
+
Singles: `pandas.DataFrame`
|
|
305
|
+
DataFrame of Single objects in the format of the InitialCMCTable
|
|
306
|
+
Binaries: `pandas.DataFrame`
|
|
307
|
+
DataFrame of Single objects in the format of the InitialCMCTable
|
|
308
|
+
|
|
309
|
+
"""
|
|
310
|
+
initconditions = CMCSample()
|
|
311
|
+
|
|
312
|
+
# if RNG seed is provided, then use it globally
|
|
313
|
+
rng_seed = kwargs.pop("seed", 0)
|
|
314
|
+
if rng_seed != 0:
|
|
315
|
+
np.random.seed(rng_seed)
|
|
316
|
+
|
|
317
|
+
# get radii, radial and transverse velocities
|
|
318
|
+
r, vr, vt = initconditions.set_r_vr_vt(cluster_profile, N=size, **kwargs)
|
|
319
|
+
|
|
320
|
+
mass1 = np.ones(size)/size
|
|
321
|
+
Reff = np.zeros(size)
|
|
322
|
+
binind = np.zeros(size)
|
|
323
|
+
single_ids = np.arange(size)
|
|
324
|
+
|
|
325
|
+
singles_table = InitialCMCTable.InitialCMCSingles(
|
|
326
|
+
single_ids + 1, initconditions.set_kstar(mass1), mass1, Reff, r, vr, vt, binind
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
# We assume no binaries for the point mass models
|
|
330
|
+
binaries_table = InitialCMCTable.InitialCMCBinaries(1,1,0,0,0,1,0,0,0,0,0)
|
|
331
|
+
|
|
332
|
+
singles_table.metallicity = 0.02
|
|
333
|
+
binaries_table.metallicity = 0.02
|
|
334
|
+
singles_table.virial_radius = kwargs.get("virial_radius",1)
|
|
335
|
+
singles_table.tidal_radius = kwargs.get("tidal_radius",1e6)
|
|
336
|
+
singles_table.central_bh = kwargs.get("central_bh",0)
|
|
337
|
+
singles_table.scale_with_central_bh = kwargs.get("scale_with_central_bh",False)
|
|
338
|
+
singles_table.mass_of_cluster = np.sum(singles_table["m"])*size
|
|
339
|
+
|
|
340
|
+
# Already scaled from the IC generators (unless we've added a central BH)
|
|
341
|
+
if singles_table.central_bh != 0:
|
|
342
|
+
singles_table.scaled_to_nbody_units = False
|
|
343
|
+
else:
|
|
344
|
+
singles_table.scaled_to_nbody_units = True
|
|
345
|
+
|
|
346
|
+
# Already scaled from the IC generators (unless we've added a central BH)
|
|
347
|
+
if singles_table.central_bh != 0:
|
|
348
|
+
singles_table.scaled_to_nbody_units = False
|
|
349
|
+
else:
|
|
350
|
+
singles_table.scaled_to_nbody_units = True
|
|
351
|
+
|
|
352
|
+
return singles_table, binaries_table
|
|
353
|
+
|
|
354
|
+
register_sampler(
|
|
355
|
+
"cmc_point_mass",
|
|
356
|
+
InitialCMCTable,
|
|
357
|
+
get_cmc_point_mass_sampler,
|
|
358
|
+
usage="size",
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
class CMCSample(Sample):
|
|
362
|
+
def set_r_vr_vt(self,cluster_profile, **kwargs):
|
|
363
|
+
if cluster_profile == "elson":
|
|
364
|
+
elson_kwargs = {
|
|
365
|
+
k: v for k, v in kwargs.items() if k in ["gamma", "r_max", "N"]
|
|
366
|
+
}
|
|
367
|
+
r, vr, vt = elson.draw_r_vr_vt(**elson_kwargs)
|
|
368
|
+
elif cluster_profile == "plummer":
|
|
369
|
+
plummer_kwargs = {k: v for k, v in kwargs.items() if k in ["r_max", "N"]}
|
|
370
|
+
r, vr, vt = elson.draw_r_vr_vt(gamma=4, **plummer_kwargs)
|
|
371
|
+
elif cluster_profile == "king":
|
|
372
|
+
king_kwargs = {k: v for k, v in kwargs.items() if k in ["w_0", "N"]}
|
|
373
|
+
r, vr, vt = king.draw_r_vr_vt(**king_kwargs)
|
|
374
|
+
else:
|
|
375
|
+
raise ValueError("Cluster profile not defined! Please specify one of [elson, plummer, king]")
|
|
376
|
+
|
|
377
|
+
return r, vr, vt
|
|
378
|
+
|
|
379
|
+
def calc_porb_max(self, mass, vr, vt, binary_index, mass1_binary, mass2_binary, **kwargs):
|
|
380
|
+
|
|
381
|
+
## First, compute a rolling average with window length of AVEKERNEL
|
|
382
|
+
## NOTE: this is in cluster code units (i.e. M=G=1), same as vr and vt
|
|
383
|
+
AVEKERNEL = 20
|
|
384
|
+
|
|
385
|
+
## Then compute the average mass and avergae of m*v^2
|
|
386
|
+
## First, to do this properly, we need to reflect the boundary points (so that the average
|
|
387
|
+
## at the boundary doesn't go to zero artifically))
|
|
388
|
+
m = np.concatenate([mass[AVEKERNEL-1::-1],mass,mass[:-AVEKERNEL-1:-1]])
|
|
389
|
+
mv2 = mass*(vr*vr + vt*vt)
|
|
390
|
+
mv2 = np.concatenate([mv2[AVEKERNEL-1::-1],mv2,mv2[:-AVEKERNEL-1:-1]])
|
|
391
|
+
|
|
392
|
+
## Then compute the averages by convolving with a uniform array (note we trim the reflected points off here)
|
|
393
|
+
m_average = np.convolve(m,np.ones(AVEKERNEL),mode='same')[AVEKERNEL:-AVEKERNEL]/AVEKERNEL
|
|
394
|
+
mv2_average = np.convolve(mv2,np.ones(AVEKERNEL),mode='same')[AVEKERNEL:-AVEKERNEL]/AVEKERNEL
|
|
395
|
+
|
|
396
|
+
## Finally return the mass-weighted average 3D velocity dispersion (in cluster code units)
|
|
397
|
+
sigma = np.sqrt(mv2_average/m_average)
|
|
398
|
+
|
|
399
|
+
## Now compute the orbital velocity corresponding to the hard/soft boundary; we only want it for the binaries
|
|
400
|
+
v_orb = 0.7*1.30294*sigma[binary_index] #sigma * 4/sqrt(3/pi); 0.7 is a factor we use in CMC
|
|
401
|
+
#v_orb = 0.7*1.30294*np.mean(sigma[:20]) #old CMC way of using just core velocity dispersion; TODO: possibly make flag option?
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
## Maximum semi-major axis just comes from Kepler's 3rd
|
|
405
|
+
## Note, to keep in code units, we need to divide binary masses by total cluster mass
|
|
406
|
+
amax = (mass1_binary+mass2_binary) / v_orb**2 / np.sum(mass)
|
|
407
|
+
|
|
408
|
+
## Convert from code units (virial radii) to RSUN
|
|
409
|
+
virial_radius = kwargs.get("virial_radius",1) ## get the virial radius of the cluster (uses 1pc if not given)
|
|
410
|
+
RSUN_PER_PARSEC = 4.435e+7
|
|
411
|
+
amax *= RSUN_PER_PARSEC * virial_radius
|
|
412
|
+
|
|
413
|
+
## Finally go from sep to porb
|
|
414
|
+
porb_max = utils.p_from_a(amax, mass1_binary, mass2_binary)
|
|
415
|
+
|
|
416
|
+
return porb_max ## returns orbital period IN DAYS
|
|
417
|
+
|
|
418
|
+
|