def _determine_ions_from_lines(line_database, lines): """ Figure out what ions are necessary to produce the desired lines """ if line_database is not None: line_database = LineDatabase(line_database) ion_list = line_database.parse_subset_to_ions(lines) else: ion_list = [] if lines == 'all' or lines == ['all']: for k,v in six.iteritems(atomic_number): for j in range(v+1): ion_list.append((k, j+1)) else: for line in lines: linen = line.split() if len(linen) >= 2: ion_list.append((linen[0], from_roman(linen[1]))) elif len(linen) == 1: num_states = atomic_number[linen[0]] for j in range(num_states+1): ion_list.append((linen[0], j+1)) else: raise RuntimeError("Cannot add a blank ion.") return uniquify(ion_list)
def add_ion_fields(ds, ions, ftype='gas', ionization_table=None, field_suffix=False, line_database=None, sampling_type='local', particle_type=None): """ Preferred method for adding ion fields to a yt dataset. Select ions based on the selection indexing set up in :class:`~trident.LineDatabase.parse_subset_to_ions` function, that is, by specifying a list of strings where each string represents an ion or line. Strings are of one of three forms: * <element> * <element> <ion state> * <element> <ion state> <line_wavelength> If a line_database is selected, then the ions chosen will be a subset of the ions present in the equivalent :class:`~trident.LineDatabase`, nominally located in ``trident.__path__/data/line_lists``. For each ion species selected, four fields will be added (example for Mg II): * Ion fraction field. e.g. ("gas", 'Mg_p1_ion_fraction') * Number density field. e.g. ("gas", 'Mg_p1_number_density') * Density field. e.g. ("gas", 'Mg_p1_density') * Mass field. e.g. ("gas", 'Mg_p1_mass') This function is the preferred method for adding ion fields to one's dataset, but for more fine-grained control, one can also employ the :class:`~trident.add_ion_fraction_field`, :class:`~trident.add_ion_number_density_field`, :class:`~trident.add_ion_density_field`, :class:`~trident.add_ion_mass_field` functions individually. Fields are added assuming collisional ionization equilibrium and photoionization in the optically thin limit from a redshift-dependent metagalactic ionizing background using the ionization_table specified. **Parameters** :ds: yt dataset object This is the dataset to which the ion fraction field will be added. :ions: list of strings List of strings matching possible lines. Strings can be of the form: * Atom - Examples: "H", "C", "Mg" * Ion - Examples: "H I", "H II", "C IV", "Mg II" * Line - Examples: "H I 1216", "C II 1336", "Mg II 1240" If set to 'all', creates **all** ions for the first 30 elements: (ie hydrogen to zinc). If set to 'all' with ``line_database`` keyword set, then creates **all** ions associated with the lines specified in the equivalent :class:`~trident.LineDatabase`. :ionization_table: string, optional Path to an appropriately formatted HDF5 table that can be used to compute the ion fraction as a function of density, temperature, metallicity, and redshift. When set to None, it uses the table specified in ~/.trident/config Default: None :field_suffix: boolean, optional Determines whether or not to append a suffix to the field name that indicates what ionization table was used. Useful when using generating ion_fields that already exist in a dataset. :line_database: string, optional Ions are selected out of the set of ions present in the line_database constructed from the line list filename specified here. See :class:`~trident.LineDatabase` for more information. :ftype: string, optional This is deprecated and no longer necessary since all relevant fields are aliased to the 'gas' ftype. Default: 'gas' :sampling_type: string, optional This is deprecated and no longer necessary. Default: 'local' :particle_type: boolean, optional This is deprecated and no longer necessary. Default: 'auto' **Example** To add ionized hydrogen, doubly-ionized Carbon, and all of the Magnesium species fields to a dataset, you would run: >>> import yt >>> import trident >>> ds = yt.load('path/to/file') >>> trident.add_ion_fields(ds, ions=['H II', 'C III', 'Mg']) """ ion_list = [] if ionization_table is None: ionization_table = ion_table_filepath # Parse the ions given following the LineDatabase syntax # If line_database is set, then use the underlying file as the line list # to select ions from. if line_database is not None: line_database = LineDatabase(line_database) ion_list = line_database.parse_subset_to_ions(ions) # Otherwise, any ion can be selected (not just ones in the line list). else: if ions == 'all' or ions == ['all']: for k, v in atomic_number.items(): for j in range(v + 1): ion_list.append((k, j + 1)) else: for ion in ions: ionn = ion.split() if len(ionn) >= 2: ion_list.append((ionn[0], from_roman(ionn[1]))) elif len(ionn) == 1: num_states = atomic_number[ionn[0]] for j in range(num_states + 1): ion_list.append((ionn[0], j + 1)) else: raise RuntimeError("Cannot add a blank ion.") # make sure ion list is unique ion_list = uniquify(ion_list) # adding X_p#_ion_mass field triggers the addition of: # - X_P#_ion_fraction # - X_P#_number_density # - X_P#_density for (atom, ion) in ion_list: add_ion_mass_field(atom, ion, ds, ftype, ionization_table, field_suffix=field_suffix, sampling_type=sampling_type)