forked from NuGrid/NuPyCEE
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchem_evol.py
5808 lines (4601 loc) · 217 KB
/
chem_evol.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
'''
Chemical Evolution - chem_evol.py
Functionality
=============
This is the superclass inherited by the SYGMA and the OMEGA modules. It provides
common functions for initialization and for the evolution of one single timestep.
Made by
=======
MAY2015: B. Cote
The core of this superclass is a reorganization of the functions previously found in
earlier versions of SYGMA:
v0.1 NOV2013: C. Fryer, C. Ritter
v0.2 JAN2014: C. Ritter
v0.3 APR2014: C. Ritter, J. F. Navarro, F. Herwig, C. Fryer, E. Starkenburg,
M. Pignatari, S. Jones, K. Venn, P. A. Denissenkov & the
NuGrid collaboration
v0.4 FEB2015: C. Ritter, B. Cote
v0.5 OCT2016: B. Cote, C. Ritter, A. Paul
Usage
=====
See sygma.py and omega.py
'''
# Standard packages
import numpy as np
import time as t_module
import copy
import math
import random
import os
import sys
import re
from pylab import polyfit
from scipy.integrate import quad
from scipy.integrate import dblquad
from scipy.interpolate import interp1d
from scipy.interpolate import UnivariateSpline
from scipy.interpolate import interp1d
from mpl_toolkits.mplot3d import Axes3D
from imp import *
import matplotlib.pyplot as plt
# Variable enabling to work in notebooks
global notebookmode
notebookmode=True
# Set the working space to the current directory
global global_path
try:
if os.environ['SYGMADIR']:
global_path = os.environ['SYGMADIR']
except KeyError:
global_path=os.getcwd()
global_path=global_path+'/'
# Import the class that reads the input yield tables
import read_yields as ry
class chem_evol(object):
'''
Input parameters (chem_evol.py)
================
special_timesteps : integer
Number of special timesteps. This option (already activated by default)
is activated when special_timesteps > 0. It uses a logarithm timestep
scheme which increases the duration of timesteps throughout the simulation.
Default value : 30
dt : float
Duration of the first timestep [yr] if special_timesteps is activated.
Duration of each timestep if special_timesteps is desactivated.
Default value : 1.0e6
tend : float
Total duration of the simulation [yr].
Default value : 13.0e9
imf_bdys : list
Upper and lower mass limits of the initial mass function (IMF) [Mo].
Default value : [0.1,100]
imf_yields_range : list
Initial mass of stars that contribute to stellar ejecta [Mo].
Default value : [1,30]
imf_type : string
Choices : 'salpeter', 'chabrier', 'kroupa', 'alphaimf'
'alphaimf' creates a custom IMF with a single power-law covering imf_bdys.
Default value : 'kroupa'
alphaimf : float
Aplha index of the custom IMF, dN/dM = Constant * M^-alphaimf
Default value : 2.35
imf_bdys_pop3 : list
Upper and lower mass limits of the IMF of PopIII stars [Mo].
Default value : [0.1,100]
imf_yields_range_pop3 : list
Initial mass of stars that contribute to PopIII stellar ejecta [Mo].
PopIII stars ejecta taken from Heger et al. (2010)
Default value : [10,30]
iniZ : float
Initial metallicity of the gas in mass fraction (e.g. Solar Z = 0.02).
Choices : 0.0, 0.0001, 0.001, 0.006, 0.01, 0.02
(-1.0 to use non-default yield tables)
Default value : 0.0
Z_trans : float
Variable used when interpolating stellar yields as a function of Z.
Transition Z below which PopIII yields are used, and above which default
yields are used.
Default value : -1 (not active)
mgal : float
Initial mass of gas in the simulation [Mo].
Default value : 1.6e11
sn1a_on : boolean
True or False to include or exclude the contribution of SNe Ia.
Default value : True
sn1a_rate : string
SN Ia delay-time distribution function used to calculate the SN Ia rate.
Choices :
'power_law' - custom power law, set parameter with beta_pow (similar to Maoz & Mannucci 2012)
'gauss' - gaussian DTD, set parameter with gauss_dtd
'exp' - exponential DTD, set parameter with exp_dtd
'maoz' - specific power law from Maoz & Mannucci (2012)
Default value : 'power_law'
sn1a_energy : float
Energy ejected by single SNIa event. Units in erg.
Default value : 1e51
ns_merger_on : boolean
True or False to include or exclude the contribution of neutron star mergers.
Note : If t_nsm_coal or nsm_dtd_power is not used (see below), the delay time
distribution of neutron star mergers is given by the standard population synthesis
models of Dominik et al. (2012), using Z = 0.002 and Z = 0.02. In this case, the
total number of neutron star mergers can be tuned using f_binary and f_merger
(see below).
Default value : False
f_binary : float
Binary fraction for massive stars used to determine the total number of neutron
star mergers in a simple stellar population.
Default value : 1.0
f_merger : float
Fraction of massive star binary systems that lead to neutron star mergers in a
simple stellar population.
Default value : 0.0008
beta_pow : float
Slope of the power law for custom SN Ia rate, R = Constant * t^-beta_pow.
Default value : -1.0
gauss_dtd : list
Contains parameter for the gaussian DTD: first the characteristic time [yrs] (gaussian center)
and then the width of the distribution [yrs].
Default value : [3.3e9,6.6e8]
exp_dtd : float
Characteristic delay time [yrs] for the e-folding DTD.
nb_1a_per_m : float
Number of SNe Ia per stellar mass formed in a simple stellar population.
Default value : 1.0e-03
direct_norm_1a : float
Normalization coefficient for SNIa rate integral.
Default: deactived but replaces the usage of teh nb_1a_per_m when its value is larger than zero.
transitionmass : float
Initial mass which marks the transition from AGB to massive stars [Mo].
Default value : 8.0
exclude_masses : list
Contains initial masses in yield tables to be excluded from the simulation;
Default value : []
table : string
Path pointing toward the stellar yield tables for massive and AGB stars.
Default value : 'yield_tables/agb_and_massive_stars_nugrid_MESAonly_fryer12delay.txt' (NuGrid)
sn1a_table : string
Path pointing toward the stellar yield table for SNe Ia.
Default value : 'yield_tables/sn1a_t86.txt' (Tielemann et al. 1986)
nsmerger_table : string
Path pointing toward the r-process yield tables for neutron star mergers
Default value : 'yield_tables/r_process_rosswog_2014.txt' (Rosswog et al. 2013)
iniabu_table : string
Path pointing toward the table of initial abuncances in mass fraction.
Default value : 'yield_tables/iniabu/iniab2.0E-02GN93.ppn'
yield_interp : string
if 'None' : no yield interpolation, no interpolation of total ejecta
if 'lin' - Simple linear yield interpolation.
if 'wiersma' - Interpolation method which makes use of net yields
as used e.g. in Wiersma+ (2009); Does not require net yields.
if netyields_on is true than makes use of given net yields
else calculates net yields from given X0 in yield table.
Default : 'lin'
netyields_on : boolean
if true assumes that yields (input from table parameter)
are net yields.
Default : false.
total_ejecta_interp : boolean
if true then interpolates total ejecta given in yield tables
over initial mass range.
Default : True
stellar_param_on : boolean
if true reads in additional stellar parameter given in table stellar_param_table.
Default : true in sygma and false in omega
stellar_param_table: string
Path pointoing toward the table hosting the evolution of stellar parameter
derived from stellar evolution calculations.
Default table : 'yield_tables/isotope_yield_table_MESA_only_param.txt'
iolevel : int
Specifies the amount of output for testing purposes (up to 3).
Default value : 0
poly_fit_dtd : list
Array of polynomial coefficients of a customized delay-time distribution
function for SNe Ia. The polynome can be of any order.
Example : [0.2, 0.3, 0.1] for rate_snIa(t) = 0.2*t**2 + 0.3*t + 0.1
Note : Must be used with the poly_fit_range parameter (see below)
Default value : np.array([]) --> Deactivated
poly_fit_range : list --> [t_min,t_max]
Time range where the customized delay-time distribution function for
SNe Ia will be applied for a simple stellar population.
Default value : np.array([]) --> Deactivated
mass_sampled : list
Stellar masses that are sampled to eject yields in a stellar population.
Warning : The use of this parameter bypasses the IMF calculation and
do not ensure a correlation with the star formation rate. Each sampled
mass will eject the exact amount of mass give in the stellar yields.
Default value : np.array([]) --> Deactivated
scale_cor : 2D list
Determine the fraction of yields ejected for any given stellar mass bin.
Example : [ [1.0,8], [0.5,100] ] means that stars with initial mass between
0 and 8 Msu will eject 100% of their yields, and stars with initial mass
between 8 and 100 will eject 50% of their yields. There is no limit for
the number of [%,M_upper_limit] arrays used.
Default value : np.array([]) --> Deactivated
t_nsm_coal : float
When greater than zero, t_nsm_coal sets the delay time (since star formation)
after which all neutron star mergers occur in a simple stellar population.
Default value : -1 --> Deactivated
nsm_dtd_power : 3-index array --> [t_min, t_max, slope_of_the_power_law]
When used, nsm_dtd_power defines a delay time distribution for neutron
star mergers in the form of a power law, for a simple stellar population.
Exemple: [1.e7, 1.e10, -1.] --> t^-1 from 10 Myr to 10 Gyr
Default value : [] --> Deactivated
nb_nsm_per_m : float
Number of neutron star mergers per stellar mass formed in a simple
stellar population.
Note : This parameter is only considered when t_nsm_coal or nsm_dtd_power
is used to define the delay time of neutron star mergers.
Default value : -1 --> Deactivated
m_ej_nsm : float
Mass ejected per neutron star merger event.
Default value : 2.5e-02
Run example
===========
See sygma.py and omega.py
'''
##############################################
## Constructor ##
##############################################
def __init__(self, imf_type='kroupa', alphaimf=2.35, imf_bdys=[0.1,100], \
sn1a_rate='power_law', iniZ=0.0, dt=1e6, special_timesteps=30, \
nsmerger_bdys=[8, 100], tend=13e9, mgal=1.6e11, transitionmass=8, iolevel=0, \
ini_alpha=True, table='yield_tables/agb_and_massive_stars_nugrid_MESAonly_fryer12delay.txt', \
hardsetZ=-1, sn1a_on=True, sn1a_table='yield_tables/sn1a_t86.txt',\
sn1a_energy=1e51, ns_merger_on=False, bhns_merger_on=False,\
f_binary=1.0, f_merger=0.0008, t_merger_max=1.0e10,\
m_ej_nsm = 2.5e-02, nb_nsm_per_m=-1.0, \
t_nsm_coal=-1.0, m_ej_bhnsm=2.5e-02, nsm_dtd_power=[],\
bhnsmerger_table = 'yield_tables/r_process_rosswog_2014.txt', \
nsmerger_table = 'yield_tables/r_process_rosswog_2014.txt',\
iniabu_table='', extra_source_on=False, \
extra_source_table=['yield_tables/extra_source.txt'], \
f_extra_source=[1.0], \
extra_source_mass_range=[[8,30]], \
extra_source_exclude_Z=[[]], \
pop3_table='yield_tables/popIII_heger10.txt', \
imf_bdys_pop3=[0.1,100], imf_yields_range_pop3=[10,30], \
starbursts=[], beta_pow=-1.0,gauss_dtd=[3.3e9,6.6e8],\
exp_dtd=2e9,nb_1a_per_m=1.0e-3,direct_norm_1a=-1,Z_trans=-1, \
f_arfo=1, imf_yields_range=[1,30],exclude_masses=[],\
netyields_on=False,wiersmamod=False,yield_interp='lin',\
total_ejecta_interp=True, tau_ferrini=False,\
input_yields=False,t_merge=-1.0,stellar_param_on=True,\
stellar_param_table='yield_tables/stellar_feedback_nugrid_MESAonly.txt',\
popIII_on=True, out_follows_E_rate=False, \
t_dtd_poly_split=-1.0, \
ism_ini=np.array([]), nsmerger_dtd_array=np.array([]),\
bhnsmerger_dtd_array=np.array([]),
ytables_in=np.array([]), zm_lifetime_grid_nugrid_in=np.array([]),\
isotopes_in=np.array([]), ytables_pop3_in=np.array([]),\
zm_lifetime_grid_pop3_in=np.array([]), ytables_1a_in=np.array([]),\
ytables_nsmerger_in=np.array([]), \
dt_in=np.array([]),dt_split_info=np.array([]),\
ej_massive=np.array([]), ej_agb=np.array([]),\
ej_sn1a=np.array([]), ej_massive_coef=np.array([]),\
ej_agb_coef=np.array([]), ej_sn1a_coef=np.array([]),\
dt_ssp=np.array([]), poly_fit_dtd_5th=np.array([]),\
mass_sampled_ssp=np.array([]), scale_cor_ssp=np.array([]),\
poly_fit_range=np.array([])):
# Initialize the history class which keeps the simulation in memory
self.history = self.__history()
self.const = self.__const()
# If we need to assume the current baryonic ratio ...
if mgal < 0.0:
# Use a temporary mgal value for chem_evol __init__ function
mgal = 1.0e06
self.bar_ratio = True
# If we use the input mgal parameter ...
else:
self.bar_ratio = False
# Attribute the input parameters to the current object
self.history.mgal = mgal
self.history.tend = tend
self.history.dt = dt
self.history.sn1a_rate = sn1a_rate
self.history.imf_bdys = imf_bdys
self.history.transitionmass = transitionmass
self.history.nsmerger_bdys = nsmerger_bdys
self.history.f_binary = f_binary
self.history.f_merger = f_merger
self.mgal = mgal
self.transitionmass = transitionmass
self.iniZ = iniZ
self.imf_bdys=imf_bdys
self.nsmerger_bdys=nsmerger_bdys
self.imf_bdys_pop3=imf_bdys_pop3
self.imf_yields_range_pop3=imf_yields_range_pop3
self.extra_source_on = extra_source_on
self.f_extra_source= f_extra_source
self.extra_source_mass_range=extra_source_mass_range
self.extra_source_exclude_Z=extra_source_exclude_Z
self.table = table
self.iniabu_table = iniabu_table
self.sn1a_table = sn1a_table
self.nsmerger_table = nsmerger_table
self.bhnsmerger_table = bhnsmerger_table
self.extra_source_table = extra_source_table
self.pop3_table = pop3_table
self.hardsetZ = hardsetZ
self.starbursts = starbursts
self.imf_type = imf_type
self.alphaimf = alphaimf
self.sn1a_on = sn1a_on
self.sn1a_energy=sn1a_energy
self.ns_merger_on = ns_merger_on
self.bhns_merger_on = bhns_merger_on
self.nsmerger_dtd_array = nsmerger_dtd_array
self.len_nsmerger_dtd_array = len(nsmerger_dtd_array)
self.bhnsmerger_dtd_array = bhnsmerger_dtd_array
self.len_bhnsmerger_dtd_array = len(bhnsmerger_dtd_array)
self.f_binary = f_binary
self.f_merger = f_merger
self.t_merger_max = t_merger_max
self.m_ej_nsm = m_ej_nsm
self.m_ej_bhnsm = m_ej_bhnsm
self.nb_nsm_per_m = nb_nsm_per_m
self.t_nsm_coal = t_nsm_coal
self.nsm_dtd_power = nsm_dtd_power
self.special_timesteps = special_timesteps
self.iolevel = iolevel
self.nb_1a_per_m = nb_1a_per_m
self.direct_norm_1a=direct_norm_1a
self.Z_trans = Z_trans
if sn1a_rate == 'maoz':
self.beta_pow = -1.0
else:
self.beta_pow = beta_pow
self.gauss_dtd = gauss_dtd
self.exp_dtd=exp_dtd
self.normalized = False # To avoid normalizing SN Ia rate more than once
self.nsm_normalized = False # To avoid normalizing NS merger rate more than once
self.bhnsm_normalized = False # To avoid normalizing BHNS merger rate more than once
self.f_arfo = f_arfo
self.imf_yields_range = imf_yields_range
self.exclude_masses=exclude_masses
self.netyields_on=netyields_on
self.wiersmamod=wiersmamod
self.yield_interp=yield_interp
self.out_follows_E_rate = out_follows_E_rate
self.total_ejecta_interp=total_ejecta_interp
self.tau_ferrini = tau_ferrini
self.t_merge = t_merge
self.ism_ini = ism_ini
self.dt_in = dt_in
self.t_dtd_poly_split = t_dtd_poly_split
self.poly_fit_dtd_5th = poly_fit_dtd_5th
self.poly_fit_range = poly_fit_range
self.stellar_param_table=stellar_param_table
self.stellar_param_on=stellar_param_on
# Normalization constants for the Kroupa IMF
if imf_type == 'kroupa':
self.p0 = 1.0
self.p1 = 0.08**(-0.3 + 1.3)
self.p2 = 0.5**(-1.3 + 2.3)
self.p3 = 1**(-2.3 +2.3)
# Define the broken power-law of Ferrini IMF approximation
self.norm_fer = [3.1,1.929,1.398,0.9113,0.538,0.3641,0.2972,\
0.2814,0.2827,0.298,0.305,0.3269,0.3423,0.3634]
self.alpha_fer = [0.6,0.35,0.15,-0.15,-0.6,-1.05,-1.4,-1.6,-1.7,\
-1.83,-1.85,-1.9,-1.92,-1.94]
self.m_up_fer = [0.15,0.2,0.24,0.31,0.42,0.56,0.76,1.05,1.5,\
3.16,4.0,10.0,20.0,120]
for i_fer in range(0,len(self.norm_fer)):
self.alpha_fer[i_fer] = self.alpha_fer[i_fer] + 1
self.norm_fer[i_fer] = self.norm_fer[i_fer]/(self.alpha_fer[i_fer])
# Parameter that determines if not enough gas is available for star formation
self.not_enough_gas_count = 0
self.not_enough_gas = False
# Check for incompatible inputs - Error messages
self.__check_inputs()
# Initialisation of the timesteps
timesteps = self.__get_timesteps()
self.history.timesteps = timesteps
self.nb_timesteps = len(timesteps)
# If the yield tables has already been read previously ...
if input_yields:
# Assign the input yields and lifetimes
self.ytables = ytables_in
self.zm_lifetime_grid_nugrid = zm_lifetime_grid_nugrid_in
self.history.isotopes = isotopes_in
self.nb_isotopes = len(self.history.isotopes)
self.ytables_pop3 = ytables_pop3_in
self.zm_lifetime_grid_pop3 = zm_lifetime_grid_pop3_in
self.ytables_1a = ytables_1a_in
self.ytables_nsmerger = ytables_nsmerger_in
self.default_yields = True
self.extra_source_on = False
self.ytables_extra = 0
# If the yield tables need to be read from the files ...
else:
# Initialisation of the yield tables
self.__set_yield_tables()
# Initialisation of the composition of the gas reservoir
ymgal = self._get_iniabu()
self.len_ymgal = len(ymgal)
# Initialisation of the timesteps
timesteps = self.__get_timesteps()
self.history.timesteps = timesteps
self.nb_timesteps = len(timesteps)
# Initialisation of the storing arrays
mdot, ymgal, ymgal_massive, ymgal_agb, ymgal_1a, ymgal_nsm, ymgal_bhnsm,\
mdot_massive, mdot_agb, mdot_1a, mdot_nsm, mdot_bhnsm, sn1a_numbers,\
sn2_numbers, nsm_numbers, bhnsm_numbers, imf_mass_ranges, \
imf_mass_ranges_contribution, imf_mass_ranges_mtot = \
self._get_storing_arrays(ymgal)
# Initialisation of the composition of the gas reservoir
if len(self.ism_ini) > 0:
for i_ini in range(0,self.len_ymgal):
ymgal[0][i_ini] = self.ism_ini[i_ini]
# Output information
if iolevel >= 1:
print 'Number of timesteps: ', '{:.1E}'.format(len(timesteps))
# Add the initialized arrays to the history class
self.history.gas_mass.append(sum(ymgal[0]))
self.history.ism_iso_yield.append(ymgal[0])
self.history.ism_iso_yield_agb.append(ymgal_agb[0])
self.history.ism_iso_yield_1a.append(ymgal_1a[0])
self.history.ism_iso_yield_nsm.append(ymgal_nsm[0])
self.history.ism_iso_yield_bhnsm.append(ymgal_bhnsm[0])
self.history.ism_iso_yield_massive.append(ymgal_massive[0])
self.history.sn1a_numbers.append(0)
self.history.nsm_numbers.append(0)
self.history.bhnsm_numbers.append(0)
self.history.sn2_numbers.append(0)
self.history.m_locked = []
self.history.m_locked_agb = []
self.history.m_locked_massive = []
# Keep track of the mass-loss rate of massive stars and SNe Ia
self.massive_ej_rate = []
for k in range(self.nb_timesteps + 1):
self.massive_ej_rate.append(0.0)
self.sn1a_ej_rate = []
for k in range(self.nb_timesteps + 1):
self.sn1a_ej_rate.append(0.0)
# Attribute arrays and variables to the current object
self.mdot = mdot
self.ymgal = ymgal
self.ymgal_massive = ymgal_massive
self.ymgal_agb = ymgal_agb
self.ymgal_1a = ymgal_1a
self.ymgal_nsm = ymgal_nsm
self.ymgal_bhnsm = ymgal_bhnsm
self.mdot_massive = mdot_massive
self.mdot_agb = mdot_agb
self.mdot_1a = mdot_1a
self.mdot_nsm = mdot_nsm
self.mdot_bhnsm = mdot_bhnsm
self.sn1a_numbers = sn1a_numbers
self.nsm_numbers = nsm_numbers
self.bhnsm_numbers = bhnsm_numbers
self.sn2_numbers = sn2_numbers
self.imf_mass_ranges = imf_mass_ranges
self.imf_mass_ranges_contribution = imf_mass_ranges_contribution
self.imf_mass_ranges_mtot = imf_mass_ranges_mtot
# Set the initial time and metallicity
zmetal = self._getmetallicity(0)
self.history.metallicity.append(zmetal)
self.t = 0
self.history.age.append(self.t)
self.zmetal = zmetal
# Get coefficients for the fraction of white dwarfs fit (2nd poly)
self.__get_coef_wd_fit()
# Output information
if iolevel > 0:
print '### Start with initial metallicity of ','{:.4E}'.format(zmetal)
print '###############################'
##############################################
# Check Inputs #
##############################################
def __check_inputs(self):
'''
This function checks for incompatible input entries and stops
the simulation if needed.
'''
self.need_to_quit = False
# Total duration of the simulation
if self.history.tend > 1.5e10:
print 'Error - tend must be less than or equal to 1.5e10 years.'
self.need_to_quit = True
# Timestep
if self.history.dt > self.history.tend:
print 'Error - dt must be smaller or equal to tend.'
self.need_to_quit = True
# Transition mass between AGB and massive stars
#if #(self.transitionmass <= 7)or(self.transitionmass > 12):
# print 'Error - transitionmass must be between 7 and 12 Mo.'
# self.need_to_quit = True
if not self.transitionmass ==8:
print 'Warning: Non-default transitionmass chosen. Use in agreement '\
'with yield input!'
# IMF
if not self.imf_type in ['salpeter','chabrier','kroupa','input', \
'alphaimf','chabrieralpha','fpp', 'kroupa93']:
print 'Error - Selected imf_type is not available.'
self.need_to_quit = True
# IMF yields range
#if self.imf_yields_range[0] < 1:
# print 'Error - imf_yields_range lower boundary must be >= 1.'
#self.need_to_quit = True
#if (self.imf_yields_range[0] >= self.imf_bdys[1]) or \
# (self.imf_yields_range[0] <= self.imf_bdys[0]) or \
# (self.imf_yields_range[1] >= self.imf_bdys[1]):
if ((self.imf_yields_range[0] > self.imf_bdys[1]) or \
(self.imf_yields_range[1] < self.imf_bdys[0])):
print 'Error - part of imf_yields_range must be within imf_bdys.'
self.need_to_quit = True
if (self.transitionmass<self.imf_yields_range[0])\
or (self.transitionmass>self.imf_yields_range[1]):
print 'Error - Transitionmass outside imf yield range'
self.need_to_quit = True
if self.ns_merger_on:
if ((self.nsmerger_bdys[0] > self.imf_bdys[1]) or \
(self.nsmerger_bdys[1] < self.imf_bdys[0])):
print 'Error - part of nsmerger_bdys must be within imf_bdys.'
self.need_to_quit = True
# SN Ia delay-time distribution function
if not self.history.sn1a_rate in \
['exp','gauss','maoz','power_law']:
print 'Error - Selected sn1a_rate is not available.'
self.need_to_quit = True
# Initial metallicity for the gas
#if not self.iniZ in [0.0, 0.0001, 0.001, 0.006, 0.01, 0.02]:
# print 'Error - Selected iniZ is not available.'
# self.need_to_quit = True
# If popIII stars are used ...
if self.iniZ == 0.0:
# IMF and yield boundary ranges
if not self.imf_yields_range_pop3 == [10,30]:
print 'Error - Selected imf_yields_range_pop3 not included yet.'
self.need_to_quit = True
if (self.imf_yields_range_pop3[0] >= self.imf_bdys_pop3[1]) or \
(self.imf_yields_range_pop3[1] <= self.imf_bdys_pop3[0]):
print 'Error - imf_yields_range_pop3 must be within \
imf_bdys_pop3.'
self.need_to_quit = True
if self.netyields_on == True:
print 'Error - net yields setting not usable with PopIII at the moment.'
self.need_to_quit = True
# If input poly fit DTD, the applicable range must be specified
if len(self.poly_fit_dtd_5th) > 0:
if not len(self.poly_fit_range) == 2:
print 'Error - poly_fit_range must be specified when ',\
'using the poly_fit_dtd_5th parameter the SNe Ia DTD.'
self.need_to_quit = True
if self.extra_source_on:
lt=len(self.extra_source_table)
lf=len(self.f_extra_source)
lmr=len(self.extra_source_mass_range)
leZ=len(self.extra_source_exclude_Z)
if (not lt == lf):
print 'Error - parameter extra_source_table and f_extra_source not of equal size'
self.need_to_quit = True
if (not lt == lmr):
print 'Error - parameter extra_source_table and extra_source_mass_range not of equal size'
self.need_to_quit = True
if (not lt == leZ):
print 'Error - parameter extra_source_table and extra_source_exclude_Z not of equal size'
self.need_to_quit = True
##############################################
# Get Iniabu #
##############################################
def _get_iniabu(self):
'''
This function returns the initial gas reservoir, ymgal, containing
the mass of all the isotopes considered by the stellar yields.
'''
# Zero metallicity gas reservoir
if self.iniZ == 0:
# If an input iniabu table is provided ...
if len(self.iniabu_table) > 0:
iniabu=ry.iniabu(global_path + self.iniabu_table)
if self.iolevel >0:
print 'Use initial abundance of ', self.iniabu_table
ymgal_gi = np.array(iniabu.iso_abundance(self.history.isotopes)) * \
self.mgal
else:
# Get the primordial composition of Walker et al. (1991)
iniabu_table = 'yield_tables/iniabu/iniab_bb_walker91.txt'
ytables_bb = ry.read_yield_sn1a_tables( \
global_path+iniabu_table, self.history.isotopes)
# Assign the composition to the gas reservoir
ymgal_gi = ytables_bb.get(quantity='Yields') * self.mgal
# Output information
if self.iolevel > 0:
print 'Use initial abundance of ', iniabu_table
# Already enriched gas reservoir
else:
# If an input iniabu table is provided ...
if len(self.iniabu_table) > 0:
iniabu=ry.iniabu(global_path + self.iniabu_table)
if self.iolevel > 0:
print 'Use initial abundance of ', self.iniabu_table
# If NuGrid's yields are used ...
#else self.default_yields:
else:
# Define all the Z and abundance input files considered by NuGrid
ini_Z = [0.01, 0.001, 0.0001, 0.02, 0.006, 0.00001, 0.000001]
ini_list = ['iniab1.0E-02GN93.ppn', 'iniab1.0E-03GN93_alpha.ppn', \
'iniab1.0E-04GN93_alpha.ppn', 'iniab2.0E-02GN93.ppn', \
'iniab6.0E-03GN93_alpha.ppn', \
'iniab1.0E-05GN93_alpha_scaled.ppn', \
'iniab1.0E-06GN93_alpha_scaled.ppn']
# Pick the composition associated to the input iniZ
for metal in ini_Z:
if metal == float(self.iniZ):
iniabu = ry.iniabu(global_path + \
'yield_tables/iniabu/' + ini_list[ini_Z.index(metal)])
if self.iolevel>0:
print 'Use initial abundance of ', \
ini_list[ini_Z.index(metal)]
break
# Input file for the initial composition ...
#else:
# iniabu=ry.iniabu(global_path + iniabu_table)
# print 'Use initial abundance of ', iniabu_table
# Assign the composition to the gas reservoir
ymgal_gi = np.array(iniabu.iso_abundance(self.history.isotopes)) * \
self.mgal
# Make sure the total mass of gas does not exceed mgal
if sum(ymgal_gi) > self.mgal:
ymgal_gi[0] = ymgal_gi[0] - (sum(ymgal_gi) - self.mgal)
# Return the gas reservoir
return ymgal_gi
##############################################
# Get Timesteps #
##############################################
def __get_timesteps(self):
'''
This function calculates and returns the duration of every timestep.
'''
# Declaration of the array containing the timesteps
timesteps_gt = []
# If the timesteps are given as an input ...
if len(self.dt_in) > 0:
# Copy the timesteps
timesteps_gt = self.dt_in
# If the timesteps need to be calculated ...
else:
# If all the timesteps have the same duration ...
if self.special_timesteps <= 0:
# Make sure the last timestep is equal to tend
counter = 0
step = 1
laststep = False
t = 0
t0 = 0
while(True):
counter+=step
if (self.history.tend/self.history.dt)==0:
if (self.history.dt*counter)>self.history.tend:
break
else:
if laststep==True:
break
if (self.history.dt*counter+step)>self.history.tend:
counter=(self.history.tend/self.history.dt)
laststep=True
t=counter
timesteps_gt.append(int(t-t0)*self.history.dt)
t0=t
# If the special timestep option is chosen ...
if self.special_timesteps > 0:
# Use a logarithm scheme
times1 = np.logspace(np.log10(self.history.dt), \
np.log10(self.history.tend), self.special_timesteps)
times1 = [0] + list(times1)
timesteps_gt = np.array(times1[1:]) - np.array(times1[:-1])
# If a timestep needs to be added to be synchronized with
# the external program managing merger trees ...
if self.t_merge > 0.0:
# Declare the new timestep array
timesteps_new = []
# Find the interval where the step needs to be added
i_temp = 0
t_temp = timesteps_gt[0]
while t_temp < self.t_merge:
timesteps_new.append(timesteps_gt[i_temp])
i_temp += 1
t_temp += timesteps_gt[i_temp]
# Add the extra timestep
dt_up_temp = t_temp - self.t_merge
dt_low_temp = timesteps_gt[i_temp] - dt_up_temp
timesteps_new.append(dt_low_temp)
timesteps_new.append(dt_up_temp)
# Keep the t_merger index in memory
self.i_t_merger = i_temp
# Add the rest of the timesteps
# Skip the current one that just has been split
for i_dtnew in range(i_temp+1,len(timesteps_gt)):
timesteps_new.append(timesteps_gt[i_dtnew])
# Replace the timesteps array to be returned
timesteps_gt = timesteps_new
# Return the duration of all timesteps
return timesteps_gt
##############################################
# Get Storing Arrays #
##############################################
def _get_storing_arrays(self, ymgal):
'''
This function declares and returns all the arrays containing information
about the evolution of the stellar ejecta, the gas reservoir, the star
formation rate, and the number of core-collapse SNe, SNe Ia, neutron star
mergers, and white dwarfs.
Argument
========
ymgal : Initial gas reservoir. This function extends it to all timesteps
'''
# Number of timesteps and isotopes
nb_dt_gsa = self.nb_timesteps
nb_iso_gsa = len(self.history.isotopes)
# Stellar ejecta
mdot = []
for k in range(nb_dt_gsa):
mdot.append(nb_iso_gsa * [0])
# Gas reservoir
ymgal = [list(ymgal)]
for k in range(nb_dt_gsa):
ymgal.append(nb_iso_gsa * [0])
# Massive stars, AGB stars, SNe Ia ejecta, and neutron star merger ejecta
ymgal_massive = []
ymgal_agb = []
ymgal_1a = []
ymgal_nsm = []
ymgal_bhnsm = []
for k in range(nb_dt_gsa + 1):
ymgal_massive.append(nb_iso_gsa * [0])
ymgal_agb.append(nb_iso_gsa * [0])
ymgal_1a.append(nb_iso_gsa * [0])
ymgal_bhnsm.append(nb_iso_gsa * [0])
ymgal_nsm.append(nb_iso_gsa * [0])
mdot_massive = copy.deepcopy(mdot)
mdot_agb = copy.deepcopy(mdot)
mdot_1a = copy.deepcopy(mdot)
mdot_nsm = copy.deepcopy(mdot)
mdot_bhnsm = copy.deepcopy(mdot)
# Number of SNe Ia, core-collapse SNe, and neutron star mergers
sn1a_numbers = [0] * nb_dt_gsa
nsm_numbers = [0] * nb_dt_gsa
bhnsm_numbers = [0] * nb_dt_gsa
sn2_numbers = [0] * nb_dt_gsa
self.wd_sn1a_range = [0] * nb_dt_gsa
self.wd_sn1a_range1 = [0] * nb_dt_gsa
# Star formation
self.number_stars_born = [0] * (nb_dt_gsa + 1)
# Related to the IMF
self.history.imf_mass_ranges = [[]] * (nb_dt_gsa + 1)
imf_mass_ranges = []
imf_mass_ranges_contribution = [[]] * (nb_dt_gsa + 1)
imf_mass_ranges_mtot = [[]] * (nb_dt_gsa + 1)
# Return all the arrays
return mdot, ymgal, ymgal_massive, ymgal_agb, ymgal_1a, ymgal_nsm, ymgal_bhnsm,\
mdot_massive, mdot_agb, mdot_1a, mdot_nsm, mdot_bhnsm, sn1a_numbers,\
sn2_numbers, nsm_numbers, bhnsm_numbers, imf_mass_ranges,\
imf_mass_ranges_contribution, imf_mass_ranges_mtot
##############################################
# Get Coef WD Fit #
##############################################
def __get_coef_wd_fit(self):
'''