makecloud 1.0.0__py3-none-any.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.
MakeCloud.py ADDED
@@ -0,0 +1,659 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Usage: MakeCloud.py [options]
4
+
5
+ Options:
6
+ -h --help Show this screen.
7
+ --R=<pc> Outer radius of the cloud in pc [default: 10.0]
8
+ --M=<msun> Mass of the cloud in msun [default: 2e4]
9
+ --filename=<name> Name of the IC file to be generated
10
+ --N=<N> Number of gas particles [default: 2000000]
11
+ --density_exponent=<f> Power law exponent of the density profile [default: 0.0]
12
+ --spin=<f> Spin parameter: fraction of binding energy in solid-body rotation [default: 0.0]
13
+ --omega_exponent=<f> Powerlaw exponent of rotational frequency as a function of cylindrical radius [default: 0.0]
14
+ --turb_slope=<f> Slope of the turbulent power spectra [default: 2.0]
15
+ --turb_sol=<f> Fraction of turbulence in solenoidal modes [default: 0.5]
16
+ --alpha_turb=<f> Turbulent virial parameter (BM92 convention: 2Eturb/|Egrav|) [default: 2.]
17
+ --bturb=<f> Magnetic energy as a fraction of the binding energy [default: 0.1]
18
+ --bfixed=<f> Magnetic field in magnitude in code units, used instead of bturb if not set to zero [default: 0]
19
+ --minmode=<N> Minimum populated turbulent wavenumber for Gaussian initial velocity field, in units of pi/R [default: 2]
20
+ --turb_path=<name> Path to store turbulent velocity fields so that we only need to generate them once (defaults to ~/turb)
21
+ --glass_path=<name> Contains the the path of the glass file (defaults to your home directory)
22
+ --boxsize=<f> Simulation box size
23
+ --Mstar=<msun> Mass of the star/black hole, if any [default: 0.0]
24
+ --v_star=<vx,vy,vz> Velocity of the star [default: 0.0,0.0,0.0]
25
+ --x_star=<x,y,z> Position of the star, defaults to center of the box
26
+ --star_stage=<N> Evolutionary stage of the star/black hole [default: 7]
27
+ --derefinement Apply radial derefinement to ambient cells outside of 3* cloud radius
28
+ --no_diffuse_gas Remove diffuse ISM envelope fills the rest of the box with uniform density.
29
+ --phimode=<f> Relative amplitude of m=2 density perturbation (e.g. for Boss-Bodenheimer test) [default: 0.0]
30
+ --localdir Changes directory defaults assuming all files are used from local directory.
31
+ --B_unit=<gauss> Unit of magnetic field in gauss [default: 1.0]
32
+ --length_unit=<pc> Unit of length in pc [default: 1]
33
+ --mass_unit=<msun> Unit of mass in M_sun [default: 1]
34
+ --v_unit=<m/s> Unit of velocity in m/s [default: 1e3]
35
+ --unit_system=<name> Units system to adopt (options: starforge_classic (m/s - pc - Msun - T), FIRE (km/s - kpc - 1e10Msun - uG)) [default: None]
36
+ --turb_seed=<N> Random seed for turbulence initialization [default: 42]
37
+ --tmax=<N> Maximum time to run the simulation to, in units of the freefall time [default: 5]
38
+ --nsnap=<N> Number of snapshots per freefall time [default: 150]
39
+ --param_only Just makes the parameters file, not the IC
40
+ --fixed_ncrit=<f> Fixes ncrit to a specific value [default: 0.0]
41
+ --makebox Creates a second box IC of equivalent volume and mass to the cloud
42
+ --impact_dist=<b> Initial separation between cloud centers of mass in units of the cloud radius (0 is no cloud-cloud collision) [default: 0.0]
43
+ --impact_param=<b> Impact parameter of cloud-cloud collision in units of the cloud radius [default: 0.0]
44
+ --v_impact=<v> Impact velocity, in units of the cloud's RMS turbulent velocity [default: 1.0]
45
+ --impact_axis=<x> Axis along which collision occurs (z is along magnetic field lines) [default: x]
46
+ --makecylinder Creates a third, cylindrical IC of equivalent volume and mass to the cloud
47
+ --cyl_aspect_ratio=<f> Sets the aspect ratio of the cylinder, i.e. Length/Diameter [default: 10]
48
+ --Z=<solar> Metallicity of the cloud in Solar units (just for params file) [default: 1.0]
49
+ --ISRF=<solar> Interstellar radiation background of the cloud in Solar neighborhood units (just for params file) [default: 1.0]
50
+ """
51
+
52
+ import os
53
+ import numpy as np
54
+ from scipy import fftpack, interpolate
55
+ from scipy.spatial.distance import cdist
56
+ import h5py
57
+ from docopt import docopt
58
+
59
+
60
+ def get_glass_coords(N_gas, glass_path, center_on_cell=False):
61
+ x = h5py.File(glass_path)["Coordinates"][:]
62
+ if not center_on_cell: # if we don't want a cell at the exact box center
63
+ np.random.seed(42)
64
+ x = (x + np.random.rand(x.shape[1])) % 1.0
65
+ Nx = len(x)
66
+
67
+ while len(x) * np.pi * 4 / 3 / 8 < N_gas:
68
+ print(
69
+ "Need %d particles, have %d. Tessellating 8 copies of the glass file to get required particle number"
70
+ % (N_gas * 8 / (4 * np.pi / 3), len(x))
71
+ )
72
+ x = np.concatenate(
73
+ [
74
+ x / 2 + i * np.array([0.5, 0, 0]) + j * np.array([0, 0.5, 0]) + k * np.array([0, 0, 0.5])
75
+ for i in range(2)
76
+ for j in range(2)
77
+ for k in range(2)
78
+ ]
79
+ )
80
+ print("Glass loaded!")
81
+ order = x.max(axis=1).argsort()
82
+ return x[order]
83
+
84
+
85
+ def TurbField(res=256, minmode=2, maxmode=64, slope=2.0, sol_weight=1.0, seed=42):
86
+ freqs = fftpack.fftfreq(res)
87
+ freq3d = np.array(np.meshgrid(freqs, freqs, freqs, indexing="ij"))
88
+ intfreq = np.around(freq3d * res)
89
+ kSqr = np.sum(np.abs(freq3d) ** 2, axis=0)
90
+ intkSqr = np.sum(np.abs(intfreq) ** 2, axis=0)
91
+ VK = []
92
+
93
+ # apply ~k^-2 exp(-k^2/kmax^2) filter to white noise to get x, y, and z components of velocity field
94
+ for i in range(3):
95
+ np.random.seed(seed + i)
96
+ rand_phase = fftpack.fftn(
97
+ np.random.normal(size=kSqr.shape)
98
+ ) # fourier transform of white noise
99
+ vk = rand_phase * (float(minmode) / res) ** 2 / (np.power(kSqr, slope/2.0) + 1e-300)
100
+ vk[intkSqr == 0] = 0.0
101
+ vk[intkSqr < minmode**2] *= (
102
+ intkSqr[intkSqr < minmode**2] ** 2 / minmode**4
103
+ ) # smoother filter than mode-freezing; should give less "ringing" artifacts
104
+ vk *= np.exp(-intkSqr / maxmode**2)
105
+
106
+ VK.append(vk)
107
+ VK = np.array(VK)
108
+
109
+ vk_new = np.zeros_like(VK)
110
+
111
+ # do projection operator to get the correct mix of compressive and solenoidal
112
+ for i in range(3):
113
+ for j in range(3):
114
+ if i == j:
115
+ vk_new[i] += sol_weight * VK[j]
116
+ vk_new[i] += (1 - 2 * sol_weight) * freq3d[i] * freq3d[j] / (kSqr + 1e-300) * VK[j]
117
+ vk_new[:, kSqr == 0] = 0.0
118
+ VK = vk_new
119
+
120
+ vel = np.array([fftpack.ifftn(vk).real for vk in VK]) # transform back to real space
121
+ vel -= np.average(vel, axis=(1, 2, 3))[:, np.newaxis, np.newaxis, np.newaxis]
122
+ vel = vel / np.sqrt(np.sum(vel**2, axis=0).mean()) # normalize so that RMS is 1
123
+ return np.array(vel)
124
+
125
+
126
+
127
+ def _load_params_template():
128
+ local = os.path.join(os.path.dirname(os.path.realpath(__file__)), "params.txt")
129
+ if os.path.exists(local):
130
+ return open(local).read()
131
+ from importlib.resources import files
132
+ return files("makecloud").joinpath("params.txt").read_text()
133
+
134
+
135
+ def main():
136
+ arguments = docopt(__doc__)
137
+ R = float(arguments["--R"])
138
+ M_gas = float(arguments["--M"])
139
+ N_gas = int(float(arguments["--N"]) + 0.5)
140
+ M_star = float(arguments["--Mstar"])
141
+ v_star = np.array([float(v) for v in arguments["--v_star"].split(",")])
142
+ spin = float(arguments["--spin"])
143
+ omega_exponent = float(arguments["--omega_exponent"])
144
+ turbulence = float(arguments["--alpha_turb"]) / 2
145
+ seed = int(float(arguments["--turb_seed"]) + 0.5)
146
+ tmax = int(float(arguments["--tmax"]))
147
+ nsnap = int(float(arguments["--nsnap"]))
148
+ turb_slope = float(arguments["--turb_slope"])
149
+ turb_sol = float(arguments["--turb_sol"])
150
+ magnetic_field = float(arguments["--bturb"])
151
+ bfixed = float(arguments["--bfixed"])
152
+ minmode = int(arguments["--minmode"])
153
+ filename = arguments["--filename"]
154
+ diffuse_gas = not arguments["--no_diffuse_gas"]
155
+ param_only = arguments["--param_only"]
156
+ if arguments["--unit_system"] is not None:
157
+ match arguments["--unit_system"]:
158
+ case "starforge_classic":
159
+ length_unit_pc = 1
160
+ mass_unit_Msun = 1
161
+ v_unit_SI = 1
162
+ B_unit_gauss = 1e4
163
+ case "FIRE":
164
+ length_unit_pc = 1e3
165
+ mass_unit_Msun = 1e10
166
+ v_unit_SI = 1e3
167
+ B_unit_gauss = 1
168
+ else:
169
+ length_unit_pc = float(arguments["--length_unit"])
170
+ mass_unit_Msun = float(arguments["--mass_unit"])
171
+ v_unit_SI = float(arguments["--v_unit"])
172
+ B_unit_gauss = float(arguments["--B_unit"])
173
+
174
+ t_unit = length_unit_pc / v_unit_SI
175
+
176
+ G = 4300.71 * v_unit_SI**-2 * mass_unit_Msun / length_unit_pc
177
+ makebox = arguments["--makebox"]
178
+ impact_param = float(arguments["--impact_param"])
179
+ impact_dist = float(arguments["--impact_dist"])
180
+ v_impact = float(arguments["--v_impact"])
181
+ impact_axis = arguments["--impact_axis"]
182
+ makecylinder = arguments["--makecylinder"]
183
+ cyl_aspect_ratio = float(arguments["--cyl_aspect_ratio"])
184
+ fixed_ncrit = float(arguments["--fixed_ncrit"])
185
+ density_exponent = float(arguments["--density_exponent"])
186
+ metallicity = float(arguments["--Z"])
187
+ ISRF = float(arguments["--ISRF"])
188
+ if arguments["--turb_path"]:
189
+ turb_path = arguments["--turb_path"]
190
+ else:
191
+ turb_path = os.path.expanduser("~") + "/turb"
192
+ if arguments["--glass_path"]:
193
+ glass_path = arguments["--glass_path"]
194
+ else:
195
+ prefix = os.path.expanduser("~") + "/.makecloud_glass"
196
+ glass_path = prefix + "/glass_256.hdf5"
197
+ if not os.path.exists(glass_path):
198
+ if not os.path.isdir(prefix):
199
+ os.mkdir(prefix)
200
+ import urllib.request
201
+
202
+ print("Downloading glass file...")
203
+ urllib.request.urlretrieve(
204
+ "https://users.flatironinstitute.org/~mgrudic/glass/glass_256.hdf5",
205
+ glass_path,
206
+ )
207
+
208
+ if arguments["--boxsize"] is not None:
209
+ boxsize = float(arguments["--boxsize"])
210
+ else:
211
+ boxsize = 10 * R
212
+
213
+ if arguments["--x_star"]:
214
+ x_star = np.array([float(x) for x in arguments["--x_star"].split(",")])
215
+ else: # default to center of box
216
+ x_star = np.repeat(0.5 * boxsize, 3)
217
+
218
+ derefinement = arguments["--derefinement"]
219
+
220
+ res_effective = int(N_gas ** (1.0 / 3.0) + 0.5)
221
+ phimode = float(arguments["--phimode"])
222
+
223
+ filename = (
224
+ "M%3.2g_" % (M_gas)
225
+ + ("Mstar%g_" % (M_star) if M_star > 0 else "")
226
+ + ("rho_exp%g_" % (-density_exponent) if density_exponent < 0 else "")
227
+ + "R%g_Z%g_S%g_A%g_B%g_I%g_Res%d_n%d_sol%g"
228
+ % (
229
+ R,
230
+ metallicity,
231
+ spin,
232
+ 2 * turbulence,
233
+ magnetic_field,
234
+ ISRF,
235
+ res_effective,
236
+ minmode,
237
+ # turb_slope,
238
+ turb_sol,
239
+ )
240
+ + ("_%d" % seed)
241
+ + ("_collision_%g_%g_%g_%s" % (impact_dist, impact_param, v_impact, impact_axis) if impact_dist > 0 else "")
242
+ + ".hdf5"
243
+ )
244
+ filename = filename.replace("+", "").replace("e0", "e")
245
+ filename = "".join(filename.split())
246
+
247
+ dm = M_gas / N_gas
248
+ dm_solar = dm / mass_unit_Msun
249
+ rho_avg = 3 * M_gas / R**3 / (4 * np.pi)
250
+ if dm_solar < 0.1: # if we're doing something marginally IMF-resolving
251
+ softening = 3.11e-5 # ~6.5 AU, minimum sink radius is 2.8 times that (~18 AU)
252
+ ncrit = 1e13 # ~100x the opacity limit
253
+ else: # something more FIRE-like, where we rely on a sub-grid prescription turning gas into star particles
254
+ softening = 0.1
255
+ ncrit = 100
256
+
257
+ if fixed_ncrit:
258
+ ncrit = fixed_ncrit
259
+
260
+ tff = (3 * np.pi / (32 * G * rho_avg)) ** 0.5
261
+ L = (4 * np.pi * R**3 / 3) ** (1.0 / 3) # volume-equivalent box size
262
+ vrms = (6 / 5 * G * M_gas / R) ** 0.5 * turbulence**0.5
263
+
264
+ if turbulence:
265
+ tcross = L / vrms
266
+ else:
267
+ tcross = tff
268
+
269
+ turbenergy = (
270
+ 0.019111097819633344 * vrms**3 / L
271
+ ) # ST_Energy sets the dissipation rate of SPECIFIC energy ~ v^2 / (L/v) ~ v^3/L
272
+
273
+ paramsfile = _load_params_template()
274
+
275
+ jet_particle_mass = min(dm, max(1e-4, dm / 10.0))
276
+ MS_wind_particle_mass = (
277
+ jet_particle_mass / 10
278
+ ) # MS winds have lower mdot than jets, so we should be able to better resolve them this way
279
+
280
+ replacements = {
281
+ "NAME": filename.replace(".hdf5", ""),
282
+ "DTSNAP": tff / nsnap,
283
+ "MAXTIMESTEP": tff / (nsnap),
284
+ "SOFTENING": softening,
285
+ "GASSOFT": 2.0e-8,
286
+ "TMAX": tff * tmax,
287
+ "RHOMAX": ncrit,
288
+ "BOXSIZE": boxsize,
289
+ "OUTFOLDER": "output",
290
+ "JET_PART_MASS": jet_particle_mass,
291
+ "MS_WIND_PART_MASS": MS_wind_particle_mass,
292
+ "BH_SEED_MASS": dm / 2.0,
293
+ "TURBDECAY": tcross / 2,
294
+ "TURBENERGY": turbenergy,
295
+ "TURBFREQ": tcross / 20,
296
+ "TURB_KMIN": int(100 * 2 * np.pi / L) / 100.0,
297
+ "TURB_KMAX": int(100 * 4 * np.pi / (L) + 1) / 100.0,
298
+ "TURB_SIGMA": (M_gas/2e4)**0.5 * (R/10)**-0.5 * 600 * turbulence**0.5,
299
+ "TURB_MINLAMBDA": int(100 * R / 2) / 100,
300
+ "TURB_MAXLAMBDA": int(100 * R * 2) / 100,
301
+ "TURB_COHERENCE_TIME": tcross / 2,
302
+ "UNIT_L": 3.085678e18 * length_unit_pc,
303
+ "UNIT_M": 1.989e33 * mass_unit_Msun,
304
+ "UNIT_V": v_unit_SI * 1e2,
305
+ "UNIT_B": B_unit_gauss,
306
+ "ZINIT": metallicity,
307
+ "ISRF": ISRF,
308
+ }
309
+
310
+ for k, r in replacements.items():
311
+ paramsfile = paramsfile.replace(k, (r if isinstance(r, str) else "{:.2e}".format(r)))
312
+
313
+ open("params_" + filename.replace(".hdf5", "") + ".txt", "w").write(paramsfile)
314
+ if makebox:
315
+ replacements_box = replacements.copy()
316
+ replacements_box["NAME"] = filename.replace(".hdf5", "_BOX")
317
+ replacements_box["BOXSIZE"] = L
318
+ replacements_box["TURB_MINLAMBDA"] = int(100 * L / 2) / 100
319
+ replacements_box["TURB_MAXLAMBDA"] = int(100 * L * 2) / 100
320
+ paramsfile = _load_params_template()
321
+ for k in replacements_box.keys():
322
+ paramsfile = paramsfile.replace(k, str(replacements_box[k]))
323
+ open("params_" + filename.replace(".hdf5", "") + "_BOX.txt", "w").write(paramsfile)
324
+ if makecylinder:
325
+ # Get cylinder params
326
+ R_cyl = R * np.sqrt(np.pi / (4 * cyl_aspect_ratio)) # surface density equivalent cylinder
327
+ L_cyl = R_cyl * 2 * cyl_aspect_ratio
328
+ vrms_cyl = (
329
+ (2 * G * M_gas / L_cyl) ** 0.5 * turbulence** 0.5
330
+ ) # the potential is different for a cylinder than for a sphere, so we need to rescale vrms to get the right alpha, using E_grav_cyl = -GM**2/L
331
+ vrms_cyl *= 0.71 # additional scaling found numerically to make the stirring run reproduce the right alpha and filament length (similarly determined numerical factor added to GIZMO)
332
+ tcross_cyl = 2 * R_cyl / vrms_cyl
333
+ boxsize_cyl = L_cyl * 1.5 + R_cyl * 5 # the box should fit the cylinder and be many times bigger than its width
334
+ print("Cylinder params: L=%g R=%g boxsize=%g vrms=%g" % (L_cyl, R_cyl, boxsize_cyl, vrms_cyl))
335
+ replacements_cyl = replacements.copy()
336
+ replacements_cyl["NAME"] = filename.replace(".hdf5", "_CYL")
337
+ replacements_cyl["BOXSIZE"] = boxsize_cyl
338
+ # New driving params
339
+ replacements_cyl["TURB_MINLAMBDA"] = int(100 * R_cyl) / 100
340
+ replacements_cyl["TURB_MAXLAMBDA"] = int(100 * R_cyl * 4) / 100
341
+ replacements_cyl["TURB_SIGMA"] = vrms_cyl
342
+ replacements_cyl["TURB_COHERENCE_TIME"] = tcross_cyl / 2
343
+ # Legacy driving params, probably needs tuning
344
+ replacements_cyl["TURBDECAY"] = tcross_cyl / 2
345
+ replacements_cyl["TURBENERGY"] = 0.019111097819633344 * vrms_cyl**3 / R_cyl
346
+ replacements_cyl["TURBFREQ"] = tcross_cyl / 20
347
+ replacements_cyl["TURB_KMIN"] = int(100 * 2 * np.pi / R_cyl) / 100.0
348
+ replacements_cyl["TURB_KMAX"] = int(100 * 4 * np.pi / (R_cyl) + 1) / 100.0
349
+ paramsfile = _load_params_template()
350
+ for k in replacements_cyl.keys():
351
+ paramsfile = paramsfile.replace(k, str(replacements_cyl[k]))
352
+ open("params_" + filename.replace(".hdf5", "") + "_CYL.txt", "w").write(paramsfile)
353
+
354
+ if param_only:
355
+ print("Parameters only run, exiting...")
356
+ exit()
357
+
358
+ mgas = np.repeat(dm, N_gas)
359
+
360
+ x = get_glass_coords(N_gas, glass_path)
361
+ Nx = len(x)
362
+ x = 2 * (x - 0.5)
363
+ print("Computing radii...")
364
+ r = cdist(x, [np.zeros(3)])[:, 0]
365
+ print("Done! Sorting coordinates...")
366
+ x = x[r.argsort()][:N_gas]
367
+ print("Done! Rescaling...")
368
+ x *= (float(Nx) / N_gas * 4 * np.pi / 3 / 8) ** (1.0 / 3) * R
369
+ print("Done! Recomupting radii...")
370
+ r = cdist(x, [np.zeros(3)])[:, 0]
371
+ if np.any(r == 0):
372
+ raise ValueError(
373
+ "found point with r=0 in the glass file, we don't handle this case throughout our calculations yet. Stopping."
374
+ )
375
+ x, r = x / r.max(), r / r.max()
376
+ print("Doing density profile...")
377
+ rnew = r ** (3.0 / (3 + density_exponent)) * R
378
+
379
+
380
+ x = x * (rnew / r)[:, None]
381
+ r = np.sum(x**2, axis=1) ** 0.5
382
+ r_order = r.argsort()
383
+ x, r = np.take(x, r_order, axis=0), r[r_order]
384
+
385
+ if not os.path.exists(turb_path):
386
+ os.makedirs(turb_path)
387
+ fname = turb_path + "/vturb%d_beta%g_sol%g_seed%d.npy" % (minmode, turb_slope, turb_sol, seed)
388
+ if not os.path.isfile(fname):
389
+ vt = TurbField(minmode=minmode, slope = turb_slope, sol_weight=turb_sol, seed=seed)
390
+ nmin, nmax = vt.shape[-1] // 4, 3 * vt.shape[-1] // 4
391
+ vt = vt[
392
+ :, nmin:nmax, nmin:nmax, nmin:nmax
393
+ ] # we take the central cube of size L/2 so that opposide sides of the cloud are not correlated
394
+ np.save(fname, vt)
395
+ else:
396
+ vt = np.load(fname)
397
+
398
+ xgrid = np.linspace(-R, R, vt.shape[-1])
399
+ v = []
400
+ for i in range(3):
401
+ v.append(interpolate.interpn((xgrid, xgrid, xgrid), vt[i, :, :, :], x))
402
+ v = np.array(v).T
403
+ print("Coordinates obtained!")
404
+
405
+ Mr = mgas.cumsum()
406
+ ugrav = G * np.sum(Mr / r * mgas)
407
+ v -= np.average(v, axis=0)
408
+ Eturb = 0.5 * dm * np.sum(v**2)
409
+ v *= np.sqrt(turbulence * ugrav / Eturb)
410
+ E_rot_target = spin * ugrav
411
+ Rcyl = np.sqrt(x[:, 0] ** 2 + x[:, 1] ** 2)
412
+ omega = Rcyl**omega_exponent
413
+ vrot = np.cross(np.c_[np.zeros_like(omega), np.zeros_like(omega), omega], x)
414
+ Erot_actual = np.sum(0.5 * mgas[:, None] * vrot**2)
415
+ vrot *= np.sqrt(E_rot_target / Erot_actual)
416
+ v += vrot
417
+
418
+ B = np.c_[np.zeros(N_gas), np.zeros(N_gas), np.ones(N_gas)]
419
+ vA_unit = (
420
+ 3.429e8 * B_unit_gauss * (M_gas) ** -0.5 * R**1.5 * np.sqrt(4 * np.pi / 3) / v_unit_SI
421
+ ) # alfven speed for unit magnetic field
422
+ uB = 0.5 * M_gas * vA_unit**2 # magnetic energy we would have for unit magnetic field
423
+ if bfixed > 0:
424
+ B = B * bfixed
425
+ else:
426
+ B = B * np.sqrt(magnetic_field * ugrav / uB) # renormalize to desired magnetic energy
427
+
428
+ v = v - np.average(v, axis=0)
429
+ x = x - np.average(x, axis=0)
430
+
431
+ r, phi = np.sum(x**2, axis=1) ** 0.5, np.arctan2(x[:, 1], x[:, 0])
432
+ theta = np.arccos(x[:, 2] / r)
433
+ phi += phimode * np.sin(2 * phi) / 2
434
+ x = r[:, np.newaxis] * np.c_[np.cos(phi) * np.sin(theta), np.sin(phi) * np.sin(theta), np.cos(theta)]
435
+
436
+ if makecylinder:
437
+
438
+ def ind_in_cylinder(x, L_cyl, R_cyl):
439
+ return (np.abs(x[:, 0]) < L_cyl / 2) & (np.sum(x[:, 1:] ** 2, axis=1) < R_cyl**2)
440
+
441
+ # Just get a roughly homogeneous cylinder along the x axis, we will stir it anyway
442
+ N_cyl = 0
443
+ while N_cyl <= N_gas: # should be very unlikely that we need to repeat, but let's check to be sure
444
+ x_cyl = np.random.rand(2 * N_gas, 3) * 2 - 1
445
+ x_cyl[:, 0] *= L_cyl / 2
446
+ x_cyl[:, 1] *= R_cyl
447
+ x_cyl[:, 2] *= R_cyl
448
+ x_cyl = x_cyl[ind_in_cylinder(x_cyl, L_cyl, R_cyl)]
449
+ N_cyl = len(x_cyl)
450
+ # print("N_cyl: %g N_gas: %g"%(N_cyl,N_gas))
451
+ x_cyl = x_cyl[:N_gas] # keep only the right amount of gas
452
+ # Let's add some initial velocity to make the driving phase shorter, let's start with a rotational component
453
+ v_cyl = np.cross([1, 0, 0], x_cyl, axis=-1) / R_cyl
454
+ # tangential with magnitude increasing linearly
455
+ v_cyl *= vrms_cyl
456
+
457
+ u = np.ones_like(mgas) * 0.101 / 2.0 # /2 needed because it is molecular
458
+
459
+ if impact_dist > 0:
460
+ x = np.concatenate([x, x])
461
+ impact_dir = {
462
+ "x": np.array([1.0, 0, 0]),
463
+ "y": np.array([0, 1, 0]),
464
+ "z": np.array([0, 0, 1]),
465
+ }[impact_axis]
466
+ impact_param_dir = {
467
+ "x": np.array([0, 1, 0]),
468
+ "y": np.array([0, 0, 1]),
469
+ "z": np.array([1, 0, 0]),
470
+ }[impact_axis]
471
+ x[:N_gas] += impact_dist * R * impact_dir
472
+ x[N_gas:] -= impact_dist * R * impact_dir
473
+ x[:N_gas] += 0.5 * impact_param * R * impact_param_dir
474
+ x[N_gas:] -= 0.5 * impact_param * R * impact_param_dir
475
+ v = np.concatenate([v, v])
476
+ vrms = np.sum(v**2, axis=1).mean() ** 0.5
477
+ v[:N_gas] -= v_impact * vrms * impact_dir
478
+ v[N_gas:] += v_impact * vrms * impact_dir
479
+ B = np.concatenate([B, B])
480
+ u = np.concatenate([u, u])
481
+ mgas = np.concatenate([mgas, mgas])
482
+
483
+ u = (
484
+ np.ones_like(mgas) * (200 / v_unit_SI) ** 2
485
+ ) # start with specific internal energy of (200m/s)^2, this is overwritten unless starting with restart flag 2###### #0.101/2.0 #/2 needed because it is molecular
486
+
487
+ if diffuse_gas:
488
+ # assuming 10K vs 10^4K gas: factor of ~10^3 density contrast
489
+ rho_warm = M_gas * 3 / (4 * np.pi * R**3) / 1000
490
+ if derefinement:
491
+ M_warm = (boxsize**3 - (4 * np.pi * R**3 / 3)) * rho_warm # mass of diffuse box-filling medium
492
+ N_warm = int(M_warm / (dm))
493
+ x0 = get_glass_coords(N_gas, glass_path)
494
+ Nx = len(x0)
495
+ x0 = 2 * (x0 - 0.5)
496
+ r0 = (x0 * x0).sum(1) ** 0.5
497
+ x0, r0 = x0[r0.argsort()], r0[r0.argsort()]
498
+ # first lay down the stuff within 3*R
499
+ N_warm = int(4 * np.pi * rho_warm * (3 * R) ** 3 / 3 / dm) # number of cells within 3R
500
+ x_warm = x0[:N_warm] * 3 * R / r0[N_warm - 1] # uniform density of cells within 3R
501
+ x0 = x0[
502
+ N_warm:
503
+ ] # now we take the ones outside the initial sphere and map them to a n(R) ~ R^-3 profile so that we get constant number of cells per log radius interval
504
+ r0 = r0[N_warm:]
505
+ rnew = 3 * R * np.exp(np.arange(len(x0)) / N_warm / 3)
506
+ x_warm = np.concatenate([x_warm, (rnew / r0)[:, None] * x0], axis=0)
507
+ x_warm = x_warm[np.max(np.abs(x_warm), axis=1) < boxsize / 2]
508
+ N_warm = len(x_warm)
509
+ R_warm = (x_warm * x_warm).sum(1) ** 0.5
510
+ mgas = np.concatenate([mgas, np.clip(dm * (R_warm / (3 * R)) ** 3, dm, np.inf)])
511
+ else:
512
+ M_warm = boxsize**3 * rho_warm # mass of diffuse box-filling medium
513
+ N_warm = int(M_warm / dm) # get glass with N_warm particles
514
+ x_warm = get_glass_coords(N_warm, glass_path)[:N_warm]
515
+ x_warm /= x_warm.max()
516
+ x_warm = boxsize * x_warm - boxsize / 2
517
+ if impact_dist == 0:
518
+ x_warm = x_warm[np.sum(x_warm**2, axis=1) > R**2]
519
+ N_warm = len(x_warm)
520
+ mgas = np.concatenate([mgas, np.repeat(mgas.sum() / len(mgas), N_warm)])
521
+ x = np.concatenate([x, x_warm])
522
+ v = np.concatenate([v, np.zeros((N_warm, 3))])
523
+ Bmag = np.average(np.sum(B**2, axis=1)) ** 0.5
524
+ B = np.concatenate([B, np.repeat(Bmag, N_warm)[:, np.newaxis] * np.array([0, 0, 1])])
525
+ u = np.concatenate([u, np.repeat(101.0, N_warm)])
526
+
527
+ if makecylinder:
528
+ # The magnetic field is paralell to the cylinder (true at low densities, so probably fine for IC)
529
+ B_cyl = np.concatenate([B, np.repeat(Bmag, N_warm)[:, np.newaxis] * np.array([1, 0, 0])])
530
+ # Add diffuse medium
531
+ M_warm_cyl = (boxsize_cyl**3 - (4 * np.pi * R**3 / 3)) * rho_warm
532
+ N_warm_cyl = int(M_warm_cyl / (dm))
533
+ x_warm = boxsize_cyl * np.random.rand(N_warm_cyl, 3) - boxsize_cyl / 2 # will be recentered later
534
+ x_warm = x_warm[~ind_in_cylinder(x_warm, L_cyl, R_cyl)] # keep only warm gas outside the cylinder
535
+ # print("N_warm_cyl: %g N_warm_cyl_kept %g "%(N_warm_cyl,len(x_warm)))
536
+ N_warm_cyl = len(x_warm)
537
+ x_cyl = np.concatenate([x_cyl, x_warm])
538
+ v_cyl = np.concatenate([v_cyl, np.zeros((N_warm, 3))])
539
+
540
+ else:
541
+ N_warm = 0
542
+
543
+ rho = np.repeat(3 * M_gas / (4 * np.pi * R**3), len(mgas))
544
+ if diffuse_gas:
545
+ rho[-N_warm:] /= 1000
546
+ h = (32 * mgas / rho) ** (1.0 / 3)
547
+
548
+ x += boxsize / 2 # cloud is always centered at (boxsize/2,boxsize/2,boxsize/2)
549
+ if makecylinder:
550
+ x_cyl += boxsize_cyl / 2
551
+
552
+ print("Writing snapshot...")
553
+
554
+ F = h5py.File(filename, "w")
555
+ F.create_group("PartType0")
556
+ F.create_group("Header")
557
+ F["Header"].attrs["NumPart_ThisFile"] = [
558
+ len(mgas),
559
+ 0,
560
+ 0,
561
+ 0,
562
+ 0,
563
+ (1 if M_star > 0 else 0),
564
+ ]
565
+ F["Header"].attrs["NumPart_Total"] = [
566
+ len(mgas),
567
+ 0,
568
+ 0,
569
+ 0,
570
+ 0,
571
+ (1 if M_star > 0 else 0),
572
+ ]
573
+ F["Header"].attrs["BoxSize"] = boxsize
574
+ F["Header"].attrs["Time"] = 0.0
575
+ F["PartType0"].create_dataset("Masses", data=mgas)
576
+ F["PartType0"].create_dataset("Coordinates", data=x)
577
+ F["PartType0"].create_dataset("Velocities", data=v)
578
+ F["PartType0"].create_dataset("ParticleIDs", data=1 + np.arange(len(mgas)))
579
+ F["PartType0"].create_dataset("InternalEnergy", data=u)
580
+
581
+ if M_star > 0:
582
+ F.create_group("PartType5")
583
+ # Let's add the sink at the center
584
+ F["PartType5"].create_dataset("Masses", data=np.array([M_star]))
585
+ F["PartType5"].create_dataset("Coordinates", data=[x_star]) # at the center
586
+ F["PartType5"].create_dataset("Velocities", data=[v_star]) # at rest
587
+ F["PartType5"].create_dataset("ParticleIDs", data=np.array([F["PartType0/ParticleIDs"][:].max() + 1]))
588
+ # Advanced properties for sinks
589
+ F["PartType5"].create_dataset("BH_Mass", data=M_star) # all the mass in the sink/protostar/star
590
+ F["PartType5"].create_dataset("BH_Mass_AlphaDisk", data=np.array([0.0])) # starts with no disk
591
+ F["PartType5"].create_dataset("BH_Mdot", data=np.array([0.0])) # starts with no mdot
592
+ F["PartType5"].create_dataset("BH_Specific_AngMom", data=np.array([0.0])) # starts with no angular momentum
593
+ F["PartType5"].create_dataset("SinkRadius", data=np.array([softening])) # Sinkradius set to softening
594
+ F["PartType5"].create_dataset("StellarFormationTime", data=np.array([0.0]))
595
+ F["PartType5"].create_dataset("ProtoStellarAge", data=np.array([0.0]))
596
+ F["PartType5"].create_dataset("ProtoStellarStage", data=np.array([5], dtype=np.int32), dtype=np.int32)
597
+ # Stellar properties
598
+ # if (central_star or central_SN):
599
+ # if central_star:
600
+ # print("Assuming central sink is a ZAMS star")
601
+ # starts as ZAMS star
602
+ # else:
603
+ # print("Assuming central sink is a ZAMS star about to go supernova")
604
+ # F["PartType5"].create_dataset("ProtoStellarStage", data=np.array([6],dtype=np.int32), dtype=np.int32) #starts as ZAMS star going SN
605
+ # Set guess for ZAMS stellar radius, will be overwritten
606
+ if (M_star) > 1.0:
607
+ R_ZAMS = (M_star) ** 0.57
608
+ else:
609
+ R_ZAMS = (M_star) ** 0.8
610
+ F["PartType5"].create_dataset("ProtoStellarRadius_inSolar", data=np.array([R_ZAMS])) # Sinkradius set to softening
611
+ F["PartType5"].create_dataset("StarLuminosity_Solar", data=np.array([0.0])) # dummy
612
+ F["PartType5"].create_dataset("Mass_D", data=np.array([0.0])) # No D left
613
+
614
+ if magnetic_field > 0.0:
615
+ F["PartType0"].create_dataset("MagneticField", data=B)
616
+ F.close()
617
+
618
+ if makebox:
619
+ F = h5py.File(filename.replace(".hdf5", "_BOX.hdf5"), "w")
620
+ F.create_group("PartType0")
621
+ F.create_group("Header")
622
+ F["Header"].attrs["NumPart_ThisFile"] = [len(mgas), 0, 0, 0, 0, 0]
623
+ F["Header"].attrs["NumPart_Total"] = [len(mgas), 0, 0, 0, 0, 0]
624
+ F["Header"].attrs["MassTable"] = [M_gas / len(mgas), 0, 0, 0, 0, 0]
625
+ F["Header"].attrs["BoxSize"] = (4 * np.pi * R**3 / 3) ** (1.0 / 3)
626
+ F["Header"].attrs["Time"] = 0.0
627
+ F["PartType0"].create_dataset("Masses", data=mgas[: len(mgas)])
628
+ F["PartType0"].create_dataset(
629
+ "Coordinates",
630
+ data=np.random.rand(len(mgas), 3) * F["Header"].attrs["BoxSize"],
631
+ )
632
+ F["PartType0"].create_dataset("Velocities", data=np.zeros((len(mgas), 3)))
633
+ F["PartType0"].create_dataset("ParticleIDs", data=1 + np.arange(len(mgas)))
634
+ F["PartType0"].create_dataset("InternalEnergy", data=u)
635
+ if magnetic_field > 0.0:
636
+ F["PartType0"].create_dataset("MagneticField", data=B[: len(mgas)])
637
+ F.close()
638
+
639
+ if makecylinder:
640
+ F = h5py.File(filename.replace(".hdf5", "_CYL.hdf5"), "w")
641
+ F.create_group("PartType0")
642
+ F.create_group("Header")
643
+ F["Header"].attrs["NumPart_ThisFile"] = [N_gas + N_warm_cyl, 0, 0, 0, 0, 0]
644
+ F["Header"].attrs["NumPart_Total"] = [N_gas + N_warm_cyl, 0, 0, 0, 0, 0]
645
+ F["Header"].attrs["MassTable"] = [dm, 0, 0, 0, 0, 0]
646
+ F["Header"].attrs["BoxSize"] = boxsize_cyl
647
+ F["Header"].attrs["Time"] = 0.0
648
+ F["PartType0"].create_dataset("Masses", data=mgas)
649
+ F["PartType0"].create_dataset("Coordinates", data=x_cyl)
650
+ F["PartType0"].create_dataset("Velocities", data=v_cyl)
651
+ F["PartType0"].create_dataset("ParticleIDs", data=1 + np.arange(N_gas + N_warm_cyl))
652
+ F["PartType0"].create_dataset("InternalEnergy", data=u)
653
+ if magnetic_field > 0.0:
654
+ F["PartType0"].create_dataset("MagneticField", data=B_cyl)
655
+ F.close()
656
+
657
+
658
+ if __name__ == "__main__":
659
+ main()
calcGMCProps.py ADDED
@@ -0,0 +1,111 @@
1
+ """
2
+ Simple routines to calculate the cloud's: radius,
3
+ average density, surface density, free-fall time (in years),
4
+ and number of particles (N) given a cloud mass and cell mass
5
+ resolution to choose
6
+
7
+ Written by: Anna Rosen, 11/2/22
8
+ """
9
+
10
+ import math
11
+ import numpy
12
+
13
+ MSUN = 1.989e33 # g
14
+ PCCM = 3.086e18 # cm
15
+ G = 6.6743e-8 # cm^3 g^-1 s
16
+ SECYR = 3600 * 24 * 365.25 # s
17
+
18
+
19
+ def calcRhoAve(Mcl, Rcl=None, Sigma=1):
20
+ """
21
+ Inputs:
22
+ #cloud mass = Mcl [Msun]
23
+ #cloud radius = Rcl [pc]
24
+ #Sigma = Cloud surface density, default = 1 g/cm^2
25
+
26
+ Output:
27
+ rho_ave [g/cm^3]
28
+ """
29
+ if Rcl is None and Sigma > 0.0:
30
+ print(
31
+ "No Rcl supplied, calc. Rcl assuming Sigma = %.2e g/cm^2" % Sigma
32
+ )
33
+ Rcl = calcRcl(Mcl, Sigma)
34
+ else:
35
+ print("Error: must supple Rcl (in pc) or Sigma > 0")
36
+ M = Mcl * MSUN
37
+ R = Rcl * PCCM
38
+ rho_ave = 3 * M / (4 * math.pi * R**3.0)
39
+ print("Average cloud density = %.2e g/cm^3" % rho_ave)
40
+ return rho_ave
41
+
42
+
43
+ def calcRcl(Mcl, Sigma):
44
+ """
45
+ Inputs:
46
+ #cloud mass = Mcl [Msun]
47
+ #cloud surface density = Sigma [g/cm^2]
48
+
49
+ Output:
50
+ Rcl [pc]
51
+ """
52
+ M = Mcl * MSUN
53
+ Rcl = (M / (math.pi * Sigma)) ** 0.5 / PCCM
54
+ print("Cloud Radius = %.2f pc" % Rcl)
55
+ return Rcl
56
+
57
+
58
+ def calc_SurfaceDensity(Mcl, Rcl):
59
+ """
60
+ Inputs:
61
+ #cloud mass = Mcl [Msun]
62
+ #cloud radius = Sigma [g/cm^2]
63
+
64
+ Output:
65
+ Sigma [g/cm^2]
66
+ """
67
+ M = Mcl * MSUN
68
+ R = Rcl * PCCM
69
+ Sigma = M / (math.pi * R**3.0)
70
+ print("Cloud surface density = %.2e g/cm^2" % Sigma)
71
+ return rho_ave
72
+
73
+
74
+ def calc_tff(Mcl, Rcl=None, Sigma=1):
75
+ """
76
+ Inputs:
77
+ #cloud mass = Mcl [Msun]
78
+ #cloud surface density = Sigma [g/cm^2]
79
+
80
+ Output:
81
+ tff [yr]
82
+ """
83
+ if Rcl is None and Sigma > 0.0:
84
+ print(
85
+ "No Rcl supplied, calc. Rcl assuming Sigma = %.2e g/cm^2" % Sigma
86
+ )
87
+ Rcl = calcRcl(Mcl, Sigma)
88
+ else:
89
+ print("Error: must supple Rcl (in pc) or Sigma > 0")
90
+
91
+ rho_ave = calcRhoAve(Mcl, Rcl=Rcl, Sigma=1)
92
+
93
+ tff = (3 * math.pi / (32 * G * rho_ave)) ** 0.5 / SECYR
94
+ print("tff = %.2f yr" % tff)
95
+ return tff
96
+
97
+
98
+ def calc_Npart(Mcl, massRes=1e-3):
99
+ """
100
+ Inputs:
101
+ #cloud mass = Mcl [Msun]
102
+ #mass/cell [Msun]
103
+
104
+ Output:
105
+ N [particles]
106
+ """
107
+
108
+ N = Mcl / massRes
109
+ print("For Mcl = %.2e Msun, Mcell = %.2e" % (Mcl, massRes))
110
+ print("N = %.2e particles" % N)
111
+ return N
makecloud/__init__.py ADDED
File without changes
makecloud/params.txt ADDED
@@ -0,0 +1,250 @@
1
+ %-------------------------------------------------------------------------
2
+ %---- This file contains the input parameters needed at run-time for
3
+ % simulations. It is based on and closely resembles the GADGET-3
4
+ % parameterfile (format of which and parsing routines written by
5
+ % Volker Springel [volker.springel@h-its.org]). It has been updated
6
+ % with new naming conventions and additional variables as needed by
7
+ % Phil Hopkins [phopkins@caltech.edu] for GIZMO.
8
+ %-------------------------------------------------------------------------
9
+
10
+ %---- Relevant files (filenames and directories)
11
+ InitCondFile NAME
12
+ OutputDir OUTFOLDER
13
+
14
+ %---- File formats (input and output)
15
+ ICFormat 3 % 1=unformatted (gadget) binary, 3=hdf5, 4=cluster
16
+ SnapFormat 3 % 1=unformatted (gadget) binary, 3=hdf5
17
+
18
+
19
+ %---- Output parameters
20
+ RestartFile restart
21
+ SnapshotFileBase snapshot
22
+ OutputListOn 0 % =1 to use list in "OutputListFilename"
23
+ OutputListFilename output_times.txt % list of times (in code units) for snaps
24
+ NumFilesPerSnapshot 1
25
+ NumFilesWrittenInParallel 1 % must be < N_processors & power of 2
26
+
27
+ %---- Output frequency
28
+ TimeOfFirstSnapshot 0. % time (code units) of first snapshot
29
+ TimeBetSnapshot DTSNAP % time between (if OutputListOn=0), code units
30
+ TimeBetStatistics DTSNAP % time between additional statistics (e.g. energy)
31
+
32
+ %---- CPU run-time and checkpointing time-limits
33
+ TimeLimitCPU 172800 % in seconds
34
+ CpuTimeBetRestartFile 7200 % in seconds
35
+ ResubmitOn 0
36
+ ResubmitCommand my-scriptfile
37
+
38
+ %---- Desired simulation beginning and end times (in code units) for run
39
+ TimeBegin 0.0 % Beginning of the simulation
40
+ TimeMax TMAX % End of the simulation
41
+
42
+ %---- Maximum and minimum timesteps allowed
43
+ MaxSizeTimestep MAXTIMESTEP % in code units, set for your problem
44
+ MinSizeTimestep 1.0e-15 % set this very low, or get the wrong answer
45
+
46
+
47
+ %---- System of units
48
+ UnitLength_in_cm UNIT_L
49
+ UnitMass_in_g UNIT_M
50
+ UnitVelocity_in_cm_per_s UNIT_V
51
+ UnitMagneticField_in_gauss UNIT_B
52
+ GravityConstantInternal 0 % calculated by code if =0
53
+
54
+ %---- Cosmological parameters
55
+ ComovingIntegrationOn 0 % is it cosmological? (yes=1, no=0)
56
+ BoxSize BOXSIZE % in code units
57
+ Omega0 0. % =0 for non-cosmological
58
+ OmegaLambda 0. % =0 for non-cosmological
59
+ OmegaBaryon 0. % =0 for non-cosmological
60
+ HubbleParam 1. % little 'h'; =1 for non-cosmological runs
61
+
62
+
63
+ %----- Memory allocation
64
+ %MaxMemSize 5000 % sets maximum MPI process memory use in MByte
65
+ PartAllocFactor 5.0 % memory load allowed for better cpu balance
66
+ BufferSize 500 % in MByte
67
+
68
+ %---- Rebuild domains when >this fraction of particles active
69
+ TreeDomainUpdateFrequency 0.005 % 0.0005-0.05, dept on core+particle number
70
+
71
+
72
+ %---- (Optional) Initial hydro temperature & temperature floor (in Kelvin)
73
+ InitGasTemp 1e4 % set by IC file if =0
74
+ MinGasTemp 2.73 % don't trust cooling function below ~5K, whether dust, molecular lines, or photoelectric heating
75
+ InitRadiationTemp 20. % Initial and/or boundary radiation temperature (for RT_INFRARED)
76
+
77
+ %---- Hydro reconstruction (kernel) parameters
78
+ DesNumNgb 32 % domain-reconstruction kernel number: 32 standard, 60-114 for quintic
79
+ MaxHsml 1.0e10 % minimum gas kernel length (some very large value to prevent errors)
80
+ MinGasHsmlFractional 0 % minimum kernel length relative to gas force softening (<= 1)
81
+
82
+
83
+ %---- Gravitational softening lengths
84
+ %----- Softening lengths per particle type. If ADAPTIVE_GRAVSOFT is set, these
85
+ %-------- are the minimum softening allowed for each type -------
86
+ %-------- (units are co-moving for cosmological integrations)
87
+ SofteningGas 1e-10 % gas (particle type=0) (in co-moving code units)
88
+ SofteningHalo 0.020 % dark matter/collisionless particles (type=1)
89
+ SofteningDisk 0.150 % collisionless particles (type=2)
90
+ SofteningBulge 0.500 % collisionless particles (type=3)
91
+ SofteningStars SOFTENING % stars spawned from gas (type=4)
92
+ SofteningBndry SOFTENING % black holes (if active), or collisionless (type=5)
93
+
94
+ %---- if these are set in cosmo runs, SofteningX switches from comoving to physical
95
+ %------- units when the comoving value exceeds the choice here
96
+ %------- (these are ignored, and *only* the above are used, for non-cosmo runs)
97
+ SofteningGasMaxPhys 0.0005 % e.g. switch to 0.5pc physical below z=1
98
+ SofteningHaloMaxPhys 0.010
99
+ SofteningDiskMaxPhys 0.075
100
+ SofteningBulgeMaxPhys 0.250
101
+ SofteningStarsMaxPhys 0.0005
102
+ SofteningBndryMaxPhys 0.0005
103
+ %----- parameters for adaptive gravitational softening
104
+ AGS_DesNumNgb 32 % neighbor number for calculating adaptive gravsoft
105
+
106
+
107
+
108
+
109
+ %-------------------------------------------------------------------------
110
+ %-------------------------------------------------------------------------
111
+ %---------- Physics Modules ----------------------------------------------
112
+ %-------------------------------------------------------------------------
113
+ %-------------------------------------------------------------------------
114
+
115
+
116
+ %------------------------------------------------------------
117
+ %------------------ Additional Fluid Physics ----------------
118
+ %------------------------------------------------------------
119
+
120
+ %---- Magneto-Hydrodynamics Parameters (MAGNETIC on)
121
+ %----- Initial B-Field Strengths (if MHD_B_SET_IN_PARAMS on, otherwise read from IC file)
122
+ BiniX 1.0e-8 % initial B_x, in code units
123
+ BiniY 1.0e-8 % initial B_y, in code units
124
+ BiniZ 1.0e-8 % initial B_z, in code units
125
+
126
+ %---- Thermal Conduction (CONDUCTION on)
127
+ %----- set coefficient kappa [code units] or, if CONDUCTION_SPITZER on, multiplies value
128
+ ConductionCoeff 1.0 % set/multiply conduction coefficient
129
+
130
+ %---- Navier-Stokes Viscosity (VISCOSITY on)
131
+ %--- set coefficients eta,zeta [code units] or, if VISCOSITY_BRAGINSKII on, multiplies value
132
+ ShearViscosityCoeff 1.0 % set/multiply shear viscosity coefficient
133
+ BulkViscosityCoeff 1.0 % set/multiply bulk viscosity coefficient
134
+
135
+ %---- Turbulent Diffusion Master Switch (TURB_DIFFUSION on)
136
+ TurbDiffusionCoefficient 1.0 % Normalizes diffusion rates relative to Smagorinsky-Lilly theory [best calibration] (~0.5-2)
137
+
138
+ %---- Cosmic Ray + Gas Fluids (COSMIC_RAYS on)
139
+ CosmicRayDiffusionCoeff 1.0 % multiplies anisotropic diffusion/streaming coefficients
140
+
141
+ %---- Dust-Gas Mixtures (GRAIN_FLUID on)
142
+ Grain_Internal_Density 1.0 % internal/material density of grains in g/cm^3
143
+ Grain_Size_Min 1.e-6 % minimum grain size in cm
144
+ Grain_Size_Max 1.e-4 % maximum grain size in cm
145
+ Grain_Size_Spectrum_Powerlaw 0.5 % power-law distribution of grain sizes (dm/dlnr~r^x)
146
+
147
+
148
+ %-------------------------------------------------------------------------
149
+ %------------------ Star, Black Hole, and Galaxy Formation ---------------
150
+ %-------------------------------------------------------------------------
151
+
152
+
153
+ %---- Star Formation parameters (GALSF on)
154
+ CritPhysDensity RHOMAX % critical physical density for star formation (cm^(-3))
155
+ SfEffPerFreeFall 1.0 % SFR/(Mgas/tfreefall) for gas which meets SF criteria
156
+
157
+
158
+
159
+ %-------------- FIRE (PFH) explicit star formation & feedback model (FIRE on)
160
+ %--- initial metallicity of gas & stars in simulation
161
+ InitMetallicity ZINIT % initial gas+stellar metallicity (in solar)
162
+ InitStellarAge 0.0 % initial mean age (in Gyr; for stars in sim ICs)
163
+ %--- local radiation-pressure driven winds (GALSF_FB_FIRE_RT_LOCALRP)
164
+ WindMomentumLoading 1.0 % fraction of photon momentum to couple
165
+ %--- SneII Heating Model (GALSF_FB_MECHANICAL)
166
+ SNeIIEnergyFrac 1.0 % fraction of mechanical energy to couple
167
+ %--- HII region photo-heating model (GALSF_FB_FIRE_RT_HIIHEATING)
168
+ HIIRegion_fLum_Coupled 1.0 % fraction of ionizing photons allowed to see gas
169
+ %--- long-range radiation pressure acceleration (GALSF_FB_FIRE_RT_LONGRANGE)
170
+ PhotonMomentum_Coupled_Fraction 1.0 % fraction of L to allow incident
171
+ PhotonMomentum_fUV 0.0 % incident SED f(L) in UV (minimum scattering)
172
+ PhotonMomentum_fOPT 0.0 % incident SED f(L) in optical/near-IR
173
+ %--- gas return/recycling
174
+ GasReturnFraction 1.0 % fraction of gas mass returned (relative to ssp)
175
+ GasReturnEnergy 1.0 % fraction of returned gas energy+momentum (relative to ssp)
176
+ %--- cosmic rays (COSMIC_RAYS)
177
+ CosmicRay_SNeFraction 0.1 % fraction of SNe ejecta kinetic energy into cosmic rays (~10%)
178
+
179
+ InterstellarRadiationFieldStrength ISRF % interstellar radiation field intensity relative to values measured in Solar neighborhood
180
+
181
+ %-------------- Black Hole accretion & formation (BLACK_HOLES on)
182
+ %--- formation/seeding
183
+ SeedSinkMass BH_SEED_MASS % initial mass (on-the-fly or single galaxy)
184
+ SeedAlphaDiskMass 0.0 % initial mass in the alpha disk (BH_ALPHADISK_ACCRETION)
185
+ SeedSinkMinRedshift 2.0 % minimum redshift where new BH particles are seeded (lower-z ceases seeding)
186
+ SeedSinkMassSigma 0.5 % lognormal standard deviation (in dex) in initial BH seed masses
187
+ %----- (specific options for on-the-fly friends-of-friends based BH seeding: FOF on)
188
+ MinFoFMassForNewSeed 10. % minimum mass of FOF group (stars or DM) to get seed, in code units
189
+ TimeBetOnTheFlyFoF 1.01 % time (in code units, e.g. scale-factor) between on-the-fly FOF searches
190
+ %--- accretion
191
+ SinkAccretionFactor 1.0 % multiplier for mdot (relative to model)
192
+ SinkEddingtonFactor 1e100 % fraction of eddington to cap (can be >1)
193
+ SinkNgbFactor 1.0 % multiplier for kernel neighbors for BH
194
+ SinkMaxAccretionRadius BOXSIZE % max radius for BH neighbor search/accretion (code units)
195
+ SinkRadiativeEfficiency 5e-7 % radiative efficiency (for accretion and feedback)
196
+ %--- feedback
197
+ SinkFeedbackFactor 1.0 % generic feedback strength multiplier
198
+ BH_FluxMomentumFactor 0.0 % multiply radiation pressure (BH_PHOTONMOMENTUM), set it to zero to avoid launching gas from rad. pressure
199
+ Sink_f_accretion 0.7 % fraction of gas swallowed by BH (BH_WIND options)
200
+ Sink_v_outflow 100. % velocity (km/s) of BAL outflow (BH_WIND options)
201
+ Sink_internal_temperature 1.0e3 % internal temperature (K) of BAL outflow (BH_WIND_SPAWN)
202
+ Sink_wind_particle_mass JET_PART_MASS % mass of 'virtual wind particles' in code units (BH_WIND_SPAWN)
203
+ Sink_wind_particle_mass_MS MS_WIND_PART_MASS % mass of 'virtual wind particles' in MS stellar winds, if 0 then Sink_wind_particle_mass is used, in code units (SINGLE_STAR_FB_WINDS)
204
+
205
+
206
+ %-------------------------------------------------------------------------
207
+ %------------------ Grackle cooling module -----------------
208
+ %-------------------------------------------------------------------------
209
+
210
+ %-------------- Grackle UVB file (COOL_GRACKLE on)
211
+ GrackleDataFile CloudyData_UVB=HM2012.h5
212
+
213
+
214
+
215
+ %-------------------------------------------------------------------------
216
+ %------------------ Driven Turbulence (Large-Eddy boxes) -----------------
217
+ %-------------------------------------------------------------------------
218
+
219
+ %-------------- Turbulent stirring parameters (TURB_DRIVING on)
220
+ TurbDrive_ApproxRMSVturb TURB_SIGMA
221
+ TurbDrive_MinWavelength TURB_MINLAMBDA
222
+ TurbDrive_MaxWavelength TURB_MAXLAMBDA
223
+ TurbDrive_SolenoidalFraction 1
224
+ TurbDrive_CoherenceTime TURB_COHERENCE_TIME
225
+ TurbDrive_DrivingSpectrum 1
226
+ TurbDrive_RandomNumberSeed 42
227
+ TurbDrive_TimeBetweenTurbUpdates DTSNAP
228
+ TurbDrive_TimeBetTurbSpectrum DTSNAP
229
+
230
+
231
+ %-------------------------------------------------------------------------------------------------
232
+ %------------------ Non-Standard Dark Matter, Dark Energy, Gravity, or Expansion -----------------
233
+ %-------------------------------------------------------------------------------------------------
234
+
235
+ %-------------- Parameters for non-standard or time-dependent Gravity/Dark Energy/Expansion (GR_TABULATED_COSMOLOGY on)
236
+ DarkEnergyConstantW -1 % time-independent DE parameter w, used only if no table
237
+ TabulatedCosmologyFile CosmoTbl % table with cosmological parameters
238
+
239
+ %--- Developer-Mode Parameters (usually hard-coded, but set manually if DEVELOPER_MODE is on) --------
240
+ ErrTolTheta 0.5 % 0.7=standard
241
+ ErrTolForceAcc 0.0025 % 0.0025=standard
242
+ ErrTolIntAccuracy 0.01 % <0.02
243
+ CourantFac 0.2 % <0.20
244
+ MaxRMSDisplacementFac 0.125 % <0.25
245
+ MaxNumNgbDeviation 0.05 % <<DesNumNgb (values<1 are fine)
246
+ AGS_MaxNumNgbDeviation 0.10 % tolerance in Nngb (make larger than gas)
247
+ %---- Magneto-Hydrodynamics Developer-Mode Parameters (MAGNETIC on)
248
+ %--- Dedner 2002 div-cleaning parameters
249
+ DivBcleaningParabolicSigma 1.0 % (3D~0.7-1.0,2D~0.2-0.3)
250
+ DivBcleaningHyperbolicSigma 1.0 % (~1)
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: makecloud
3
+ Version: 1.0.0
4
+ Summary: Generate turbulent molecular cloud initial conditions for GIZMO/STARFORGE simulations
5
+ Author: Dávid Guszejnov
6
+ Author-email: "Michael Y. Grudić" <mgrudic@flatironinstitute.org>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/mikegrudic/MakeCloud
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: numpy
12
+ Requires-Dist: scipy
13
+ Requires-Dist: h5py
14
+ Requires-Dist: docopt
15
+
16
+ # MakeCloud
17
+
18
+ Requires the following packages:
19
+ numpy
20
+ scipy
21
+ h5py
22
+ docopt
23
+
24
+ If you want to use glassy initial conditions, you will need <a href=https://data.obs.carnegiescience.edu/starforge/glass_orig.npy>this file</a>, and you should point the script to it via the --glass_path option.
25
+
26
+ By default, we generate turbulent velocity fields on the fly but then store that data somewhere so we don't have to do all those FFTs again next. You'll have to specify the path where the files get stored via the --turb_path option.
27
+
28
+ #Usage
29
+
30
+ Run python MakeCloud.py -h for instructions
31
+
32
+ e.g. if I wanted a 10^6 solar mass cloud of radius 100pc, resolved in 10^6 gas cells, I would do
33
+
34
+ python MakeCloud.py --M=1e6 --R=100 --N=1000000
@@ -0,0 +1,9 @@
1
+ MakeCloud.py,sha256=-GTTBdi9z0Gi5lXg3kKK_eQnmyDvCHD_9QoD23jSH0s,30189
2
+ calcGMCProps.py,sha256=WLwbbdaDhOuINiA1jvoJPBw8_93yBxBq1DsjIZKdS-M,2431
3
+ makecloud/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ makecloud/params.txt,sha256=K61wpr_WZfQd8SgiiDn07w7nOH3qIaAhGggvhw7uHt8,13195
5
+ makecloud-1.0.0.dist-info/METADATA,sha256=XzsXVNd6_zNGdaHGBLj_awoT0YWecQ-V43xnIVh6jtI,1182
6
+ makecloud-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
7
+ makecloud-1.0.0.dist-info/entry_points.txt,sha256=BxkpuR1g-bMmiKvujKWfycXjSsuUTgiu5kBj-OzPyww,45
8
+ makecloud-1.0.0.dist-info/top_level.txt,sha256=SBWk-PJKnv2Qp6YTEm1Aq43vWa5e-eGb9foeV4qrBJs,33
9
+ makecloud-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ makecloud = MakeCloud:main
@@ -0,0 +1,3 @@
1
+ MakeCloud
2
+ calcGMCProps
3
+ makecloud