def test_absorption_spectrum_non_cosmo(self): """ This test generates an absorption spectrum from a simple light ray on a grid dataset """ lr = LightRay(COSMO_PLUS_SINGLE) ray_start = [0,0,0] ray_end = [1,1,1] lr.make_light_ray(start_position=ray_start, end_position=ray_end, fields=[('gas', 'temperature'), ('gas', 'density'), ('gas', 'H_p0_number_density')], data_filename='lightray.h5') sp = AbsorptionSpectrum(1200.0, 1300.0, 10001) my_label = 'HI Lya' field = ('gas', 'H_p0_number_density') wavelength = 1215.6700 # Angstromss f_value = 4.164E-01 gamma = 6.265e+08 mass = 1.00794 sp.add_line(my_label, field, wavelength, f_value, gamma, mass, label_threshold=1.e10) filename = "spectrum.h5" wavelength, flux = sp.make_spectrum('lightray.h5', output_file=filename, output_absorbers_file='lines.txt', use_peculiar_velocity=True) return filename
def test_absorption_spectrum_with_continuum(self): """ This test generates an absorption spectrum from a simple light ray on a grid dataset and adds Lyman alpha and Lyman continuum to it """ ds = load(ISO_GALAXY) lr = LightRay(ds) ray_start = ds.domain_left_edge ray_end = ds.domain_right_edge lr.make_light_ray( start_position=ray_start, end_position=ray_end, fields=['temperature', 'density', 'H_number_density'], data_filename='lightray.h5') sp = AbsorptionSpectrum(800.0, 1300.0, 5001) my_label = 'HI Lya' field = 'H_number_density' wavelength = 1215.6700 # Angstromss f_value = 4.164E-01 gamma = 6.265e+08 mass = 1.00794 sp.add_line(my_label, field, wavelength, f_value, gamma, mass, label_threshold=1.e10) my_label = 'Ly C' field = 'H_number_density' wavelength = 912.323660 # Angstroms normalization = 1.6e17 index = 3.0 sp.add_continuum(my_label, field, wavelength, normalization, index) filename = "spectrum.h5" wavelength, flux = sp.make_spectrum('lightray.h5', output_file=filename, line_list_file='lines.txt', use_peculiar_velocity=True) return filename
def test_absorption_spectrum_cosmo(self): """ This test generates an absorption spectrum from a compound light ray on a grid dataset """ lr = LightRay(COSMO_PLUS, 'Enzo', 0.0, 0.03) lr.make_light_ray( seed=1234567, fields=['temperature', 'density', 'H_number_density'], data_filename='lightray.h5') sp = AbsorptionSpectrum(900.0, 1800.0, 10000) my_label = 'HI Lya' field = 'H_number_density' wavelength = 1215.6700 # Angstromss f_value = 4.164E-01 gamma = 6.265e+08 mass = 1.00794 sp.add_line(my_label, field, wavelength, f_value, gamma, mass, label_threshold=1.e10) my_label = 'HI Lya' field = 'H_number_density' wavelength = 912.323660 # Angstroms normalization = 1.6e17 index = 3.0 sp.add_continuum(my_label, field, wavelength, normalization, index) filename = "spectrum.h5" wavelength, flux = sp.make_spectrum('lightray.h5', output_file=filename, line_list_file='lines.txt', use_peculiar_velocity=True) return filename
def test_absorption_spectrum_fits(self): """ This test generates an absorption spectrum and saves it as a fits file. """ lr = LightRay(COSMO_PLUS_SINGLE) ray_start = [0, 0, 0] ray_end = [1, 1, 1] lr.make_light_ray( start_position=ray_start, end_position=ray_end, fields=['temperature', 'density', 'H_number_density'], data_filename='lightray.h5') sp = AbsorptionSpectrum(900.0, 1800.0, 10000) my_label = 'HI Lya' field = 'H_number_density' wavelength = 1215.6700 # Angstromss f_value = 4.164E-01 gamma = 6.265e+08 mass = 1.00794 sp.add_line(my_label, field, wavelength, f_value, gamma, mass, label_threshold=1.e10) my_label = 'HI Lya' field = 'H_number_density' wavelength = 912.323660 # Angstroms normalization = 1.6e17 index = 3.0 sp.add_continuum(my_label, field, wavelength, normalization, index) wavelength, flux = sp.make_spectrum('lightray.h5', output_file='spectrum.fits', line_list_file='lines.txt', use_peculiar_velocity=True)
def test_equivalent_width_conserved(self): """ This tests that the equivalent width of the optical depth is conserved regardless of the bin width employed in wavelength space. Unresolved lines should still deposit optical depth into the spectrum. """ lr = LightRay(COSMO_PLUS_SINGLE) ray_start = [0, 0, 0] ray_end = [1, 1, 1] lr.make_light_ray( start_position=ray_start, end_position=ray_end, fields=['temperature', 'density', 'H_number_density'], data_filename='lightray.h5') my_label = 'HI Lya' field = 'H_number_density' wave = 1215.6700 # Angstromss f_value = 4.164E-01 gamma = 6.265e+08 mass = 1.00794 lambda_min = 1200 lambda_max = 1300 lambda_bin_widths = [1e-3, 1e-2, 1e-1, 1e0, 1e1] total_tau = [] for lambda_bin_width in lambda_bin_widths: n_lambda = ((lambda_max - lambda_min) / lambda_bin_width) + 1 sp = AbsorptionSpectrum(lambda_min=lambda_min, lambda_max=lambda_max, n_lambda=n_lambda) sp.add_line(my_label, field, wave, f_value, gamma, mass) wavelength, flux = sp.make_spectrum('lightray.h5') total_tau.append((lambda_bin_width * sp.tau_field).sum()) # assure that the total tau values are all within 1e-3 of each other for tau in total_tau: assert_almost_equal(tau, total_tau[0], 3)
def test_absorption_spectrum_with_zero_field(self): """ This test generates an absorption spectrum with some particle dataset """ ds = load(FIRE) lr = LightRay(ds) # Define species and associated parameters to add to continuum # Parameters used for both adding the transition to the spectrum # and for fitting # Note that for single species that produce multiple lines # (as in the OVI doublet), 'numLines' will be equal to the number # of lines, and f,gamma, and wavelength will have multiple values. HI_parameters = { 'name': 'HI', 'field': 'H_number_density', 'f': [.4164], 'Gamma': [6.265E8], 'wavelength': [1215.67], 'mass': 1.00794, 'numLines': 1, 'maxN': 1E22, 'minN': 1E11, 'maxb': 300, 'minb': 1, 'maxz': 6, 'minz': 0, 'init_b': 30, 'init_N': 1E14 } species_dicts = {'HI': HI_parameters} # Get all fields that need to be added to the light ray fields = [('gas', 'temperature')] for s, params in species_dicts.items(): fields.append(params['field']) # With a single dataset, a start_position and # end_position or trajectory must be given. # Trajectory should be given as (r, theta, phi) lr.make_light_ray(start_position=ds.arr([0., 0., 0.], 'unitary'), end_position=ds.arr([1., 1., 1.], 'unitary'), solution_filename='test_lightraysolution.txt', data_filename='test_lightray.h5', fields=fields) # Create an AbsorptionSpectrum object extending from # lambda = 900 to lambda = 1800, with 10000 pixels sp = AbsorptionSpectrum(900.0, 1400.0, 50000) # Iterate over species for s, params in species_dicts.items(): # Iterate over transitions for a single species for i in range(params['numLines']): # Add the lines to the spectrum sp.add_line(s, params['field'], params['wavelength'][i], params['f'][i], params['Gamma'][i], params['mass'], label_threshold=1.e10) # Make and save spectrum wavelength, flux = sp.make_spectrum('test_lightray.h5', output_file='test_spectrum.h5', line_list_file='test_lines.txt', use_peculiar_velocity=True)
def make_spectrum(self, ray, lines='all', output_file=None, use_peculiar_velocity=True, observing_redshift=0.0, ly_continuum=True, store_observables=False, min_tau=1e-3, njobs="auto"): """ Make a spectrum from ray data depositing the desired lines. Make sure to pass this function a LightRay object and potentially also a list of strings representing what lines you'd like to actually have be deposited in your final spectrum. **Parameters** :ray: string or dataset Ray dataset filename or a loaded ray dataset :lines: list of strings List of strings that determine which lines will be added to the spectrum. List can include things like "C", "O VI", or "Mg II ####", where #### would be the integer wavelength value of the desired line. If set to 'all', includes all lines in LineDatabase set in SpectrumGenerator. Default: 'all' :output_file: optional, string Filename of output if you wish to save the spectrum immediately without any further processing. File formats are chosen based on the filename extension. ".h5" for HDF5, ".fits" for FITS, and everything else is ASCII. Equivalent of calling :class:`~trident.SpectrumGenerator.save_spectrum`. Default: None :use_peculiar_velocity: optional, bool If True, include the effects of doppler redshift of the gas in shifting lines in the final spectrum. Default: True :observing_redshift: optional, float This is the value of the redshift at which the observer of this spectrum exists. In most cases, this will be a redshift of 0. Default: 0. :ly_continuum: optional, boolean If any H I lines are used in the line list, this assures a Lyman continuum will be included in the spectral generation. Lyman continuum begins at final Lyman line deposited (Ly 39 = 912.32 A) not at formal Lyman Limit (911.76 A) so as to not have a gap between final Lyman lines and continuum. Uses power law of index 3 and normalization to match opacity of final Lyman lines. Default: True :store_observables: optional, boolean If set to true, observable properties for each cell in the light ray will be saved for each line in the line list. Properties include the column density, tau, thermal b, and the wavelength where tau was deposited. Best applied for a reasonable number of lines. Default: False :min_tau: optional, float This value determines size of the wavelength window used to deposit lines or continua. The wavelength window is expanded until the optical depth at the edge is below this value. If too high, this will result in features appearing cut off at the edges. Decreasing this will make features smoother but will also increase run time. An increase by a factor of ten will result in roughly a 2x slow down. Default: 1e-3. :njobs: optional, int or "auto" The number of process groups into which the loop over absorption lines will be divided. If set to -1, each absorption line will be deposited by exactly one processor. If njobs is set to a value less than the total number of available processors (N), then the deposition of an individual line will be parallelized over (N / njobs) processors. If set to "auto", it will first try to parallelize over the list of lines and only parallelize the line deposition if there are more processors than lines. This is the optimal strategy for parallelizing spectrum generation. Default: "auto" **Example** Make a one zone ray and generate a COS spectrum for it including only Oxygen VI, Mg II, and all Carbon lines, and plot to disk. >>> import trident >>> ray = trident.make_onezone_ray() >>> sg = trident.SpectrumGenerator('COS') >>> sg.make_spectrum(ray, lines=['O VI', 'Mg II', 'C']) >>> sg.plot_spectrum('spec_raw.png') """ self.observing_redshift = observing_redshift if isinstance(ray, str): ray = load(ray) ad = ray.all_data() # Clear out any previous spectrum that existed first self.clear_spectrum() active_lines = self.line_database.parse_subset(lines) # Make sure we've produced all the necessary # derived fields if they aren't native to the data for line in active_lines: # if successful, means line.field is in ds.derived_field_list try: disk_field = ad._determine_fields(line.field)[0] # otherwise we probably need to add the field to the dataset except: my_ion = \ line.field[:line.field.find("number_density")] on_ion = my_ion.split("_") # Add the field if greater than level 1 ionization # because there is only one naming convention for these fields: # X_pY_number_density if on_ion[1]: my_lev = int(on_ion[1][1:]) + 1 mylog.info("Creating %s from ray's density, " "temperature, metallicity." % (line.field)) add_ion_number_density_field( on_ion[0], my_lev, ray, ionization_table=self.ionization_table) # If level 1 ionization, check to see if other name for # field is present in dataset else: my_lev = 1 alias_field = ('gas', "".join([my_ion, 'p0_number_density'])) # Don't add the X_number_density if X_p0_number_density is # in dataset already if alias_field in ray.derived_field_list: line.field = alias_field # But add the field if neither X_number_density nor # X_p0_number_density is in the dataset else: mylog.info("Creating %s from ray's density, " "temperature, metallicity." % (line.field)) add_ion_number_density_field( on_ion[0], my_lev, ray, ionization_table=self.ionization_table) self.add_line(line.identifier, line.field, float(line.wavelength), float(line.f_value), float(line.gamma), atomic_mass[line.element], label_threshold=1e3) # If there are H I lines present, add a Lyman continuum source # Lyman continuum source starts at wavelength where last Lyman line # is deposited (Ly 40), as opposed to true Lyman Limit at 911.763 A # so there won't be a gap between lines and continuum. Using # power law of index 3.0 and normalization to match the opacity of # the final Lyman line into the FUV. H_lines = self.line_database.select_lines(source_list=active_lines, element='H', ion_state='I') if (len(H_lines) > 0) and (ly_continuum == True): self.add_continuum('Ly C', H_lines[0].field, 912.32336, 1.6e17, 3.0) AbsorptionSpectrum.make_spectrum( self, ray, output_file=None, line_list_file=None, use_peculiar_velocity=use_peculiar_velocity, observing_redshift=observing_redshift, store_observables=store_observables, min_tau=min_tau, njobs=njobs)