Beispiel #1
0
    def plotRatio(self, stageN, stageD, tRange=0, yr=0, label=1, title=1,  bw=0, semilogx = 1, verbose=0):
        '''
        Plots the ratio of the ionization equilibria of two stages of a given element

        self.plotRatio(stageN, stageD)
        stages = sequence of ions to be plotted, neutral == 1, fully stripped == Z+1
        stageN = numerator
        stageD = denominator
        tRange = temperature range, yr = ion fraction range

        '''
        ionN = util.zion2name(self.Z, stageN)
        ionD = util.zion2name(self.Z, stageD)
        ionNS = util.zion2spectroscopic(self.Z, stageN)
        ionDS = util.zion2spectroscopic(self.Z, stageD)

        atitle = 'ratio of %s to %s '%(ionN, ionD)
        alabel = 'ratio of %s to %s '%(ionNS, ionDS)
        print(atitle)
        if not hasattr(self, 'Ioneq'):
            print(' must first load or calculate an ionization equilibrium')
            return

        if bw:
            linestyle = ['k-','k--', 'k-.', 'k:']
            plt.rcParams['font.size'] = 16.
            lw = 2
        else:
            linestyle = ['b-','r--', 'g-.', 'm:']
            plt.rcParams['font.size'] = 14.
            lw = 2
        #
        goodTn = self.Ioneq[stageN - 1, :] > 0.
        goodTd = self.Ioneq[stageD -1, : ] > 0.
        realGoodT = np.logical_and(goodTn, goodTd)
        goodT = self.Temperature[realGoodT]
        goodR = self.Ioneq[stageN - 1, realGoodT]/self.Ioneq[stageD - 1, realGoodT]
        if not tRange:
            tRange = [goodT.min(), goodT.max()]
        if not yr:
            yr = [0.01, 1.1]
        xyr = list(tRange)
        xyr.extend(list(yr))
        #
        if semilogx:
            plt.semilogx(goodT, goodR, linestyle[0], lw=lw, label=alabel)
        else:
            plt.loglog(goodT, goodR, linestyle[0], lw=lw, label=alabel)
        plt.xlabel('Temperature (K)')
        plt.ylabel('Ratio')
        if title:
            atitle = 'CHIANTI Ionization Equilibrium'
            plt.title(atitle)
        plt.legend(loc='lower right')
        plt.tight_layout()
        self.Ratio={'Temperature':goodT, 'Ratio':goodR, 'label':alabel}
Beispiel #2
0
    def freeBound(self, wavelength, include_abundance=True, include_ioneq=True, use_verner=True, **kwargs):
        """
        Calculate the free-bound emission of an ion. The result is returned as a 2D array to the
        `free_bound_emission` attribute.

        The total free-bound continuum emissivity is given by,

        .. math::
           \\frac{dW}{dtdVd\lambda} = \\frac{1}{4\pi}\\frac{2}{hk_Bc^3m_e\sqrt{2\pi k_Bm_e}}\\frac{E^5}{T^{3/2}}\sum_i\\frac{\omega_i}{\omega_0}\sigma_i^{bf}\exp\left(-\\frac{E - I_i}{k_BT}\\right)

        where :math:`E=hc/\lambda` is the photon energy, :math:`\omega_i` and :math:`\omega_0`
        are the statistical weights of the :math:`i^{\mathrm{th}}` level of the recombined ion
        and the ground level of the recombing ion, respectively, :math:`\sigma_i^{bf}` is the
        photoionization cross-section, and :math:`I_i` is the ionization potential of level :math:`i`.
        This expression comes from Eq. 12 of [3]_. For more information about the free-bound continuum
        calculation, see `Peter Young's notes on free-bound continuum`_.

        The photoionization cross-sections are calculated using the methods of [2]_ for the
        transitions to the ground state and [1]_ for all other transitions. See
        `verner_cross_section` and `karzas_cross_section` for more details.

        .. _Peter Young's notes on free-bound continuum: http://www.pyoung.org/chianti/freebound.pdf

        The free-bound emission is in units of erg
        :math:`\mathrm{cm}^3\mathrm{s}^{-1}\mathrm{\mathring{A}}^{-1}\mathrm{str}^{-1}`. If the emission
        measure has been set, the units will be multiplied by :math:`\mathrm{cm}^{-5}` or
        :math:`\mathrm{cm}^{-3}`, depending on whether it is the line-of-sight or volumetric
        emission measure, respectively.

        Parameters
        ----------
        wavelength : array-like
            In units of angstroms
        include_abundance : `bool`, optional
            If True, include the ion abundance in the final output.
        include_ioneq : `bool`, optional
            If True, include the ionization equilibrium in the final output
        use_verner : `bool`, optional
            If True, cross-sections of ground-state transitions using [2]_, i.e. `verner_cross_section`

        Raises
        ------
        ValueError
            If no .fblvl file is available for this ion

        References
        ----------
        .. [1] Karzas and Latter, 1961, ApJSS, `6, 167
            <http://adsabs.harvard.edu/abs/1961ApJS....6..167K>`_
        .. [2] Verner & Yakovlev, 1995, A&AS, `109, 125
            <http://adsabs.harvard.edu/abs/1995A%26AS..109..125V>`_
        .. [3] Young et al., 2003, ApJSS, `144, 135
            <http://adsabs.harvard.edu/abs/2003ApJS..144..135Y>`_
        """
        wavelength = np.atleast_1d(wavelength)
        if wavelength.size < 2:
            print(' wavelength must have at least two values, current length %3i'%(wavelength.size))
            return
        self.NWavelength = wavelength.size
        # calculate the photon energy in erg
        photon_energy = ch_const.planck*(1.e8*ch_const.light)/wavelength
        prefactor = (2./np.sqrt(2.*np.pi)/(4.*np.pi)/(ch_const.planck*(ch_const.light**3)
                     * (ch_const.emass*ch_const.boltzmann)**(3./2.)))
        # read the free-bound level information for the recombined and recombining ion
        recombining_fblvl = ch_io.fblvlRead(self.ion_string)
        # get the multiplicity of the ground state of the recombining ion
        if 'errorMessage' in recombining_fblvl:
            omega_0 = 1.
        else:
            omega_0 = recombining_fblvl['mult'][0]
        
        self.Recombined_fblvl = ch_io.fblvlRead(self.nameDict['lower'])
        if 'errorMessage' in self.Recombined_fblvl:
#            raise ValueError('No free-bound information available for {}'.format(ch_util.zion2name(self.Z, self.stage)))
            errorMessage = 'No free-bound information available for {}'.format(ch_util.zion2name(self.Z, self.stage))
            fb_emiss = np.zeros((self.NTemperature, self.NWavelength), 'float64')
#            self.free_bound_emission = fb_emiss.squeeze()
            self.FreeBound = {'intensity':fb_emiss, 'temperature':self.Temperature,'wvl':wavelength,'em':self.Em, 'errorMessage':errorMessage}
            return

        energy_over_temp_factor = np.outer(1./(self.Temperature**1.5), photon_energy**5.).squeeze()
#        if self.NWavelength > 1:
#            print(' energy shape %5i %5i'%(energy_over_temp_factor.shape[0],energy_over_temp_factor.shape[1]))
#        else:
#            print(' energy size %5i'%(energy_over_temp_factor.size))
        # sum over levels of the recombined ion
        sum_factor = np.zeros((len(self.Temperature), len(wavelength)))
        for i,omega_i in enumerate(self.Recombined_fblvl['mult']):
            # ionization potential for level i
#            ip = self.ionization_potential - recombined_fblvl['ecm'][i]*ch_const.planck*ch_const.light
            ip = self.IprErg - self.Recombined_fblvl['ecm'][i]*ch_const.planck*ch_const.light
            # skip level if photon energy is not sufficiently high
            if ip < 0. or np.all(np.max(photon_energy) < (self.ionization_potential - ip)):
                continue
            # calculate cross-section
            if i == 0 and use_verner:
                cross_section = self.verner_cross_section(photon_energy)
            else:
                cross_section = self.karzas_cross_section(photon_energy, ip,
                                                          self.Recombined_fblvl['pqn'][i],
                                                          self.Recombined_fblvl['l'][i])
            scaled_energy = np.outer(1./(ch_const.boltzmann*self.Temperature), photon_energy - ip)
            # the exponential term can go to infinity for low temperatures
            # but if the cross-section is zero this does not matter
            scaled_energy[:,np.where(cross_section == 0.0)] = 0.0
            sum_factor += omega_i/omega_0*np.exp(-scaled_energy)*cross_section

        # combine factors
        fb_emiss = prefactor*energy_over_temp_factor*sum_factor.squeeze()
#        if self.NWavelength > 1:
#            print(' fb emiss.shape %5i %5i'%(fb_emiss.shape[0], fb_emiss.shape[1]))
#        else:
#            print(' fb emiss.size %5i'%(fb_emiss.size))
        # include abundance, ionization equilibrium, photon conversion, emission measure
        if include_abundance:
            fb_emiss *= self.abundance
        if include_ioneq:
            if self.NTemperature > 1:
                if self.NWavelength > 1:
#                    fb_emiss *= self.ioneq_one(self.stage, **kwargs)[:,np.newaxis]
                    fb_emiss *= self.IoneqOne[:,np.newaxis]
                else:
                    fb_emiss *= self.IoneqOne
            else:
                fb_emiss *= self.IoneqOne
        if self.Em is not None:
            if self.Em.size > 1:
                fb_emiss *= self.Em[:,np.newaxis]
            else:
                fb_emiss *= self.Em
                
        if ch_data.Defaults['flux'] == 'photon':
            fb_emiss /= photon_energy
        # the final units should be per angstrom
        fb_emiss /= 1e8

#        self.free_bound_emission = fb_emiss.squeeze()
        self.FreeBound = {'intensity':fb_emiss.squeeze(), 'temperature':self.Temperature,'wvl':wavelength,'em':self.Em, 'ions':self.ion_string}
Beispiel #3
0
    def calculate(self, temperature):
        """
        Calculate ion fractions for given temperature array using the total
        ionization and recombination rates.
        """
        self.Temperature = np.array(temperature, np.float64)
        if self.Temperature.size == 1:
            print(' temperature must be an array')
            return
        ionList = []
        chIons = []
        z = self.Z
        for stage in range(1, z+2):
            ionStr = util.zion2name(z, stage)
            ionList.append(ionStr)
            atom = ion(ionStr, temperature = self.Temperature, setup=0)
            atom.setupIonrec()
            atom.ionizRate()
            atom.recombRate()
            chIons.append(atom)

        ntemp = chIons[0].IonizRate['temperature'].size
        if ntemp == 1:
            ioneq = np.zeros((z+1), np.float64)
            factor = []
            for anIon in chIons:
                if hasattr(anIon, 'RecombRate') and hasattr(anIon, 'IonizRate'):
                    rat = anIon.IonizRate['rate']/anIon.RecombRate['rate']
                    factor.append(rat**2 + rat**(-2))
                else:
                    factor.append(0.)
            factor[0] = max(factor)
            factor[-1] = max(factor)
            ionmax = factor.index(min(factor))
            ioneq[ionmax] = 1.

            for iz in range(ionmax+1, z+1):
                ionrate = chIons[iz-1].IonizRate['rate']
                recrate = chIons[iz].RecombRate['rate']
                ioneq[iz] = ionrate*ioneq[iz-1]/recrate

            for iz in range(ionmax-1, -1, -1):
                ionrate = chIons[iz].IonizRate['rate']
                recrate = chIons[iz+1].RecombRate['rate']
                ioneq[iz] = recrate*ioneq[iz+1]/ionrate
            ionsum = ioneq.sum()
            ioneq = ioneq/ionsum
            self.Ioneq = ioneq
        else:
            ioneq = np.zeros((z+1,ntemp ), np.float64)
            for it in range(ntemp):
                factor = []
                for anIon in chIons:
                    if anIon.IonizRate is not None and anIon.RecombRate is not None:
                        ioniz = anIon.IonizRate['rate'][it]
                        recomb = anIon.RecombRate['rate'][it]
                        if ioniz == 0. or recomb == 0.:
                            rat = 1.e-100
                        else:
                            rat = anIon.IonizRate['rate'][it]/anIon.RecombRate['rate'][it]
                        try:
                            factor.append(rat**2 + rat**(-2))
                        except:
                            factor.append(0.)
                    else:
                        factor.append(0.)
                factor[0] = max(factor)
                factor[-1] = max(factor)
                ionmax = factor.index(min(factor))
                ioneq[ionmax, it] = 1.

                for iz in range(ionmax+1, z+1):
                    ionrate = chIons[iz-1].IonizRate['rate'][it]
                    recrate = chIons[iz].RecombRate['rate'][it]
                    if recrate != 0.:
                        ioneq[iz, it] = ionrate*ioneq[iz-1, it]/recrate
                    else:
                        ioneq[iz, it] = 0.

                for iz in range(ionmax-1, -1, -1):
                    ionrate = chIons[iz].IonizRate['rate'][it]
                    recrate = chIons[iz+1].RecombRate['rate'][it]
                    if ionrate != 0.:
                        ioneq[iz, it] = recrate*ioneq[iz+1, it]/ionrate
                    else:
                        ioneq[iz, it] = 0.
                ionsum = ioneq[:, it].sum()
                ioneq[:, it] = ioneq[:, it]/ionsum
            self.Ioneq = ioneq
Beispiel #4
0
    def freeBound(self,
                  wavelength,
                  includeAbundance=True,
                  includeIoneq=True,
                  useVerner=True,
                  **kwargs):
        """
        Calculate the free-bound emission of an ion. The result is returned as a 2D array to the
        `free_bound_emission` attribute.

        The total free-bound continuum emissivity is given by,

        .. math::
           \\frac{dW}{dtdVd\lambda} = \\frac{1}{4\pi}\\frac{2}{hk_Bc^3m_e\sqrt{2\pi k_Bm_e}}\\frac{E^5}{T^{3/2}}\sum_i\\frac{\omega_i}{\omega_0}\sigma_i^{bf}\exp\left(-\\frac{E - I_i}{k_BT}\\right)

        where :math:`E=hc/\lambda` is the photon energy, :math:`\omega_i` and :math:`\omega_0`
        are the statistical weights of the :math:`i^{\mathrm{th}}` level of the recombined ion
        and the ground level of the recombing ion, respectively, :math:`\sigma_i^{bf}` is the
        photoionization cross-section, and :math:`I_i` is the ionization potential of level :math:`i`.
        This expression comes from Eq. 12 of [3]_. For more information about the free-bound continuum
        calculation, see `Peter Young's notes on free-bound continuum`_.

        The photoionization cross-sections are calculated using the methods of [2]_ for the
        transitions to the ground state and [1]_ for all other transitions. See
        `verner_cross_section` and `karzas_cross_section` for more details.

        .. _Peter Young's notes on free-bound continuum: http://www.pyoung.org/chianti/freebound.pdf

        The free-bound emission is in units of erg
        :math:`\mathrm{cm}^3\mathrm{s}^{-1}\mathrm{\mathring{A}}^{-1}\mathrm{str}^{-1}`. If the emission
        measure has been set, the units will be multiplied by :math:`\mathrm{cm}^{-5}` or
        :math:`\mathrm{cm}^{-3}`, depending on whether it is the line-of-sight or volumetric
        emission measure, respectively.

        Parameters
        ----------
        wavelength : array-like
            In units of angstroms
        include_abundance : `bool`, optional
            If True, include the ion abundance in the final output.
        include_ioneq : `bool`, optional
            If True, include the ionization equilibrium in the final output
        use_verner : `bool`, optional
            If True, cross-sections of ground-state transitions using [2]_, i.e. `verner_cross_section`

        Raises
        ------
        ValueError
            If no .fblvl file is available for this ion

        References
        ----------
        .. [1] Karzas and Latter, 1961, ApJSS, `6, 167
            <http://adsabs.harvard.edu/abs/1961ApJS....6..167K>`_
        .. [2] Verner & Yakovlev, 1995, A&AS, `109, 125
            <http://adsabs.harvard.edu/abs/1995A%26AS..109..125V>`_
        .. [3] Young et al., 2003, ApJSS, `144, 135
            <http://adsabs.harvard.edu/abs/2003ApJS..144..135Y>`_
        """
        wavelength = np.atleast_1d(wavelength)
        if wavelength.size < 2:
            print(
                ' wavelength must have at least two values, current length %3i'
                % (wavelength.size))
            return
        self.NWavelength = wavelength.size
        # calculate the photon energy in erg
        photon_energy = ch_const.planck * (1.e8 * ch_const.light) / wavelength
        prefactor = (2. / np.sqrt(2. * np.pi) / (4. * np.pi) /
                     (ch_const.planck * (ch_const.light**3) *
                      (ch_const.emass * ch_const.boltzmann)**(3. / 2.)))
        # read the free-bound level information for the recombined and recombining ion
        recombining_fblvl = ch_io.fblvlRead(self.ion_string)
        # get the multiplicity of the ground state of the recombining ion
        if 'errorMessage' in recombining_fblvl:
            omega_0 = 1.
        else:
            omega_0 = recombining_fblvl['mult'][0]

        self.Recombined_fblvl = ch_io.fblvlRead(self.nameDict['lower'])
        if 'errorMessage' in self.Recombined_fblvl:
            #            raise ValueError('No free-bound information available for {}'.format(ch_util.zion2name(self.Z, self.stage)))
            errorMessage = 'No free-bound information available for {}'.format(
                ch_util.zion2name(self.Z, self.stage))
            fb_emiss = np.zeros((self.NTemperature, self.NWavelength),
                                'float64')
            #            self.free_bound_emission = fb_emiss.squeeze()
            self.FreeBound = {
                'intensity': fb_emiss,
                'temperature': self.Temperature,
                'wvl': wavelength,
                'em': self.Em,
                'errorMessage': errorMessage
            }
            return

        energy_over_temp_factor = np.outer(1. / (self.Temperature**1.5),
                                           photon_energy**5.).squeeze()
        #        if self.NWavelength > 1:
        #            print(' energy shape %5i %5i'%(energy_over_temp_factor.shape[0],energy_over_temp_factor.shape[1]))
        #        else:
        #            print(' energy size %5i'%(energy_over_temp_factor.size))
        # sum over levels of the recombined ion
        sum_factor = np.zeros((len(self.Temperature), len(wavelength)))
        for i, omega_i in enumerate(self.Recombined_fblvl['mult']):
            # ionization potential for level i
            #            ip = self.ionization_potential - recombined_fblvl['ecm'][i]*ch_const.planck*ch_const.light
            ip = self.IprErg - self.Recombined_fblvl['ecm'][
                i] * ch_const.planck * ch_const.light
            # skip level if photon energy is not sufficiently high
            if ip < 0. or np.all(
                    np.max(photon_energy) < (self.ionization_potential - ip)):
                continue
            # calculate cross-section
            if i == 0 and useVerner:
                cross_section = self.verner_cross_section(photon_energy)
            else:
                cross_section = self.karzas_cross_section(
                    photon_energy, ip, self.Recombined_fblvl['pqn'][i],
                    self.Recombined_fblvl['l'][i])
            scaled_energy = np.outer(
                1. / (ch_const.boltzmann * self.Temperature),
                photon_energy - ip)
            # the exponential term can go to infinity for low temperatures
            # but if the cross-section is zero this does not matter
            scaled_energy[:, np.where(cross_section == 0.0)] = 0.0
            sum_factor += omega_i / omega_0 * np.exp(
                -scaled_energy) * cross_section

        # combine factors
        fb_emiss = prefactor * energy_over_temp_factor * sum_factor.squeeze()
        #        if self.NWavelength > 1:
        #            print(' fb emiss.shape %5i %5i'%(fb_emiss.shape[0], fb_emiss.shape[1]))
        #        else:
        #            print(' fb emiss.size %5i'%(fb_emiss.size))
        # include abundance, ionization equilibrium, photon conversion, emission measure
        if includeAbundance:
            fb_emiss *= self.Abundance
            includeAbundance = self.Abundance
        if includeIoneq:
            if self.NTemperature > 1:
                if self.NWavelength > 1:
                    #                    fb_emiss *= self.ioneq_one(self.stage, **kwargs)[:,np.newaxis]
                    fb_emiss *= self.IoneqOne[:, np.newaxis]
                    includeAbundance = self.IoneqOne[:, np.newaxis]
                else:
                    fb_emiss *= self.IoneqOne
                    includeAbundance = self.IoneqOne
            else:
                fb_emiss *= self.IoneqOne
                includeAbundance = self.IoneqOne
        if self.Em is not None:
            if self.Em.size > 1:
                fb_emiss *= self.Em[:, np.newaxis]
            else:
                fb_emiss *= self.Em

        if ch_data.Defaults['flux'] == 'photon':
            fb_emiss /= photon_energy
        # the final units should be per angstrom
        fb_emiss /= 1e8

        #        self.free_bound_emission = fb_emiss.squeeze()
        self.FreeBound = {
            'intensity': fb_emiss.squeeze(),
            'temperature': self.Temperature,
            'wvl': wavelength,
            'em': self.Em,
            'ions': self.ion_string,
            'abundance': includeAbundance,
            'ioneq': includeIoneq
        }
Beispiel #5
0
 def ionGate(self,
             elementList=None,
             ionList=None,
             minAbund=None,
             doLines=1,
             doContinuum=1,
             doWvlTest=1,
             verbose=0):
     '''
     creates a list of ions for free-free, free-bound, and line intensity calculations
     if doing the radiative losses, accept all wavelength -> doWvlTest=0
     the list is a dictionary self.Todo
     '''
     #
     masterlist = chdata.MasterList
     abundAll = self.AbundAll
     #
     nonzed = abundAll > 0.
     minAbundAll = abundAll[nonzed].min()
     if minAbund:
         if minAbund < minAbundAll:
             minAbund = minAbundAll
     ionInfo = chio.masterListInfo()
     #
     if hasattr(self, 'Wavelength'):
         wvlRange = [self.Wavelength.min(), self.Wavelength.max()]
     elif hasattr(self, 'WvlRange'):
         wvlRange = self.WvlRange
     else:
         print(' need a wavelength range in ionGate ')
     #
     temperature = self.Temperature
     #
     # use the ionList but make sure the ions are in the database
     self.Todo = {}
     #
     #
     if elementList:
         for i, element in enumerate(elementList):
             elementList[i] = element.lower()
             if verbose:
                 print('el = %s' % (element))
             z = const.El.index(element.lower()) + 1
             for one in masterlist:
                 nameDict = util.convertName(one)
                 if nameDict['Element'].lower() in elementList:
                     if verbose:
                         print(' ion = %s' % (one))
                     if doLines:
                         self.Todo[one] = 'line'
             for stage in range(2, z + 2):
                 name = util.zion2name(z, stage)
                 if doContinuum and not nameDict['Dielectronic']:
                     if name not in self.Todo.keys():
                         self.Todo[name] = 'ff'
                     else:
                         self.Todo[name] += '_ff'
                     self.Todo[name] += '_fb'
     if ionList:
         for one in ionList:
             nameDict = util.convertName(one)
             if masterlist.count(one):
                 if doLines:
                     self.Todo[one] = 'line'
             else:
                 if verbose:
                     pstring = ' %s not in CHIANTI database' % (one)
                     print(pstring)
             if doContinuum and not nameDict['Dielectronic']:
                 if one not in self.Todo.keys():
                     self.Todo[one] = 'ff'
                 else:
                     self.Todo[one] += '_ff'
                 self.Todo[one] += '_fb'
     #
     #
     #
     if minAbund:
         for iz in range(1, 31):
             abundance = chdata.Abundance[self.AbundanceName]['abundance'][
                 iz - 1]
             if abundance >= minAbund:
                 if verbose:
                     print(' %5i %5s abundance = %10.2e ' %
                           (iz, const.El[iz - 1], abundance))
                 #
                 for ionstage in range(1, iz + 1):
                     ionS = util.zion2name(iz, ionstage)
                     masterListTest = ionS in masterlist
                     masterListInfoTest = ionS in sorted(ionInfo.keys())
                     if masterListTest or masterListInfoTest:
                         if masterListTest or masterListInfoTest:
                             if doWvlTest:
                                 wvlTestMin = wvlRange[0] <= ionInfo[ionS][
                                     'wmax']
                                 wvlTestMax = wvlRange[1] >= ionInfo[ionS][
                                     'wmin']
                             else:
                                 wvlTestMin = 1
                                 wvlTestMax = 1
                         ioneqTest = (
                             temperature.max() >= ionInfo[ionS]['tmin']
                         ) and (temperature.min() <= ionInfo[ionS]['tmax'])
                     # construct similar test for the dielectronic files
                     ionSd = util.zion2name(iz, ionstage, dielectronic=1)
                     masterListTestD = ionSd in masterlist
                     masterListInfoTestD = ionSd in sorted(ionInfo.keys())
                     if masterListTestD or masterListInfoTestD:
                         if doWvlTest:
                             wvlTestMinD = wvlRange[0] <= ionInfo[ionSd][
                                 'wmax']
                             wvlTestMaxD = wvlRange[1] >= ionInfo[ionSd][
                                 'wmin']
                         else:
                             wvlTestMinD = 1
                             wvlTestMaxD = 1
                         ioneqTestD = (
                             temperature.max() >= ionInfo[ionSd]['tmin']
                         ) and (temperature.min() <= ionInfo[ionSd]['tmax'])
                         #
                     if masterListTest and wvlTestMin and wvlTestMax and ioneqTest and doLines:
                         if ionS in sorted(self.Todo.keys()):
                             self.Todo[ionS] += '_line'
                         else:
                             self.Todo[ionS] = 'line'
                     # get dielectronic lines
                         if verbose:
                             print(' for ion %s do : %s' %
                                   (ionS, self.Todo[ionS]))
                     if masterListTestD and wvlTestMinD and wvlTestMaxD and ioneqTestD and doLines:
                         if ionSd in sorted(self.Todo.keys()):
                             self.Todo[ionSd] += '_line'
                         else:
                             self.Todo[ionSd] = 'line'
                         if verbose:
                             print(' for ion %s do : %s' %
                                   (ionSd, self.Todo[ionSd]))
     #
     #
                 for ionstage in range(2, iz + 2):
                     ionS = util.zion2name(iz, ionstage)
                     if ionS in ionInfo.keys():
                         ioneqTest = (
                             temperature.max() >= ionInfo[ionS]['tmin']
                         ) and (temperature.min() <= ionInfo[ionS]['tmax'])
                     else:
                         ioneqTest = 1
                     # construct similar test for the dielectronic files
                     if ioneqTest and doContinuum:
                         # ionS is the target ion, cannot be the neutral for the continuum
                         #                            if verbose:
                         #                                print(' setting up continuum calculation for %s  '%(ionS))
                         if ionS in sorted(self.Todo.keys()):
                             self.Todo[ionS] += '_ff_fb'
                         else:
                             self.Todo[ionS] = 'ff_fb'
                         if verbose:
                             print(' for ion %s do : %s' %
                                   (ionS, self.Todo[ionS]))
     if len(self.Todo.keys()) == 0:
         print(' no elements have been selected')
         print(
             ' it is necessary to provide an elementList, an ionList, or set minAbund > 0.'
         )
     return
Beispiel #6
0
def populateNew(self, popCorrect=1, verbose=0, **kwargs):
    """
    Calculate level populations for specified ion.  This is a new version that will enable the calculation
    of dielectronic satellite lines without resorting to the dielectronic ions, such as c_5d
    possible keyword arguments include temperature, eDensity, pDensity, radTemperature and rStar
    this is a developmental method for using the autoionizing A-values to determine level resolved
    dielectronic recombination rates
    """
    #
    #
    for one in kwargs.keys():
        if one not in chdata.keywordArgs:
            print(' keyword is not understood - %s' % (one))
    #
    nlvls = self.Nlvls
    nwgfa = self.Nwgfa
    nscups = self.Nscups
    npsplups = self.Npsplups
    #
    if kwargs.has_key('temperature'):
        self.Temperature = np.asarray(kwargs['temperature'])
        temperature = self.Temperature
    elif hasattr(self, 'Temperature'):
        temperature = self.Temperature
    else:
        print(' no temperature values have been set')
        return {'errorMessage': ' no temperature values have been set'}
    #
    if kwargs.has_key('eDensity'):
        self.EDensity = np.asarray(kwargs['eDensity'])
        eDensity = self.EDensity
    elif hasattr(self, 'EDensity'):
        eDensity = self.EDensity
    else:
        print(' no eDensity values have been set')
        return {'errorMessage': ' no eDensity values have been set'}
    #
    if kwargs.has_key('pDensity'):
        if kwargs['pDensity'] == 'default':
            self.p2eRatio()
            protonDensity = self.ProtonDensityRatio * self.EDensity
        else:
            try:
                self.PDensity = np.asarray(kwargs['pDensity'])
            except:
                print(' could not interpret value for keyword pDensity')
                print(' should be either "default" or a number or array')
                return
    else:
        if hasattr(self, 'PDensity'):
            protonDensity = self.PDensity
        else:
            self.p2eRatio()
            self.PDensity = self.ProtonDensityRatio * self.EDensity
            protonDensity = self.PDensity
            print(' proton density not specified, set to "default"')
    #
    if 'radTemperature' in kwargs.keys() and 'rStar' in kwargs.keys():
        self.RadTemperature = np.asarray(kwargs['radTemperature'])
        radTemperature = np.array(self.RadTemperature)
        self.RStar = np.asarray(kwargs['rStar'])
        rStar = np.asarray(self.RStar)
    elif hasattr(self, 'RadTemperature') and hasattr(self, 'RStar'):
        radTemperature = self.RadTemperature
        rStar = self.RStar
    #
    #
    #
    if self.Ncilvl:
        ci = 1
        cilvl = self.Cilvl
        #            if hasattr(self, 'CilvlRate'):
        #                cilvlRate = self.CilvlRate
        #            else:
        #                self.cireclvlDescale('cilvl')
        #                cilvlRate = self.CilvlRate
        self.recombRate()
        #
        lowers = util.zion2name(self.Z, self.Ion - 1)
        # get the lower ionization stage
        self.Lower = ion(lowers,
                         temperature=self.Temperature,
                         eDensity=self.EDensity)
        self.Lower.ionizRate()
        # need to get multiplicity of lower ionization stage
        lowMult = self.Lower.Elvlc['mult']
    else:
        ci = 0
    #  evetually will be looking for just an recLvl attribute
    #
    #  if the higher ion does not exist in the database, rec=0
    highers = util.zion2name(self.Z, self.Ion + 1)
    if self.Nreclvl:
        reclvl = self.Reclvl
        if hasattr(self, 'ReclvlRate'):
            reclvlRate = self.ReclvlRate
        else:
            self.cireclvlDescale('reclvl')
            reclvlRate = self.ReclvlRate
    if hasattr(self, 'Auto'):
        self.drRateLvl()
    if self.Nreclvl or hasattr(self, 'Auto'):
        rec = 1
        # get ionization rate of this ion
        self.ionizRate()
        #  get the higher ionization stage
    else:
        rec = 0
    #  if the higher ion does not exist in the database, rec=0
    highers = util.zion2name(self.Z, self.Ion + 1)
    #        if highers in chdata.MasterList:
    self.Higher = ion(highers,
                      temperature=self.Temperature,
                      eDensity=self.EDensity)
    self.Higher.recombRate()
    #        else:
    #        #
    rad = np.zeros(
        (nlvls + ci + rec, nlvls + ci + rec),
        "float64")  #  the populating matrix for radiative transitions
    #
    #
    for iwgfa in range(nwgfa):
        l1 = self.Wgfa["lvl1"][iwgfa] - 1
        l2 = self.Wgfa["lvl2"][iwgfa] - 1
        rad[l1 + ci, l2 + ci] += self.Wgfa["avalue"][iwgfa]
        rad[l2 + ci, l2 + ci] -= self.Wgfa["avalue"][iwgfa]
        # photo-excitation and stimulated emission
        if self.RadTemperature:
            if not self.RStar:
                dilute = 0.5
            else:
                dilute = util.dilute(self.RStar)
            # next - don't include autoionization lines
            if abs(self.Wgfa['wvl'][iwgfa]) > 0.:
                if self.Elvlc['ecm'][l2] >= 0.:
                    ecm2 = self.Elvlc['ecm'][l2]
                else:
                    ecm2 = self.Elvlc['ecmth'][l2]
                #
                if self.Elvlc['ecm'][l1] >= 0.:
                    ecm1 = self.Elvlc['ecm'][l1]
                else:
                    ecm1 = self.Elvlc['ecmth'][l1]
                de = const.invCm2Erg * (ecm2 - ecm1)
                dekt = de / (const.boltzmann * self.RadTemperature)
                # photoexcitation
                phexFactor = dilute * (float(self.Elvlc['mult'][l2]) / float(
                    self.Elvlc['mult'][l1])) / (np.exp(dekt) - 1.)
                rad[l2 + ci,
                    l1 + ci] += self.Wgfa["avalue"][iwgfa] * phexFactor
                rad[l1 + ci,
                    l1 + ci] -= self.Wgfa["avalue"][iwgfa] * phexFactor
                # stimulated emission
                stemFactor = dilute / (np.exp(-dekt) - 1.)
                rad[l1 + ci,
                    l2 + ci] += self.Wgfa["avalue"][iwgfa] * stemFactor
                rad[l2 + ci,
                    l2 + ci] -= self.Wgfa["avalue"][iwgfa] * stemFactor
    if hasattr(self, 'Auto'):
        # as of 7/2012, we only consider the ground level in the next higher ionization stage
        # hence, requiring lvl1 = 1 or, l1 = 0
        for iauto, avalue in enumerate(self.Auto['avalue']):
            l1 = self.Auto["lvl1"][iauto] - 1
            l2 = self.Auto["lvl2"][iauto] - 1
            # for now only consider a single level for upper/higher ion
            if l1 == 0 and rec:
                rad[l1 + ci + nlvls, l2 + ci] += avalue
                rad[l2 + ci, l2 + ci] -= avalue
            # if the higher ion is not in the database, decay to the ground level
            elif l1 == 0:
                rad[l1 + ci, l2 + ci] += avalue
                rad[l2 + ci, l2 + ci] -= avalue

    #
    #
    if self.Nscups:
        #            print(' Nscups = %10i'%(self.Nscups))
        self.upsilonDescale()
        ups = self.Upsilon['upsilon']
        exRate = self.Upsilon['exRate']
        dexRate = self.Upsilon['dexRate']
    #
    if self.Npsplups:
        self.upsilonDescaleSplups(prot=1)
        #            pups = self.PUpsilon['upsilon']
        pexRate = self.PUpsilon['exRate']
        pdexRate = self.PUpsilon['dexRate']
    #
    temp = temperature
    ntemp = temp.size
    #
    cc = const.collision * self.EDensity
    ndens = cc.size
    if self.Npsplups:
        cp = const.collision * protonDensity
    if ntemp > 1 and ndens > 1 and ntemp != ndens:
        print(' unless temperature or eDensity are single values')
        print(' the number of temperatures values must match the ')
        print(' the number of eDensity values')
        return
    #
    # get corrections for recombination and excitation
    #
    nscups = self.Nscups
    #
    #  first, for ntemp=ndens=1
    if ndens == 1 and ntemp == 1:
        popmat = np.copy(rad)
        for iscups in range(0, nscups):
            l1 = self.Scups["lvl1"][iscups] - 1
            l2 = self.Scups["lvl2"][iscups] - 1
            #
            popmat[l1 + ci, l2 + ci] += self.EDensity * dexRate[iscups]
            popmat[l2 + ci, l1 + ci] += self.EDensity * exRate[iscups]
            popmat[l1 + ci, l1 + ci] -= self.EDensity * exRate[iscups]
            popmat[l2 + ci, l2 + ci] -= self.EDensity * dexRate[iscups]
            #
        for isplups in range(0, npsplups):
            l1 = self.Psplups["lvl1"][isplups] - 1
            l2 = self.Psplups["lvl2"][isplups] - 1
            #
            popmat[l1 + ci, l2 + ci] += self.PDensity * pdexRate[isplups]
            popmat[l2 + ci, l1 + ci] += self.PDensity * pexRate[isplups]
            popmat[l1 + ci, l1 + ci] -= self.PDensity * pexRate[isplups]
            popmat[l2 + ci, l2 + ci] -= self.PDensity * pdexRate[isplups]
    # now include ionization rate from
        if ci:
            #
            # the ciRate can be computed for all temperatures
            #
            ciTot = 0.
            for itrans in range(len(cilvl['lvl1'])):
                lvl1 = cilvl['lvl1'][itrans] - 1
                lvl2 = cilvl['lvl2'][itrans] - 1
                # this is kind of double booking the ionization rate components
                popmat[lvl2 + ci,
                       lvl1] += self.EDensity * self.CilvlRate['rate'][itrans]
                popmat[lvl1,
                       lvl1] -= self.EDensity * self.CilvlRate['rate'][itrans]
                ciTot += self.EDensity * self.CilvlRate['rate'][itrans]
            #
            popmat[1,
                   0] += (self.EDensity * self.Lower.IonizRate['rate'] - ciTot)
            popmat[0,
                   0] -= (self.EDensity * self.Lower.IonizRate['rate'] - ciTot)
            popmat[0, 1] += self.EDensity * self.RecombRate['rate']
            popmat[1, 1] -= self.EDensity * self.RecombRate['rate']
        if rec:
            # ntemp=ndens=1
            #
            if hasattr(self, 'DrRateLvl'):
                #                    branch = np.zeros(self.Ndielsplups, 'float64')
                for idr, rate in enumerate(self.DrRateLvl['rate']):
                    l1 = self.Auto["lvl1"][idr] - 1
                    l2 = self.Auto["lvl2"][idr] - 1
                    #                        popmat[l2+ci,-1] += self.EDensity*self.DrRateLvl['rate'][idr]
                    #                        popmat[-1, -1] -= self.EDensity*self.DrRateLvl['rate'][idr]
                    popmat[l2 + ci, -1] += self.EDensity * rate
                    popmat[-1, -1] -= self.EDensity * rate
                    #
                dielTot = self.DrRateLvl['totalRate'][0]
            else:
                dielTot = 0.
            #
            #
            #
            for itrans in range(self.Nreclvl):
                #                    lvl1 = reclvl['lvl1'][itrans]-1
                lvl2 = reclvl['lvl2'][itrans] - 1
                popmat[lvl2 + ci,
                       -1] += self.EDensity * reclvlRate['rate'][itrans]
                popmat[-1, -1] -= self.EDensity * reclvlRate['rate'][itrans]
            if self.Nreclvl:
                recTot = reclvlRate['rate'].sum(axis=0)
            else:
                recTot = 0.
            #
            #
            popmat[-1, ci] += self.EDensity * self.IonizRate['rate']
            popmat[ci, ci] -= self.EDensity * self.IonizRate['rate']
            #
            # next 2 line take care of overbooking
            netRecomb = self.EDensity * (self.Higher.RecombRate['rate'][0] -
                                         recTot - dielTot)
            #
            if netRecomb > 0.:
                popmat[ci, -1] += netRecomb
                popmat[-1, -1] -= netRecomb
        #
        # normalize to unity
        norm = np.ones(nlvls + ci + rec, 'float64')
        if ci:
            norm[0] = 0.
        if rec:
            norm[nlvls + ci + rec - 1] = 0.
        popmata = np.copy(popmat)
        normRow = (nlvls + ci + rec - 1) / 2
        #popmata[nlvls+ci+rec-1]=norm
        popmata[normRow] = norm
        b = np.zeros(nlvls + ci + rec, 'float64')
        #b[nlvls+ci+rec-1]=1.
        b[normRow] = 1.
        try:
            fullpop = np.linalg.solve(popmata, b)
            pop = fullpop[ci:ci + nlvls]
            if rec:
                popHigher = fullpop[-1]
            else:
                popHigher = 0.
        except np.linalg.LinAlgError:
            pop = np.zeros(nlvls, 'float64')
            popHigher = 0.
#                print ' error in matrix inversion, setting populations to zero at T = ', ('%8.2e')%(temperature)
#
# ------------- ntemp = 1 ---------------------------------------------------------
#
#
    elif ndens == 1:
        pop = np.zeros((ntemp, nlvls), "float64")
        popHigher = np.zeros(ntemp, 'float64')
        #            pop=np.zeros((ntemp,ci + nlvls + rec),"float64")
        for itemp in range(ntemp):
            popmat = np.copy(rad)
            for iscups in range(0, nscups):
                l1 = self.Scups["lvl1"][iscups] - 1
                l2 = self.Scups["lvl2"][iscups] - 1
                popmat[l1 + ci,
                       l2 + ci] += self.EDensity * dexRate[iscups, itemp]
                popmat[l2 + ci,
                       l1 + ci] += self.EDensity * exRate[iscups, itemp]
                popmat[l1 + ci,
                       l1 + ci] -= self.EDensity * exRate[iscups, itemp]
                popmat[l2 + ci,
                       l2 + ci] -= self.EDensity * dexRate[iscups, itemp]
            for isplups in range(0, npsplups):
                l1 = self.Psplups["lvl1"][isplups] - 1
                l2 = self.Psplups["lvl2"][isplups] - 1
                # for proton excitation, the levels are all below the ionization potential
                #
                popmat[l1 + ci, l2 +
                       ci] += self.PDensity[itemp] * pdexRate[isplups, itemp]
                popmat[l2 + ci, l1 +
                       ci] += self.PDensity[itemp] * pexRate[isplups, itemp]
                popmat[l1 + ci, l1 +
                       ci] -= self.PDensity[itemp] * pexRate[isplups, itemp]
                popmat[l2 + ci, l2 +
                       ci] -= self.PDensity[itemp] * pdexRate[isplups, itemp]
            # now include ionization rate from
            if ci:
                #
                # the ciRate can be computed for all temperatures
                #
                ciTot = 0.
                for itrans in range(len(cilvl['lvl1'])):
                    lvl1 = cilvl['lvl1'][itrans] - 1
                    lvl2 = cilvl['lvl2'][itrans] - 1
                    #mult = lowMult[lvl1-1]
                    popmat[lvl2 + ci,
                           lvl1] += self.EDensity * self.CilvlRate['rate'][
                               itrans, itemp]
                    popmat[lvl1,
                           lvl1] -= self.EDensity * self.CilvlRate['rate'][
                               itrans, itemp]
                    ciTot += self.EDensity * self.CilvlRate['rate'][itrans,
                                                                    itemp]
                #
                popmat[1, 0] += (
                    self.EDensity * self.Lower.IonizRate['rate'][itemp] -
                    ciTot)
                popmat[0, 0] -= (
                    self.EDensity * self.Lower.IonizRate['rate'][itemp] -
                    ciTot)
                popmat[0, 1] += self.EDensity * self.RecombRate['rate'][itemp]
                popmat[1, 1] -= self.EDensity * self.RecombRate['rate'][itemp]
            if rec:
                #
                # ndens=1
                if hasattr(self, 'DrRateLvl'):
                    for idr, rate in enumerate(self.DrRateLvl['rate']):
                        l1 = self.Auto["lvl1"][idr] - 1
                        l2 = self.Auto["lvl2"][idr] - 1
                        popmat[l2 + ci, -1] += self.EDensity * rate[itemp]
                        popmat[-1, -1] -= self.EDensity * rate[itemp]
                        #
                    # DrRateLvl['totalRate'] includes the branching ratio
                    dielTot = self.DrRateLvl['totalRate'][itemp]
                else:
                    dielTot = 0.
                #
                #
                for itrans in range(self.Nreclvl):
                    lvl1 = reclvl['lvl1'][itrans] - 1
                    lvl2 = reclvl['lvl2'][itrans] - 1
                    popmat[lvl2 + ci,
                           -1] += self.EDensity * self.ReclvlRate['rate'][
                               itrans, itemp]
                    popmat[-1, -1] -= self.EDensity * self.ReclvlRate['rate'][
                        itrans, itemp]
                #
                if self.Nreclvl:
                    recTot = self.ReclvlRate['rate'][:, itemp].sum()
                else:
                    recTot = 0.
                #
                #
                popmat[-1, ci] += self.EDensity * self.IonizRate['rate'][itemp]
                popmat[ci, ci] -= self.EDensity * self.IonizRate['rate'][itemp]
                #
                netRecomb = self.EDensity * (
                    self.Higher.RecombRate['rate'][itemp] - recTot - dielTot)
                #
                if netRecomb > 0.:
                    popmat[ci, -1] += netRecomb
                    popmat[-1, -1] -= netRecomb
                #
            # normalize to unity
            # ndens = 1
            norm = np.ones(nlvls + ci + rec, 'float64')
            if ci:
                norm[0] = 0.
            if rec:
                norm[-1] = 0.
            popmata = np.copy(popmat)
            normRow = (nlvls + ci + rec - 1) / 2
            #popmata[nlvls+ci+rec-1]=norm
            popmata[normRow] = norm
            b = np.zeros(nlvls + ci + rec, 'float64')
            #b[nlvls+ci+rec-1]=1.
            b[normRow] = 1.
            #popmata[nlvls+ci+rec-1]=norm
            #b=np.zeros(nlvls+ci+rec,'float64')
            #b[nlvls+ci+rec-1] = 1.
            #                b[-1] = 1.
            try:
                thispop = np.linalg.solve(popmata, b)
                pop[itemp] = thispop[ci:ci + nlvls]
                popHigher[itemp] = thispop[-1]
            except np.linalg.LinAlgError:
                pop[itemp] = np.zeros(nlvls, 'float64')
                popHigher[itemp] = 0.
#                    print ' error in matrix inversion, setting populations to zero at T = ', ('%8.2e')%(temperature[itemp])
#
    elif ntemp == 1:
        pop = np.zeros((ndens, nlvls), "float64")
        popHigher = np.zeros(ndens, 'float64')
        for idens in range(0, ndens):
            popmat = np.copy(rad)
            for iscups in range(0, nscups):
                l1 = self.Scups["lvl1"][iscups] - 1
                l2 = self.Scups["lvl2"][iscups] - 1
                #
                popmat[l1 + ci,
                       l2 + ci] += self.EDensity[idens] * dexRate[iscups]
                popmat[l2 + ci,
                       l1 + ci] += self.EDensity[idens] * exRate[iscups]
                popmat[l1 + ci,
                       l1 + ci] -= self.EDensity[idens] * exRate[iscups]
                popmat[l2 + ci,
                       l2 + ci] -= self.EDensity[idens] * dexRate[iscups]
            #
            for isplups in range(0, npsplups):
                l1 = self.Psplups["lvl1"][isplups] - 1
                l2 = self.Psplups["lvl2"][isplups] - 1
                #
                popmat[l1 + ci,
                       l2 + ci] += self.PDensity[idens] * pdexRate[isplups]
                popmat[l2 + ci,
                       l1 + ci] += self.PDensity[idens] * pexRate[isplups]
                popmat[l1 + ci,
                       l1 + ci] -= self.PDensity[idens] * pexRate[isplups]
                popmat[l2 + ci,
                       l2 + ci] -= self.PDensity[idens] * pdexRate[isplups]
            # now include ionization rate from
            if ci:
                #
                #
                ciTot = 0.
                for itrans in range(len(cilvl['lvl1'])):
                    lvl1 = cilvl['lvl1'][itrans] - 1
                    lvl2 = cilvl['lvl2'][itrans] - 1
                    popmat[lvl2 + ci, lvl1] += self.EDensity[
                        idens] * self.CilvlRate['rate'][itrans]
                    popmat[lvl1, lvl1] -= self.EDensity[
                        idens] * self.CilvlRate['rate'][itrans]
                    ciTot += self.EDensity[idens] * self.CilvlRate['rate'][
                        itrans]
                popmat[1, 0] += (
                    self.EDensity[idens] * self.Lower.IonizRate['rate'] -
                    ciTot)
                popmat[0, 0] -= (
                    self.EDensity[idens] * self.Lower.IonizRate['rate'] -
                    ciTot)
                popmat[0, 1] += self.EDensity[idens] * self.RecombRate['rate']
                popmat[1, 1] -= self.EDensity[idens] * self.RecombRate['rate']
            if rec:
                #ntemp = 1
                if hasattr(self, 'DrRateLvl'):
                    for idr, rate in enumerate(self.DrRateLvl['rate']):
                        l1 = self.Auto["lvl1"][idr] - 1
                        l2 = self.Auto["lvl2"][idr] - 1
                        #                            popmat[l2+ci,l1+ci+nlvls] += self.EDensity[idens]*self.DrRateLvl['rate'][idr]
                        #                            popmat[l1+ci,l1+ci] -= self.EDensity[idens]*self.DrRateLvl['rate'][idr]
                        popmat[l2 + ci, -1] += self.EDensity[idens] * rate
                        popmat[-1, -1] -= self.EDensity[idens] * rate
                        #
                    dielTot = self.DrRateLvl['totalRate'][0]
                else:
                    dielTot = 0.
                if self.Nreclvl:
                    recTot = self.ReclvlRate['rate'].sum()
                else:
                    recTot = 0.
                #
                popmat[-1, ci] += self.EDensity[idens] * self.IonizRate['rate']
                popmat[ci, ci] -= self.EDensity[idens] * self.IonizRate['rate']
                #
                netRecomb = self.EDensity[idens] * (
                    self.Higher.RecombRate['rate'] - recTot - dielTot)
                if netRecomb > 0.:
                    popmat[ci, -1] += netRecomb
                    popmat[-1, -1] -= netRecomb
                #
#                    for itrans in range(len(rrlvl['lvl1'])):
                for itrans in range(self.Nreclvl):
                    lvl1 = reclvl['lvl1'][itrans] - 1
                    lvl2 = reclvl['lvl2'][itrans] - 1
                    popmat[lvl2 + ci, -1] += self.EDensity[
                        idens] * self.ReclvlRate['rate'][itrans]
                    popmat[-1, -1] -= self.EDensity[idens] * self.ReclvlRate[
                        'rate'][itrans]
            #
            # normalize to unity
            norm = np.ones(nlvls + ci + rec, 'float64')
            if ci:
                norm[0] = 0.
            if rec:
                norm[-1] = 0.
            popmata = np.copy(popmat)
            popmata[nlvls + ci + rec - 1] = norm
            #                popmat[nlvls+ci+rec-1] = norm
            b = np.zeros(nlvls + ci + rec, 'float64')
            b[nlvls + ci + rec - 1] = 1.
            try:
                thispop = np.linalg.solve(popmata, b)
                pop[idens] = thispop[ci:ci + nlvls]
                popHigher[idens] = thispop[-1]
            except np.linalg.LinAlgError:
                pop[idens] = np.zeros(nlvls, 'float64')
                popHigher[idens] = 0.
#                    print ' error in matrix inversion, setting populations to zero at eDensity = ', ('%8.2e')%(eDensity[idens])
#
    elif ntemp > 1 and ntemp == ndens:
        pop = np.zeros((ntemp, nlvls), "float64")
        popHigher = np.zeros(ntemp, 'float64')
        for itemp in range(0, ntemp):
            temp = self.Temperature[itemp]
            popmat = np.copy(rad)
            for iscups in range(0, nscups):
                l1 = self.Scups["lvl1"][iscups] - 1
                l2 = self.Scups["lvl2"][iscups] - 1
                #
                popmat[l1 + ci, l2 +
                       ci] += self.EDensity[itemp] * dexRate[iscups, itemp]
                popmat[l2 + ci,
                       l1 + ci] += self.EDensity[itemp] * exRate[iscups, itemp]
                popmat[l1 + ci,
                       l1 + ci] -= self.EDensity[itemp] * exRate[iscups, itemp]
                popmat[l2 + ci, l2 +
                       ci] -= self.EDensity[itemp] * dexRate[iscups, itemp]
            # proton rates
            for isplups in range(0, npsplups):
                l1 = self.Psplups["lvl1"][isplups] - 1
                l2 = self.Psplups["lvl2"][isplups] - 1
                # for proton excitation, the levels are all below the ionization potential
                #
                popmat[l1 + ci, l2 +
                       ci] += self.PDensity[itemp] * pdexRate[isplups, itemp]
                popmat[l2 + ci, l1 +
                       ci] += self.PDensity[itemp] * pexRate[isplups, itemp]
                popmat[l1 + ci, l1 +
                       ci] -= self.PDensity[itemp] * pexRate[isplups, itemp]
                popmat[l2 + ci, l2 +
                       ci] -= self.PDensity[itemp] * pdexRate[isplups, itemp]
            # now include ionization rate from
            if ci:
                #
                # the ciRate can be computed for all temperatures
                #
                ciTot = 0.
                for itrans in range(len(cilvl['lvl1'])):
                    lvl1 = cilvl['lvl1'][itrans] - 1
                    lvl2 = cilvl['lvl2'][itrans] - 1
                    popmat[lvl2 + ci, lvl1] += self.EDensity[
                        itemp] * self.CilvlRate['rate'][itrans, itemp]
                    popmat[lvl1, lvl1] -= self.EDensity[
                        itemp] * self.CilvlRate['rate'][itrans, itemp]
                    ciTot += self.EDensity[itemp] * self.CilvlRAte['rate'][
                        itrans, itemp]
                popmat[1, 0] += (self.EDensity[itemp] *
                                 self.Lower.IonizRate['rate'][itemp] - ciTot)
                popmat[0, 0] -= (self.EDensity[itemp] *
                                 self.Lower.IonizRate['rate'][itemp] - ciTot)
                popmat[
                    0,
                    1] += self.EDensity[itemp] * self.RecombRate['rate'][itemp]
                popmat[
                    1,
                    1] -= self.EDensity[itemp] * self.RecombRate['rate'][itemp]
            if rec:
                #
                if hasattr(self, 'DrRateLvl'):
                    #                    branch = np.zeros(self.Ndielsplups, 'float64')
                    for idr, rate in enumerate(self.DrRateLvl['rate']):
                        l1 = self.Auto["lvl1"][idr] - 1
                        l2 = self.Auto["lvl2"][idr] - 1
                        popmat[l2 + ci, l1 + ci +
                               nlvls] += self.EDensity[itemp] * rate[itemp]
                        popmat[-1, -1] -= self.EDensity[itemp] * rate[itemp]
                        #
                    dielTot = self.DrRateLvl['totalRate'][itemp]
                else:
                    dielTot = 0.
            #
                if self.Nreclvl:
                    recTot = self.RrlvlRate['rate'][:, itemp].sum()
                else:
                    recTot = 0.
            #
            #
                popmat[
                    -1,
                    ci] += self.EDensity[itemp] * self.IonizRate['rate'][itemp]
                popmat[
                    ci,
                    ci] -= self.EDensity[itemp] * self.IonizRate['rate'][itemp]
                #
                netRecomb = self.EDensity[itemp] * (
                    self.Higher.RecombRate['rate'][itemp] - recTot - dielTot)
                if netRecomb > 0.:
                    popmat[ci, -1] += netRecomb
                    popmat[-1, -1] -= netRecomb
                #
                for itrans in range(self.Nreclvl):
                    lvl1 = reclvl['lvl1'][itrans] - 1
                    lvl2 = reclvl['lvl2'][itrans] - 1
                    popmat[lvl2 + ci, -1] += self.EDensity[
                        itemp] * self.RrlvlRate['rate'][itrans, itemp]
                    popmat[-1, -1] -= self.EDensity[itemp] * self.RrlvlRate[
                        'rate'][itrans, itemp]
            #
            # normalize to unity
            norm = np.ones(nlvls + ci + rec, 'float64')
            if ci:
                norm[0] = 0.
            if rec:
                norm[-1] = 0.
            popmata = np.copy(popmat)
            popmata[nlvls + ci + rec - 1] = norm
            #                popmat[nlvls+ci+rec-1] = norm
            b = np.zeros(nlvls + ci + rec, 'float64')
            b[nlvls + ci + rec - 1] = 1.
            try:
                thispop = np.linalg.solve(popmata, b)
                pop[itemp] = thispop[ci:ci + nlvls]
                popHigher[itemp] = thispop[-1]
            except np.linalg.LinAlgError:
                pop[itemp] = np.zeros(nlvls, 'float64')
                popHigher[itemp] = 0.
#                    print ' error in matrix inversion, setting populations to zero at T = ', ('%8.2e')%(temperature[itemp])
#
    pop = np.where(pop > 0., pop, 0.)
    self.Population = {
        "temperature": temperature,
        "eDensity": eDensity,
        "population": pop,
        "protonDensity": protonDensity,
        "ci": ci,
        "rec": rec,
        'popmat': popmata,
        'b': b,
        'rad': rad
    }
    if rec:
        self.Population['popHigher'] = popHigher
        self.Population['higher'] = self.Higher
        self.Population['recTot'] = recTot
        self.Population['dielTot'] = dielTot
        self.Population['netRecomb'] = netRecomb
    #
    return
Beispiel #7
0
    def ionGate(self, elementList = None, ionList = None, minAbund=None, doLines=1, doContinuum=1, doWvlTest=1, verbose=0):
        '''
        creates a list of ions for free-free, free-bound, and line intensity calculations
        if doing the radiative losses, accept all wavelength -> doWvlTest=0
        the list is a dictionary self.Todo
        '''
        #
        masterlist = chdata.MasterList
        abundAll = self.AbundAll
        #
        nonzed = abundAll > 0.
        minAbundAll = abundAll[nonzed].min()
        if minAbund:
            if minAbund < minAbundAll:
                minAbund = minAbundAll
        ionInfo = chio.masterListInfo()
        #
        if hasattr(self, 'Wavelength'):
            wvlRange = [self.Wavelength.min(), self.Wavelength.max()]
        elif hasattr(self, 'WvlRange'):
            wvlRange = self.WvlRange
        else:
            print(' need a wavelength range in ionGate ')
        #
        temperature = self.Temperature
        #
        # use the ionList but make sure the ions are in the database
        self.Todo = {}
        #
        #
        if elementList:
            for i,  element in enumerate(elementList):
                elementList[i] = element.lower()
                if verbose:
                    print('el = %s'%(element))
                z = const.El.index(element.lower()) + 1 
                for one in masterlist:
                    nameDict = util.convertName(one)
                    if nameDict['Element'].lower() in elementList:
                        if verbose:
                            print(' ion = %s'%(one))
                        if doLines:
                            self.Todo[one] = 'line'
                for stage in range(2, z+2):
                    name = util.zion2name(z, stage)            
                    if doContinuum and not nameDict['Dielectronic']:
                        if name not in self.Todo.keys():
                            self.Todo[name] = 'ff'
                        else:
                            self.Todo[name] += '_ff'
                        self.Todo[name] += '_fb'
        if ionList:
            for one in ionList:
                nameDict = util.convertName(one)
                if masterlist.count(one):
                    if doLines:
                        self.Todo[one] = 'line'
                else:
                    if verbose:
                        pstring = ' %s not in CHIANTI database'%(one)
                        print(pstring)
                if doContinuum and not nameDict['Dielectronic']:
                    if one not in self.Todo.keys():
                        self.Todo[one] = 'ff'
                    else:
                        self.Todo[one] += '_ff'
                    self.Todo[one] += '_fb'
        #
        #
        #
        if minAbund:
            for iz in range(1, 31):
                abundance = chdata.Abundance[self.AbundanceName]['abundance'][iz-1]
                if abundance >= minAbund:
                    if verbose:
                        print(' %5i %5s abundance = %10.2e '%(iz, const.El[iz-1],  abundance))
                    #
                    for ionstage in range(1, iz+1):
                        ionS = util.zion2name(iz, ionstage)
                        masterListTest = ionS in masterlist
                        masterListInfoTest = ionS in sorted(ionInfo.keys())
                        if masterListTest or masterListInfoTest:
                            if masterListTest or masterListInfoTest:
                                if doWvlTest:
                                    wvlTestMin = wvlRange[0] <= ionInfo[ionS]['wmax']
                                    wvlTestMax = wvlRange[1] >= ionInfo[ionS]['wmin']
                                else:
                                    wvlTestMin = 1
                                    wvlTestMax = 1
                            ioneqTest = (temperature.max() >= ionInfo[ionS]['tmin']) and (temperature.min() <= ionInfo[ionS]['tmax'])
                        # construct similar test for the dielectronic files
                        ionSd = util.zion2name(iz, ionstage, dielectronic=1)
                        masterListTestD = ionSd in masterlist
                        masterListInfoTestD = ionSd in sorted(ionInfo.keys())
                        if masterListTestD or masterListInfoTestD:
                            if doWvlTest:
                                wvlTestMinD = wvlRange[0] <= ionInfo[ionSd]['wmax']
                                wvlTestMaxD = wvlRange[1] >= ionInfo[ionSd]['wmin']
                            else:
                                wvlTestMinD = 1
                                wvlTestMaxD = 1
                            ioneqTestD = (temperature.max() >= ionInfo[ionSd]['tmin']) and (temperature.min() <=ionInfo[ionSd]['tmax'])
                            #
                        if masterListTest and wvlTestMin and wvlTestMax and ioneqTest and doLines:
                            if ionS in sorted(self.Todo.keys()):
                                self.Todo[ionS] += '_line'
                            else:
                                self.Todo[ionS] = 'line'
                        # get dielectronic lines
                            if verbose:
                                print(' for ion %s do : %s'%(ionS, self.Todo[ionS]))
                        if masterListTestD and wvlTestMinD and wvlTestMaxD and ioneqTestD and doLines:
                            if ionSd in sorted(self.Todo.keys()):
                                self.Todo[ionSd] += '_line'
                            else:
                                self.Todo[ionSd] = 'line'
                            if verbose:
                                print(' for ion %s do : %s'%(ionSd, self.Todo[ionSd]))
        #
                    #
                    for ionstage in range(2, iz+2):
                        ionS = util.zion2name(iz, ionstage)
                        if ionS in ionInfo.keys():
                            ioneqTest = (temperature.max() >= ionInfo[ionS]['tmin']) and (temperature.min() <= ionInfo[ionS]['tmax'])
                        else:
                            ioneqTest = 1
                        # construct similar test for the dielectronic files
                        if ioneqTest and doContinuum:
                            # ionS is the target ion, cannot be the neutral for the continuum
#                            if verbose:
#                                print(' setting up continuum calculation for %s  '%(ionS))
                            if ionS in sorted(self.Todo.keys()):
                                self.Todo[ionS] += '_ff_fb'
                            else:
                                self.Todo[ionS] = 'ff_fb'
                            if verbose:
                                print(' for ion %s do : %s'%(ionS, self.Todo[ionS]))
        if len(self.Todo.keys()) == 0:
            print(' no elements have been selected')
            print(' it is necessary to provide an elementList, an ionList, or set minAbund > 0.')
        return
Beispiel #8
0
def populateNew(self, popCorrect=1, verbose=0, **kwargs):
    """
    Calculate level populations for specified ion.  This is a new version that will enable the calculation
    of dielectronic satellite lines without resorting to the dielectronic ions, such as c_5d
    possible keyword arguments include temperature, eDensity, pDensity, radTemperature and rStar
    this is a developmental method for using the autoionizing A-values to determine level resolved
    dielectronic recombination rates
    """
    #
    #
    for one in kwargs.keys():
        if one not in chdata.keywordArgs:
            print(' keyword is not understood - %s'%(one))
    #
    nlvls = self.Nlvls
    nwgfa = self.Nwgfa
    nscups = self.Nscups
    npsplups = self.Npsplups
    #
    if kwargs.has_key('temperature'):
        self.Temperature = np.asarray(kwargs['temperature'])
        temperature = self.Temperature
    elif hasattr(self, 'Temperature'):
        temperature=self.Temperature
    else:
        print(' no temperature values have been set')
        return {'errorMessage':' no temperature values have been set'}
    #
    if kwargs.has_key('eDensity'):
        self.EDensity = np.asarray(kwargs['eDensity'])
        eDensity = self.EDensity
    elif hasattr(self, 'EDensity'):
        eDensity = self.EDensity
    else:
        print(' no eDensity values have been set')
        return {'errorMessage':' no eDensity values have been set'}
    #
    if kwargs.has_key('pDensity'):
        if kwargs['pDensity'] == 'default':
            self.p2eRatio()
            protonDensity = self.ProtonDensityRatio*self.EDensity
        else:
            try:
                self.PDensity = np.asarray(kwargs['pDensity'])
            except:
                print(' could not interpret value for keyword pDensity')
                print(' should be either "default" or a number or array')
                return
    else:
        if hasattr(self, 'PDensity'):
            protonDensity = self.PDensity
        else:
            self.p2eRatio()
            self.PDensity = self.ProtonDensityRatio*self.EDensity
            protonDensity = self.PDensity
            print(' proton density not specified, set to "default"')
    #
    if 'radTemperature' in kwargs.keys() and 'rStar' in kwargs.keys():
        self.RadTemperature = np.asarray(kwargs['radTemperature'])
        radTemperature = np.array(self.RadTemperature)
        self.RStar = np.asarray(kwargs['rStar'])
        rStar = np.asarray(self.RStar)
    elif hasattr(self, 'RadTemperature') and hasattr(self, 'RStar'):
        radTemperature = self.RadTemperature
        rStar = self.RStar
    #
    #
    #
    if self.Ncilvl:
        ci = 1
        cilvl = self.Cilvl
#            if hasattr(self, 'CilvlRate'):
#                cilvlRate = self.CilvlRate
#            else:
#                self.cireclvlDescale('cilvl')
#                cilvlRate = self.CilvlRate
        self.recombRate()
        #
        lowers = util.zion2name(self.Z, self.Ion-1)
        # get the lower ionization stage
        self.Lower = ion(lowers, temperature=self.Temperature, eDensity = self.EDensity)
        self.Lower.ionizRate()
        # need to get multiplicity of lower ionization stage
        lowMult = self.Lower.Elvlc['mult']
    else:
        ci = 0
    #  evetually will be looking for just an recLvl attribute
    #
    #  if the higher ion does not exist in the database, rec=0
    highers = util.zion2name(self.Z, self.Ion+1)
    if self.Nreclvl:
        reclvl = self.Reclvl
        if hasattr(self, 'ReclvlRate'):
            reclvlRate = self.ReclvlRate
        else:
            self.cireclvlDescale('reclvl')
            reclvlRate = self.ReclvlRate
    if hasattr(self, 'Auto'):
        self.drRateLvl()
    if self.Nreclvl or hasattr(self, 'Auto'):
        rec=1
        # get ionization rate of this ion
        self.ionizRate()
            #  get the higher ionization stage
    else:
        rec = 0
    #  if the higher ion does not exist in the database, rec=0
    highers = util.zion2name(self.Z, self.Ion+1)
#        if highers in chdata.MasterList:
    self.Higher = ion(highers, temperature=self.Temperature, eDensity=self.EDensity)
    self.Higher.recombRate()
#        else:
#        #
    rad=np.zeros((nlvls+ci+rec,nlvls+ci+rec),"float64")  #  the populating matrix for radiative transitions
    #
    #
    for iwgfa in range(nwgfa):
        l1 = self.Wgfa["lvl1"][iwgfa]-1
        l2 = self.Wgfa["lvl2"][iwgfa]-1
        rad[l1+ci,l2+ci] += self.Wgfa["avalue"][iwgfa]
        rad[l2+ci,l2+ci] -= self.Wgfa["avalue"][iwgfa]
        # photo-excitation and stimulated emission
        if self.RadTemperature:
            if not self.RStar:
                dilute = 0.5
            else:
                dilute = util.dilute(self.RStar)
            # next - don't include autoionization lines
            if abs(self.Wgfa['wvl'][iwgfa]) > 0.:
                if self.Elvlc['ecm'][l2] >= 0.:
                    ecm2 = self.Elvlc['ecm'][l2]
                else:
                    ecm2 = self.Elvlc['ecmth'][l2]
                #
                if self.Elvlc['ecm'][l1] >= 0.:
                    ecm1 = self.Elvlc['ecm'][l1]
                else:
                    ecm1 = self.Elvlc['ecmth'][l1]
                de = const.invCm2Erg*(ecm2 - ecm1)
                dekt = de/(const.boltzmann*self.RadTemperature)
                # photoexcitation
                phexFactor = dilute*(float(self.Elvlc['mult'][l2])/float(self.Elvlc['mult'][l1]))/(np.exp(dekt) -1.)
                rad[l2+ci,l1+ci] += self.Wgfa["avalue"][iwgfa]*phexFactor
                rad[l1+ci,l1+ci] -= self.Wgfa["avalue"][iwgfa]*phexFactor
                # stimulated emission
                stemFactor = dilute/(np.exp(-dekt) -1.)
                rad[l1+ci,l2+ci] += self.Wgfa["avalue"][iwgfa]*stemFactor
                rad[l2+ci,l2+ci] -= self.Wgfa["avalue"][iwgfa]*stemFactor
    if hasattr(self, 'Auto'):
        # as of 7/2012, we only consider the ground level in the next higher ionization stage
        # hence, requiring lvl1 = 1 or, l1 = 0
        for iauto, avalue in enumerate(self.Auto['avalue']):
            l1 = self.Auto["lvl1"][iauto] -1
            l2 = self.Auto["lvl2"][iauto] -1
            # for now only consider a single level for upper/higher ion
            if l1 == 0 and rec:
                rad[l1 + ci + nlvls, l2 + ci] += avalue
                rad[l2 + ci,  l2 + ci] -= avalue
            # if the higher ion is not in the database, decay to the ground level
            elif l1 == 0:
                rad[l1 + ci, l2 + ci] += avalue
                rad[l2 + ci,  l2 + ci] -= avalue

    #
    #
    if self.Nscups:
#            print(' Nscups = %10i'%(self.Nscups))
        self.upsilonDescale()
        ups = self.Upsilon['upsilon']
        exRate = self.Upsilon['exRate']
        dexRate = self.Upsilon['dexRate']
    #
    if self.Npsplups:
        self.upsilonDescaleSplups(prot=1)
#            pups = self.PUpsilon['upsilon']
        pexRate = self.PUpsilon['exRate']
        pdexRate = self.PUpsilon['dexRate']
    #
    temp=temperature
    ntemp=temp.size
    #
    cc=const.collision*self.EDensity
    ndens=cc.size
    if self.Npsplups:
        cp=const.collision*protonDensity
    if ntemp > 1 and ndens >1 and ntemp != ndens:
        print(' unless temperature or eDensity are single values')
        print(' the number of temperatures values must match the ')
        print(' the number of eDensity values')
        return
    #
    # get corrections for recombination and excitation
    #
    nscups = self.Nscups
    #
    #  first, for ntemp=ndens=1
    if ndens==1 and ntemp==1:
        popmat=np.copy(rad)
        for iscups in range(0,nscups):
            l1=self.Scups["lvl1"][iscups]-1
            l2=self.Scups["lvl2"][iscups]-1
            #
            popmat[l1+ci,l2+ci] += self.EDensity*dexRate[iscups]
            popmat[l2+ci,l1+ci] += self.EDensity*exRate[iscups]
            popmat[l1+ci,l1+ci] -= self.EDensity*exRate[iscups]
            popmat[l2+ci,l2+ci] -= self.EDensity*dexRate[iscups]
            #
        for isplups in range(0,npsplups):
            l1=self.Psplups["lvl1"][isplups]-1
            l2=self.Psplups["lvl2"][isplups]-1
             #
            popmat[l1+ci,l2+ci] += self.PDensity*pdexRate[isplups]
            popmat[l2+ci,l1+ci] += self.PDensity*pexRate[isplups]
            popmat[l1+ci,l1+ci] -= self.PDensity*pexRate[isplups]
            popmat[l2+ci,l2+ci] -= self.PDensity*pdexRate[isplups]
       # now include ionization rate from
        if ci:
            #
            # the ciRate can be computed for all temperatures
            #
            ciTot = 0.
            for itrans in range(len(cilvl['lvl1'])):
                lvl1 = cilvl['lvl1'][itrans]-1
                lvl2 = cilvl['lvl2'][itrans]-1
                # this is kind of double booking the ionization rate components
                popmat[lvl2+ci, lvl1] += self.EDensity*self.CilvlRate['rate'][itrans]
                popmat[lvl1, lvl1] -= self.EDensity*self.CilvlRate['rate'][itrans]
                ciTot += self.EDensity*self.CilvlRate['rate'][itrans]
            #
            popmat[1, 0] += (self.EDensity*self.Lower.IonizRate['rate'] - ciTot)
            popmat[0, 0] -= (self.EDensity*self.Lower.IonizRate['rate'] - ciTot)
            popmat[0, 1] += self.EDensity*self.RecombRate['rate']
            popmat[1, 1] -= self.EDensity*self.RecombRate['rate']
        if rec:
            # ntemp=ndens=1
            #
            if hasattr(self, 'DrRateLvl'):
#                    branch = np.zeros(self.Ndielsplups, 'float64')
                for idr, rate in enumerate(self.DrRateLvl['rate']):
                    l1 = self.Auto["lvl1"][idr] - 1
                    l2 = self.Auto["lvl2"][idr] - 1
#                        popmat[l2+ci,-1] += self.EDensity*self.DrRateLvl['rate'][idr]
#                        popmat[-1, -1] -= self.EDensity*self.DrRateLvl['rate'][idr]
                    popmat[l2+ci,-1] += self.EDensity*rate
                    popmat[-1, -1] -= self.EDensity*rate
                    #
                dielTot = self.DrRateLvl['totalRate'][0]
            else:
                dielTot = 0.
            #
            #
            #
            for itrans in range(self.Nreclvl):
#                    lvl1 = reclvl['lvl1'][itrans]-1
                lvl2 = reclvl['lvl2'][itrans]-1
                popmat[lvl2+ci, -1] += self.EDensity*reclvlRate['rate'][itrans]
                popmat[-1, -1] -= self.EDensity*reclvlRate['rate'][itrans]
            if self.Nreclvl:
                recTot = reclvlRate['rate'].sum(axis=0)
            else:
                recTot = 0.
            #
            #
            popmat[-1,  ci] += self.EDensity*self.IonizRate['rate']
            popmat[ci, ci] -= self.EDensity*self.IonizRate['rate']
            #
            # next 2 line take care of overbooking
            netRecomb = self.EDensity*(self.Higher.RecombRate['rate'][0]- recTot - dielTot)
            #
            if netRecomb > 0.:
                popmat[ci, -1] += netRecomb
                popmat[-1, -1] -= netRecomb
        #
        # normalize to unity
        norm=np.ones(nlvls+ci+rec,'float64')
        if ci:
            norm[0] = 0.
        if rec:
            norm[nlvls+ci+rec-1] = 0.
        popmata = np.copy(popmat)
        normRow = (nlvls+ci+rec-1)/2
        #popmata[nlvls+ci+rec-1]=norm
        popmata[normRow]=norm
        b=np.zeros(nlvls+ci+rec,'float64')
        #b[nlvls+ci+rec-1]=1.
        b[normRow] = 1.
        try:
            fullpop=np.linalg.solve(popmata,b)
            pop = fullpop[ci:ci+nlvls]
            if rec:
                popHigher = fullpop[-1]
            else:
                popHigher = 0.
        except np.linalg.LinAlgError:
            pop = np.zeros(nlvls, 'float64')
            popHigher = 0.
#                print ' error in matrix inversion, setting populations to zero at T = ', ('%8.2e')%(temperature)
    #
    # ------------- ntemp = 1 ---------------------------------------------------------
    #
    #
    elif ndens == 1:
        pop = np.zeros((ntemp, nlvls),"float64")
        popHigher = np.zeros(ntemp, 'float64')
#            pop=np.zeros((ntemp,ci + nlvls + rec),"float64")
        for itemp in range(ntemp):
            popmat=np.copy(rad)
            for iscups in range(0,nscups):
                l1=self.Scups["lvl1"][iscups]-1
                l2=self.Scups["lvl2"][iscups]-1
                popmat[l1+ci,l2+ci] += self.EDensity*dexRate[iscups, itemp]
                popmat[l2+ci,l1+ci] += self.EDensity*exRate[iscups, itemp]
                popmat[l1+ci,l1+ci] -= self.EDensity*exRate[iscups, itemp]
                popmat[l2+ci,l2+ci] -= self.EDensity*dexRate[iscups, itemp]
            for isplups in range(0,npsplups):
                l1=self.Psplups["lvl1"][isplups]-1
                l2=self.Psplups["lvl2"][isplups]-1
                # for proton excitation, the levels are all below the ionization potential
                 #
                popmat[l1+ci,l2+ci] += self.PDensity[itemp]*pdexRate[isplups, itemp]
                popmat[l2+ci,l1+ci] += self.PDensity[itemp]*pexRate[isplups, itemp]
                popmat[l1+ci,l1+ci] -= self.PDensity[itemp]*pexRate[isplups, itemp]
                popmat[l2+ci,l2+ci] -= self.PDensity[itemp]*pdexRate[isplups, itemp]
            # now include ionization rate from
            if ci:
                #
                # the ciRate can be computed for all temperatures
                #
                ciTot = 0.
                for itrans in range(len(cilvl['lvl1'])):
                    lvl1 = cilvl['lvl1'][itrans]-1
                    lvl2 = cilvl['lvl2'][itrans]-1
                    #mult = lowMult[lvl1-1]
                    popmat[lvl2+ci, lvl1] += self.EDensity*self.CilvlRate['rate'][itrans, itemp]
                    popmat[lvl1, lvl1] -= self.EDensity*self.CilvlRate['rate'][itrans, itemp]
                    ciTot += self.EDensity*self.CilvlRate['rate'][itrans, itemp]
                #
                popmat[1, 0] += (self.EDensity*self.Lower.IonizRate['rate'][itemp] - ciTot)
                popmat[0, 0] -= (self.EDensity*self.Lower.IonizRate['rate'][itemp] - ciTot)
                popmat[0, 1] += self.EDensity*self.RecombRate['rate'][itemp]
                popmat[1, 1] -= self.EDensity*self.RecombRate['rate'][itemp]
            if rec:
            #
            # ndens=1
                if hasattr(self, 'DrRateLvl'):
                    for idr, rate in enumerate(self.DrRateLvl['rate']):
                        l1 = self.Auto["lvl1"][idr] - 1
                        l2 = self.Auto["lvl2"][idr] - 1
                        popmat[l2 + ci, -1] += self.EDensity*rate[itemp]
                        popmat[-1, -1] -= self.EDensity*rate[itemp]
                        #
                    # DrRateLvl['totalRate'] includes the branching ratio
                    dielTot = self.DrRateLvl['totalRate'][itemp]
                else:
                    dielTot = 0.
                #
                #
                for itrans in range(self.Nreclvl):
                    lvl1 = reclvl['lvl1'][itrans]-1
                    lvl2 = reclvl['lvl2'][itrans]-1
                    popmat[lvl2+ci, -1] += self.EDensity*self.ReclvlRate['rate'][itrans, itemp]
                    popmat[-1, -1] -= self.EDensity*self.ReclvlRate['rate'][itrans, itemp]
                #
                if self.Nreclvl:
                    recTot = self.ReclvlRate['rate'][:, itemp].sum()
                else:
                    recTot = 0.
                #
                #
                popmat[-1, ci] += self.EDensity*self.IonizRate['rate'][itemp]
                popmat[ci, ci] -= self.EDensity*self.IonizRate['rate'][itemp]
                #
                netRecomb = self.EDensity*(self.Higher.RecombRate['rate'][itemp]- recTot - dielTot)
                #
                if netRecomb > 0.:
                    popmat[ci, -1] += netRecomb
                    popmat[-1, -1] -= netRecomb
                #
            # normalize to unity
            # ndens = 1
            norm=np.ones(nlvls+ci+rec,'float64')
            if ci:
                norm[0] = 0.
            if rec:
                norm[-1] = 0.
            popmata = np.copy(popmat)
            normRow = (nlvls+ci+rec-1)/2
            #popmata[nlvls+ci+rec-1]=norm
            popmata[normRow]=norm
            b=np.zeros(nlvls+ci+rec,'float64')
            #b[nlvls+ci+rec-1]=1.
            b[normRow] = 1.
            #popmata[nlvls+ci+rec-1]=norm
            #b=np.zeros(nlvls+ci+rec,'float64')
            #b[nlvls+ci+rec-1] = 1.
#                b[-1] = 1.
            try:
                thispop=np.linalg.solve(popmata,b)
                pop[itemp] = thispop[ci:ci+nlvls]
                popHigher[itemp] = thispop[-1]
            except np.linalg.LinAlgError:
                pop[itemp] = np.zeros(nlvls, 'float64')
                popHigher[itemp] = 0.
#                    print ' error in matrix inversion, setting populations to zero at T = ', ('%8.2e')%(temperature[itemp])
        #
    elif ntemp == 1:
        pop=np.zeros((ndens,nlvls),"float64")
        popHigher = np.zeros(ndens, 'float64')
        for idens in range(0,ndens):
            popmat=np.copy(rad)
            for iscups in range(0,nscups):
                l1=self.Scups["lvl1"][iscups]-1
                l2=self.Scups["lvl2"][iscups]-1
            #
                popmat[l1+ci,l2+ci] += self.EDensity[idens]*dexRate[iscups]
                popmat[l2+ci,l1+ci] += self.EDensity[idens]*exRate[iscups]
                popmat[l1+ci,l1+ci] -= self.EDensity[idens]*exRate[iscups]
                popmat[l2+ci,l2+ci] -= self.EDensity[idens]*dexRate[iscups]
            #
            for isplups in range(0,npsplups):
                l1=self.Psplups["lvl1"][isplups]-1
                l2=self.Psplups["lvl2"][isplups]-1
             #
                popmat[l1+ci,l2+ci] += self.PDensity[idens]*pdexRate[isplups]
                popmat[l2+ci,l1+ci] += self.PDensity[idens]*pexRate[isplups]
                popmat[l1+ci,l1+ci] -= self.PDensity[idens]*pexRate[isplups]
                popmat[l2+ci,l2+ci] -= self.PDensity[idens]*pdexRate[isplups]
            # now include ionization rate from
            if ci:
                #
                #
                ciTot = 0.
                for itrans in range(len(cilvl['lvl1'])):
                    lvl1 = cilvl['lvl1'][itrans] -1
                    lvl2 = cilvl['lvl2'][itrans] -1
                    popmat[lvl2+ci, lvl1] += self.EDensity[idens]*self.CilvlRate['rate'][itrans]
                    popmat[lvl1, lvl1] -= self.EDensity[idens]*self.CilvlRate['rate'][itrans]
                    ciTot += self.EDensity[idens]*self.CilvlRate['rate'][itrans]
                popmat[1, 0] += (self.EDensity[idens]*self.Lower.IonizRate['rate'] -ciTot)
                popmat[0, 0] -= (self.EDensity[idens]*self.Lower.IonizRate['rate'] -ciTot)
                popmat[0, 1] += self.EDensity[idens]*self.RecombRate['rate']
                popmat[1, 1] -= self.EDensity[idens]*self.RecombRate['rate']
            if rec:
                #ntemp = 1
                if hasattr(self, 'DrRateLvl'):
                    for idr, rate in enumerate(self.DrRateLvl['rate']):
                        l1 = self.Auto["lvl1"][idr] - 1
                        l2 = self.Auto["lvl2"][idr] - 1
#                            popmat[l2+ci,l1+ci+nlvls] += self.EDensity[idens]*self.DrRateLvl['rate'][idr]
#                            popmat[l1+ci,l1+ci] -= self.EDensity[idens]*self.DrRateLvl['rate'][idr]
                        popmat[l2+ci,-1] += self.EDensity[idens]*rate
                        popmat[-1, -1] -= self.EDensity[idens]*rate
                        #
                    dielTot = self.DrRateLvl['totalRate'][0]
                else:
                    dielTot = 0.
                if self.Nreclvl:
                    recTot = self.ReclvlRate['rate'].sum()
                else:
                    recTot = 0.
                #
                popmat[-1,  ci] += self.EDensity[idens]*self.IonizRate['rate']
                popmat[ci, ci] -= self.EDensity[idens]*self.IonizRate['rate']
                #
                netRecomb = self.EDensity[idens]*(self.Higher.RecombRate['rate'] - recTot - dielTot)
                if netRecomb > 0.:
                    popmat[ci, -1] += netRecomb
                    popmat[-1, -1] -= netRecomb
                #
#                    for itrans in range(len(rrlvl['lvl1'])):
                for itrans in range(self.Nreclvl):
                    lvl1 = reclvl['lvl1'][itrans]-1
                    lvl2 = reclvl['lvl2'][itrans]-1
                    popmat[lvl2+ci, -1] += self.EDensity[idens]*self.ReclvlRate['rate'][itrans]
                    popmat[-1, -1] -= self.EDensity[idens]*self.ReclvlRate['rate'][itrans]
            #
            # normalize to unity
            norm=np.ones(nlvls+ci+rec,'float64')
            if ci:
                norm[0] = 0.
            if rec:
                norm[-1] = 0.
            popmata = np.copy(popmat)
            popmata[nlvls+ci+rec-1] = norm
#                popmat[nlvls+ci+rec-1] = norm
            b = np.zeros(nlvls+ci+rec,'float64')
            b[nlvls+ci+rec-1] = 1.
            try:
                thispop=np.linalg.solve(popmata,b)
                pop[idens] = thispop[ci:ci+nlvls]
                popHigher[idens] = thispop[-1]
            except np.linalg.LinAlgError:
                pop[idens] = np.zeros(nlvls, 'float64')
                popHigher[idens] = 0.
#                    print ' error in matrix inversion, setting populations to zero at eDensity = ', ('%8.2e')%(eDensity[idens])
            #
    elif ntemp>1  and ntemp==ndens:
        pop=np.zeros((ntemp,nlvls),"float64")
        popHigher = np.zeros(ntemp, 'float64')
        for itemp in range(0,ntemp):
            temp=self.Temperature[itemp]
            popmat=np.copy(rad)
            for iscups in range(0,nscups):
                l1=self.Scups["lvl1"][iscups]-1
                l2=self.Scups["lvl2"][iscups]-1
                #
                popmat[l1+ci,l2+ci] += self.EDensity[itemp]*dexRate[iscups, itemp]
                popmat[l2+ci,l1+ci] += self.EDensity[itemp]*exRate[iscups, itemp]
                popmat[l1+ci,l1+ci] -= self.EDensity[itemp]*exRate[iscups, itemp]
                popmat[l2+ci,l2+ci] -= self.EDensity[itemp]*dexRate[iscups, itemp]
            # proton rates
            for isplups in range(0,npsplups):
                l1=self.Psplups["lvl1"][isplups]-1
                l2=self.Psplups["lvl2"][isplups]-1
                # for proton excitation, the levels are all below the ionization potential
                 #
                popmat[l1+ci,l2+ci] += self.PDensity[itemp]*pdexRate[isplups, itemp]
                popmat[l2+ci,l1+ci] += self.PDensity[itemp]*pexRate[isplups, itemp]
                popmat[l1+ci,l1+ci] -= self.PDensity[itemp]*pexRate[isplups, itemp]
                popmat[l2+ci,l2+ci] -= self.PDensity[itemp]*pdexRate[isplups, itemp]
            # now include ionization rate from
            if ci:
                #
                # the ciRate can be computed for all temperatures
                #
                ciTot = 0.
                for itrans in range(len(cilvl['lvl1'])):
                    lvl1 = cilvl['lvl1'][itrans] -1
                    lvl2 = cilvl['lvl2'][itrans] -1
                    popmat[lvl2+ci, lvl1] += self.EDensity[itemp]*self.CilvlRate['rate'][itrans, itemp]
                    popmat[lvl1, lvl1] -= self.EDensity[itemp]*self.CilvlRate['rate'][itrans, itemp]
                    ciTot += self.EDensity[itemp]*self.CilvlRAte['rate'][itrans, itemp]
                popmat[1, 0] += (self.EDensity[itemp]*self.Lower.IonizRate['rate'][itemp] - ciTot)
                popmat[0, 0] -= (self.EDensity[itemp]*self.Lower.IonizRate['rate'][itemp] - ciTot)
                popmat[0, 1] += self.EDensity[itemp]*self.RecombRate['rate'][itemp]
                popmat[1, 1] -= self.EDensity[itemp]*self.RecombRate['rate'][itemp]
            if rec:
            #
                if hasattr(self, 'DrRateLvl'):
#                    branch = np.zeros(self.Ndielsplups, 'float64')
                    for idr, rate in enumerate(self.DrRateLvl['rate']):
                        l1 = self.Auto["lvl1"][idr] - 1
                        l2 = self.Auto["lvl2"][idr] - 1
                        popmat[l2+ci,l1+ci+nlvls] += self.EDensity[itemp]*rate[itemp]
                        popmat[-1, -1] -= self.EDensity[itemp]*rate[itemp]
                        #
                    dielTot = self.DrRateLvl['totalRate'][itemp]
                else:
                    dielTot = 0.
            #
                if self.Nreclvl:
                    recTot = self.RrlvlRate['rate'][:, itemp].sum()
                else:
                    recTot = 0.
            #
                #
                popmat[-1,  ci] += self.EDensity[itemp]*self.IonizRate['rate'][itemp]
                popmat[ci, ci] -= self.EDensity[itemp]*self.IonizRate['rate'][itemp]
                #
                netRecomb = self.EDensity[itemp]*(self.Higher.RecombRate['rate'][itemp] - recTot - dielTot)
                if netRecomb > 0.:
                    popmat[ci, -1] += netRecomb
                    popmat[-1, -1] -= netRecomb
                #
                for itrans in range(self.Nreclvl):
                    lvl1 = reclvl['lvl1'][itrans]-1
                    lvl2 = reclvl['lvl2'][itrans]-1
                    popmat[lvl2+ci, -1] += self.EDensity[itemp]*self.RrlvlRate['rate'][itrans, itemp]
                    popmat[-1, -1] -= self.EDensity[itemp]*self.RrlvlRate['rate'][itrans, itemp]
            #
            # normalize to unity
            norm=np.ones(nlvls+ci+rec,'float64')
            if ci:
                norm[0] = 0.
            if rec:
                norm[-1] = 0.
            popmata = np.copy(popmat)
            popmata[nlvls+ci+rec-1] = norm
#                popmat[nlvls+ci+rec-1] = norm
            b=np.zeros(nlvls+ci+rec,'float64')
            b[nlvls+ci+rec-1]=1.
            try:
                thispop=np.linalg.solve(popmata,b)
                pop[itemp] = thispop[ci:ci+nlvls]
                popHigher[itemp] = thispop[-1]
            except np.linalg.LinAlgError:
                pop[itemp] = np.zeros(nlvls, 'float64')
                popHigher[itemp] = 0.
#                    print ' error in matrix inversion, setting populations to zero at T = ', ('%8.2e')%(temperature[itemp])
        #
    pop=np.where(pop >0., pop,0.)
    self.Population={"temperature":temperature, "eDensity":eDensity, "population":pop, "protonDensity":protonDensity, "ci":ci, "rec":rec, 'popmat':popmata, 'b':b, 'rad':rad}
    if rec:
        self.Population['popHigher']= popHigher
        self.Population['higher'] = self.Higher
        self.Population['recTot'] = recTot
        self.Population['dielTot'] = dielTot
        self.Population['netRecomb'] = netRecomb
    #
    return
Beispiel #9
0
    def calculate(self, temperature):
        """
        Calculate ion fractions for given temperature array using the total
        ionization and recombination rates.
        """
        self.Temperature = np.array(temperature, 'float64')
        if self.Temperature.size == 1:
            print(' temperature must be an array')
            return
        ionList = []
        chIons = []
        z = self.Z
        for stage in range(1, z+2):
            ionStr = util.zion2name(z, stage)
            ionList.append(ionStr)
            atom = ion(ionStr, temperature = self.Temperature)
            atom.ionizRate()
            atom.recombRate()
            chIons.append(atom)

        ntemp = chIons[0].IonizRate['temperature'].size
        if ntemp == 1:
            ioneq = np.zeros((z+1), 'Float64')
            factor = []
            for anIon in chIons:
                if hasattr(anIon, 'RecombRate') and hasattr(anIon, 'IonizRate'):
                    rat = anIon.IonizRate['rate']/anIon.RecombRate['rate']
                    factor.append(rat**2 + rat**(-2))
                else:
                    factor.append(0.)
            factor[0] = max(factor)
            factor[-1] = max(factor)
            ionmax = factor.index(min(factor))
            ioneq[ionmax] = 1.

            for iz in range(ionmax+1, z+1):
                ionrate = chIons[iz-1].IonizRate['rate']
                recrate = chIons[iz].RecombRate['rate']
                ioneq[iz] = ionrate*ioneq[iz-1]/recrate

            for iz in range(ionmax-1, -1, -1):
                ionrate = chIons[iz].IonizRate['rate']
                recrate = chIons[iz+1].RecombRate['rate']
                ioneq[iz] = recrate*ioneq[iz+1]/ionrate
            ionsum = ioneq.sum()
            ioneq = ioneq/ionsum
            self.Ioneq = ioneq
        else:
            ioneq = np.zeros((z+1,ntemp ), 'Float64')
            for it in range(ntemp):
                factor = []
                for anIon in chIons:
                    if type(anIon.IonizRate) != type(None) and type(anIon.RecombRate) != type(None):
                        ioniz = anIon.IonizRate['rate'][it]
                        recomb = anIon.RecombRate['rate'][it]
                        if ioniz == 0. or recomb == 0.:
                            rat = 1.e-100
                        else:
                            rat = anIon.IonizRate['rate'][it]/anIon.RecombRate['rate'][it]
                        try:
                            factor.append(rat**2 + rat**(-2))
                        except:
                            factor.append(0.)
                    else:
                        factor.append(0.)
                factor[0] = max(factor)
                factor[-1] = max(factor)
                ionmax = factor.index(min(factor))
                ioneq[ionmax, it] = 1.

                for iz in range(ionmax+1, z+1):
                    ionrate = chIons[iz-1].IonizRate['rate'][it]
                    recrate = chIons[iz].RecombRate['rate'][it]
                    if recrate != 0.:
                        ioneq[iz, it] = ionrate*ioneq[iz-1, it]/recrate
                    else:
                        ioneq[iz, it] = 0.

                for iz in range(ionmax-1, -1, -1):
                    ionrate = chIons[iz].IonizRate['rate'][it]
                    recrate = chIons[iz+1].RecombRate['rate'][it]
                    if ionrate != 0.:
                        ioneq[iz, it] = recrate*ioneq[iz+1, it]/ionrate
                    else:
                        ioneq[iz, it] = 0.
                ionsum = ioneq[:, it].sum()
                ioneq[:, it] = ioneq[:, it]/ionsum
            self.Ioneq = ioneq