示例#1
0
    def eq_bands(self,
                 Tgas,
                 mole_fraction=None,
                 path_length=None,
                 pressure=None,
                 levels='all',
                 drop_lines=True):
        ''' Return all vibrational bands as a list of spectra for a spectrum
        calculated under equilibrium.

        By default, drop_lines is set to True so line_survey cannot be done on
        spectra. See drop_lines for more information

        Parameters
        ----------

        Tgas: float
            Gas temperature (K)

        mole_fraction: float
            database species mole fraction. If None, Factory mole fraction is used.

        path_length: float
            slab size (cm). If None, Factory mole fraction is used.

        pressure: float
            pressure (bar). If None, the default Factory pressure is used.

        Other Parameters
        ----------------

        levels: ``'all'``, int, list of str
            calculate only bands that feature certain levels. If ``'all'``, all
            bands are returned. If N (int), return bands for the first N levels
            (sorted by energy). If list of str, return for all levels in the list.
            The remaining levels are also calculated and returned merged together
            in the ``'others'`` key. Default ``'all'``

        drop_lines: boolean
            if False remove the line database from each bands. Helps save a lot
            of space, but line survey cannot be performed anymore. Default ``True``.

        Returns
        -------

        bands: list of :class:`~radis.spectrum.spectrum.Spectrum` objects

        Use .get(something) to get something among ['radiance', 'radiance_noslit',
                'absorbance', etc...]

        Or directly .plot(something) to plot it

        Notes
        -----

        Process:

        - Calculate line strenghts correcting the CDSD reference one. 
        - Then call the main routine that sums over all lines


        '''

        try:

            # update defaults
            if path_length is not None:
                self.input.path_length = path_length
            if mole_fraction is not None:
                self.input.mole_fraction = mole_fraction
            if pressure is not None:
                self.input.pressure_mbar = pressure * 1e3
            if not is_float(Tgas):
                raise ValueError(
                    'Tgas should be float. Use ParallelFactory for multiple cases'
                )
            assert type(levels) in [str, list, int]
            if type(levels) == str:
                assert levels == 'all'
            # Temporary:
            if type(levels) == int:
                raise NotImplementedError

            # Get temperatures
            self.input.Tgas = Tgas
            self.input.Tvib = Tgas  # just for info
            self.input.Trot = Tgas  # just for info

            # Init variables
            pressure_mbar = self.input.pressure_mbar
            mole_fraction = self.input.mole_fraction
            path_length = self.input.path_length
            verbose = self.verbose

            # %% Retrieve from database if exists
            if self.autoretrievedatabase:
                s = self._retrieve_bands_from_database()
                if s is not None:
                    return s

            # Print conditions
            if verbose:
                print('Calculating Equilibrium bands')
                self.print_conditions()

            # Start
            t0 = time()

            # %% Make sure database is loaded
            if self.df0 is None:
                raise AttributeError('Load databank first (.load_databank())')

            if not 'band' in self.df0:
                self._add_bands()

            # %% Calculate the spectrum
            # ---------------------------------------------------
            t0 = time()

            self._reinitialize()

            # --------------------------------------------------------------------

            # First calculate the linestrength at given temperature
            self._calc_linestrength_eq(Tgas)
            self._cutoff_linestrength()

            # ----------------------------------------------------------------------

            # Calculate line shift
            self._calc_lineshift()

            # ----------------------------------------------------------------------

            # Line broadening

            # ... calculate broadening  FWHM
            self._calc_broadening_FWHM()

            # ... find weak lines and calculate semi-continuum (optional)
            I_continuum = self._calculate_pseudo_continuum()
            if I_continuum:
                raise NotImplementedError(
                    'pseudo continuum not implemented for bands')

            # ... apply lineshape and get absorption coefficient
            # ... (this is the performance bottleneck)
            wavenumber, abscoeff_v_bands = self._calc_broadening_bands()
            #    :         :
            #   cm-1    1/(#.cm-2)

            #            # ... add semi-continuum (optional)
            #            abscoeff_v_bands = self._add_pseudo_continuum(abscoeff_v_bands, I_continuum)

            # ----------------------------------------------------------------------
            # Remove certain bands
            if levels != 'all':
                # Filter levels that feature the given energy levels. The rest
                # is stored in 'others'
                lines = self.df1
                # We need levels to be explicitely stated for given molecule
                assert hasattr(lines, 'viblvl_u')
                assert hasattr(lines, 'viblvl_l')
                # Get bands to remove
                merge_bands = []
                for band in abscoeff_v_bands:  # note: could be vectorized with pandas str split. # TODO
                    viblvl_l, viblvl_u = band.split('->')
                    if not viblvl_l in levels and not viblvl_u in levels:
                        merge_bands.append(band)
                # Remove bands from bandlist and add them to `others`
                abscoeff_others = np.zeros_like(wavenumber)
                for band in merge_bands:
                    abscoeff = abscoeff_v_bands.pop(band)
                    abscoeff_others += abscoeff
                abscoeff_v_bands['others'] = abscoeff_others
                if verbose:
                    print('{0} bands grouped under `others`'.format(
                        len(merge_bands)))

            # ----------------------------------------------------------------------
            # Generate spectra

            # Progress bar for spectra generation
            Nbands = len(abscoeff_v_bands)
            if self.verbose:
                print('Generating bands ({0})'.format(Nbands))
            pb = ProgressBar(Nbands, active=self.verbose)
            if Nbands < 100:
                pb.set_active(False)  # hide for low line number

            # Generate spectra
            s_bands = {}
            for i, (band, abscoeff_v) in enumerate(abscoeff_v_bands.items()):

                # incorporate density of molecules (see equation (A.16) )
                density = mole_fraction * ((pressure_mbar * 100) /
                                           (k_b * Tgas)) * 1e-6
                #  :
                # (#/cm3)
                abscoeff = abscoeff_v * density  # cm-1

                # ==============================================================================
                # Warning
                # ---------
                # if the code is extended to multi-species, then density has to be added
                # before lineshape broadening (as it would not be constant for all species)
                # ==============================================================================

                # get absorbance (technically it's the optical depth `tau`,
                #                absorbance `A` being `A = tau/ln(10)` )
                absorbance = abscoeff * path_length

                # Generate output quantities
                transmittance_noslit = exp(-absorbance)
                emissivity_noslit = 1 - transmittance_noslit
                radiance_noslit = calc_radiance(
                    wavenumber,
                    emissivity_noslit,
                    Tgas,
                    unit=self.units['radiance_noslit'])

                # ----------------------------- Export:

                lines = self.df1[self.df1.band == band]
                # if band == 'others': all lines will be None. # TODO
                populations = None  # self._get_vib_populations(lines)

                # Store results in Spectrum class
                if drop_lines:
                    lines = None
                    if self.save_memory:
                        try:
                            del self.df1  # saves some memory
                        except AttributeError:  # already deleted
                            pass
                conditions = self.get_conditions()
                # Add band name and hitran band name in conditions
                conditions.update({'band': band})

                if lines:

                    def add_attr(attr):
                        if attr in lines:
                            if band == 'others':
                                val = 'N/A'
                            else:
                                # all have to be the same
                                val = lines[attr].iloc[0]
                            conditions.update({attr: val})

                    add_attr('band_htrn')
                    add_attr('viblvl_l')
                    add_attr('viblvl_u')
                s = Spectrum(
                    quantities={
                        'abscoeff': (wavenumber, abscoeff),
                        'absorbance': (wavenumber, absorbance),
                        'emissivity_noslit': (wavenumber, emissivity_noslit),
                        'transmittance_noslit':
                        (wavenumber, transmittance_noslit),
                        # (mW/cm2/sr/nm)
                        'radiance_noslit': (wavenumber, radiance_noslit),
                    },
                    conditions=conditions,
                    populations=populations,
                    lines=lines,
                    units=self.units,
                    cond_units=self.cond_units,
                    waveunit=self.params.waveunit,  # cm-1
                    name=band,
                    # dont check input (much faster, and Spectrum
                    warnings=False,
                    # is freshly baken so probably in a good format
                )

                #            # update database if asked so
                #            if self.autoupdatedatabase:
                #                self.SpecDatabase.add(s)
                #                                                     # Tvib=Trot=Tgas... but this way names in a database
                #                                                     # generated with eq_spectrum are consistent with names
                #                                                     # in one generated with non_eq_spectrum

                s_bands[band] = s

                pb.update(i)  # progress bar
            pb.done()

            if verbose:
                print(('... process done in {0:.1f}s'.format(time() - t0)))

            return s_bands

        except:
            # An error occured: clean before crashing
            self._clean_temp_file()
            raise
示例#2
0
    def non_eq_bands(self,
                     Tvib,
                     Trot,
                     Ttrans=None,
                     mole_fraction=None,
                     path_length=None,
                     pressure=None,
                     vib_distribution='boltzmann',
                     rot_distribution='boltzmann',
                     levels='all',
                     return_lines=None):
        ''' Calculate vibrational bands in non-equilibrium case. Calculates
        absorption with broadened linestrength and emission with broadened
        Einstein coefficient.

        Parameters
        ----------

        Tvib: float
            vibrational temperature [K]
            can be a tuple of float for the special case of more-than-diatomic
            molecules (e.g: CO2)

        Trot: float
            rotational temperature [K]

        Ttrans: float
            translational temperature [K]. If None, translational temperature is
            taken as rotational temperature (valid at 1 atm for times above ~ 2ns
            which is the RT characteristic time)

        mole_fraction: float
            database species mole fraction. If None, Factory mole fraction is used.

        path_length: float
            slab size (cm). If None, Factory mole fraction is used.

        pressure: float
            pressure (bar). If None, the default Factory pressure is used.

        Other Parameters
        ----------------

        levels: ``'all'``, int, list of str
            calculate only bands that feature certain levels. If ``'all'``, all
            bands are returned. If N (int), return bands for the first N levels
            (sorted by energy). If list of str, return for all levels in the list.
            The remaining levels are also calculated and returned merged together
            in the ``'others'`` key. Default ``'all'``

        return_lines: boolean
            if ``True`` returns each band with its line database. Can produce big
            spectra! Default ``True``
            DEPRECATED. Now use export_lines attribute in Factory

        Returns
        -------

        Returns :class:`~radis.spectrum.spectrum.Spectrum` object

        Use .get(something) to get something among ['radiance', 'radiance_noslit',
        'absorbance', etc...]

        Or directly .plot(something) to plot it

        '''

        try:

            # check inputs, update defaults
            if path_length is not None:
                self.input.path_length = path_length
            if mole_fraction is not None:
                self.input.mole_fraction = mole_fraction
            if pressure is not None:
                self.input.pressure_mbar = pressure * 1e3
            if not (is_float(Tvib) or isinstance(Tvib, tuple)):
                raise TypeError(
                    'Tvib should be float, or tuple (got {0})'.format(
                        type(Tvib)) +
                    'For parallel processing use ParallelFactory with a ' +
                    'list of float or a list of tuple')
            singleTvibmode = is_float(Tvib)
            if not is_float(Trot):
                raise ValueError(
                    'Trot should be float. Use ParallelFactory for multiple cases'
                )
            assert type(levels) in [str, list, int]
            if type(levels) == str:
                assert levels == 'all'
            else:
                if len(levels) != len(set(levels)):
                    raise ValueError('levels list has duplicates')
            if not vib_distribution in ['boltzmann']:
                raise ValueError(
                    'calculate per band not meaningful if not Boltzmann')
            # Temporary:
            if type(levels) == int:
                raise NotImplementedError
            if return_lines is not None:
                warn(
                    DeprecationWarning(
                        'return_lines replaced with export_lines attribute in Factory'
                    ))
                self.misc.export_lines = return_lines

            # Get translational temperature
            Tgas = Ttrans
            if Tgas is None:
                Tgas = Trot  # assuming Ttrans = Trot
            self.input.Tgas = Tgas
            self.input.Tvib = Tvib
            self.input.Trot = Trot

            # Init variables
            path_length = self.input.path_length
            mole_fraction = self.input.mole_fraction
            pressure_mbar = self.input.pressure_mbar
            verbose = self.verbose

            # %% Retrieve from database if exists
            if self.autoretrievedatabase:
                s = self._retrieve_bands_from_database()
                if s is not None:
                    return s

            # Print conditions
            if verbose:
                print('Calculating Non-Equilibrium bands')
                self.print_conditions()

            # %% Make sure database is loaded
            self._check_line_databank()
            self._check_noneq_parameters(vib_distribution, singleTvibmode)

            if self.df0 is None:
                raise AttributeError('Load databank first (.load_databank())')

            # Make sure database has pre-computed non equilibrium quantities
            # (Evib, Erot, etc.)
            if not 'Evib' in self.df0:
                self._calc_noneq_parameters()

            if not 'Aul' in self.df0:
                self._calc_weighted_trans_moment()
                self._calc_einstein_coefficients()

            if not 'band' in self.df0:
                self._add_bands()

            # %% Calculate the spectrum
            # ---------------------------------------------------
            t0 = time()

            self._reinitialize()

            # ----------------------------------------------------------------------
            # Calculate Populations, Linestrength and Emission Integral
            # (Note: Emission Integral is non canonical quantity, equivalent to
            #  Linestrength for absorption)
            self._calc_populations_noneq(Tvib, Trot)
            self._calc_linestrength_noneq()
            self._calc_emission_integral()

            # ----------------------------------------------------------------------
            # Cutoff linestrength
            self._cutoff_linestrength()

            # ----------------------------------------------------------------------

            # Calculate lineshift
            self._calc_lineshift()

            # ----------------------------------------------------------------------

            # Line broadening

            # ... calculate broadening  FWHM
            self._calc_broadening_FWHM()

            # ... find weak lines and calculate semi-continuum (optional)
            I_continuum = self._calculate_pseudo_continuum()
            if I_continuum:
                raise NotImplementedError(
                    'pseudo continuum not implemented for bands')

            # ... apply lineshape and get absorption coefficient
            # ... (this is the performance bottleneck)
            wavenumber, abscoeff_v_bands, emisscoeff_v_bands = self._calc_broadening_noneq_bands(
            )
            #    :         :            :
            #   cm-1    1/(#.cm-2)   mW/sr/cm_1

            #            # ... add semi-continuum (optional)
            #            abscoeff_v_bands = self._add_pseudo_continuum(abscoeff_v_bands, I_continuum)

            # ----------------------------------------------------------------------
            # Remove bands
            if levels != 'all':
                # Filter levels that feature the given energy levels. The rest
                # is stored in 'others'
                lines = self.df1
                # We need levels to be explicitely stated for given molecule
                assert hasattr(lines, 'viblvl_u')
                assert hasattr(lines, 'viblvl_l')
                # Get bands to remove
                merge_bands = []
                for band in abscoeff_v_bands:  # note: could be vectorized with pandas str split. # TODO
                    viblvl_l, viblvl_u = band.split('->')
                    if not viblvl_l in levels and not viblvl_u in levels:
                        merge_bands.append(band)
                # Remove bands from bandlist and add them to `others`
                abscoeff_others = np.zeros_like(wavenumber)
                emisscoeff_others = np.zeros_like(wavenumber)
                for band in merge_bands:
                    abscoeff = abscoeff_v_bands.pop(band)
                    emisscoeff = emisscoeff_v_bands.pop(band)
                    abscoeff_others += abscoeff
                    emisscoeff_others += emisscoeff
                abscoeff_v_bands['others'] = abscoeff_others
                emisscoeff_v_bands['others'] = emisscoeff_others
                if verbose:
                    print('{0} bands grouped under `others`'.format(
                        len(merge_bands)))

            # ----------------------------------------------------------------------
            # Generate spectra

            # Progress bar for spectra generation
            Nbands = len(abscoeff_v_bands)
            if self.verbose:
                print('Generating bands ({0})'.format(Nbands))
            pb = ProgressBar(Nbands, active=self.verbose)
            if Nbands < 100:
                pb.set_active(False)  # hide for low line number

            # Create spectra
            s_bands = {}
            for i, band in enumerate(abscoeff_v_bands):
                abscoeff_v = abscoeff_v_bands[band]
                emisscoeff_v = emisscoeff_v_bands[band]

                # incorporate density of molecules (see equation (A.16) )
                density = mole_fraction * ((pressure_mbar * 100) /
                                           (k_b * Tgas)) * 1e-6
                #  :
                # (#/cm3)

                abscoeff = abscoeff_v * density  # cm-1
                emisscoeff = emisscoeff_v * density  # m/sr/cm3/cm_1

                # ==============================================================================
                # Warning
                # ---------
                # if the code is extended to multi-species, then density has to be added
                # before lineshape broadening (as it would not be constant for all species)
                # ==============================================================================

                # get absorbance (technically it's the optical depth `tau`,
                #                absorbance `A` being `A = tau/ln(10)` )

                # Generate output quantities
                absorbance = abscoeff * path_length  # (adim)
                transmittance_noslit = exp(-absorbance)

                if self.input.self_absorption:
                    # Analytical output of computing RTE over a single slab of constant
                    # emissivity and absorption coefficient
                    b = abscoeff == 0  # optically thin mask
                    radiance_noslit = np.zeros_like(emisscoeff)
                    radiance_noslit[~b] = emisscoeff[~b] / \
                        abscoeff[~b]*(1-transmittance_noslit[~b])
                    radiance_noslit[b] = emisscoeff[b] * path_length
                else:
                    # Note that for k -> 0,
                    radiance_noslit = emisscoeff * \
                        path_length    # (mW/sr/cm2/cm_1)

                # Convert `radiance_noslit` from (mW/sr/cm2/cm_1) to (mW/sr/cm2/nm)
                radiance_noslit = convert_rad2nm(radiance_noslit, wavenumber,
                                                 'mW/sr/cm2/cm_1',
                                                 'mW/sr/cm2/nm')
                # Convert 'emisscoeff' from (mW/sr/cm3/cm_1) to (mW/sr/cm3/nm)
                emisscoeff = convert_emi2nm(emisscoeff, wavenumber,
                                            'mW/sr/cm3/cm_1', 'mW/sr/cm3/nm')
                # Note: emissivity not defined under non equilibrium

                # ----------------------------- Export:

                lines = self.df1[self.df1.band == band]
                # Note: if band == 'others':  # for others: all will be None. # TODO. FIXME

                populations = self.get_populations(
                    self.misc.export_populations)

                if not self.misc.export_lines:
                    lines = None

                # Store results in Spectrum class
                if self.save_memory:
                    try:
                        # saves some memory (note: only once 'lines' is discarded)
                        del self.df1
                    except AttributeError:  # already deleted
                        pass
                conditions = self.get_conditions()
                conditions.update({'thermal_equilibrium': False})

                # Add band name and hitran band name in conditions

                def add_attr(attr):
                    ''' # TODO: implement properly'''
                    if attr in lines:
                        if band == 'others':
                            val = 'N/A'
                        else:
                            # all have to be the same
                            val = lines[attr].iloc[0]
                        conditions.update({attr: val})

                add_attr('band_htrn')
                add_attr('viblvl_l')
                add_attr('viblvl_u')
                s = Spectrum(
                    quantities={
                        'abscoeff': (wavenumber, abscoeff),
                        'absorbance': (wavenumber, absorbance),
                        # (mW/cm3/sr/nm)
                        'emisscoeff': (wavenumber, emisscoeff),
                        'transmittance_noslit':
                        (wavenumber, transmittance_noslit),
                        # (mW/cm2/sr/nm)
                        'radiance_noslit': (wavenumber, radiance_noslit),
                    },
                    conditions=conditions,
                    populations=populations,
                    lines=lines,
                    units=self.units,
                    cond_units=self.cond_units,
                    waveunit=self.params.waveunit,  # cm-1
                    name=band,
                    # dont check input (much faster, and Spectrum
                    warnings=False,
                    # is freshly baken so probably in a good format
                )

                #            # update database if asked so
                #            if self.autoupdatedatabase:
                #                self.SpecDatabase.add(s, add_info=['Tvib', 'Trot'], add_date='%Y%m%d')

                s_bands[band] = s

                pb.update(i)  # progress bar
            pb.done()

            if verbose:
                print(('... process done in {0:.1f}s'.format(time() - t0)))

            return s_bands

        except:
            # An error occured: clean before crashing
            self._clean_temp_file()
            raise