コード例 #1
0
ファイル: manage_phyat_list.py プロジェクト: Morisset/pySSN
def get_atom_str(atom):
    """
    return atom with leading 0.
    eg: get_atom_str('Mg5') -> Mg005
    """
    ato, ion = parseAtom(atom)
    return('{}{:03d}'.format(remove_iso(ato), int(ion)))
コード例 #2
0
def get_atom_str(atom):
    """
    return atom with leading 0.
    eg: get_atom_str('Mg5') -> Mg005
    """
    ato, ion = parseAtom(atom)
    return ('{}{:03d}'.format(remove_iso(ato), int(ion)))
コード例 #3
0
def p5():

    # this will plot all the available data in pyneb for the omegas
    ions = [
        'C2',
        'C3',
        'N2',
        'N3',
        'O2',
        'O3',
        'O4',
        'Ne2',
        'Ne3',
        'Ne5',
        'S2',
        'S3',
        'S4',
        'Cl3',
        'Ar2',
        'Ar3',
        'Ar4',
        'Ar5',
    ]

    for ion in ions:
        # split ion into elem and spec, e.g 'O3' into 'O' and 3
        elem, spec = parseAtom(ion)
        # instanciate the corresponding Atom object
        atom = pn.Atom(elem, spec)
        # print information including transition probabilities
        #atom.printIonic(printA = True)
        dp = pn.DataPlot(elem, spec)
        dp.plotOmega(save=True)
コード例 #4
0
def plot_2comp(tem1=1e4, tem2=1e4, dens1=3e2, dens2=5e5, mass1=1, mass2=5e-4):

    # List of diagnostics used to analyze the region
    diags = pn.Diagnostics()

    diags.addDiag([
        '[NI] 5198/5200', '[NII] 5755/6548', '[OII] 3726/3729',
        '[OII] 3727+/7325+', '[OIII] 4363/5007', '[ArIII] 5192/7136',
        '[ArIII] 5192/7300+', '[ArIV] 4740/4711', '[ArIV] 7230+/4720+',
        '[SII] 6731/6716', '[SII] 4072+/6720+', '[SIII] 6312/9069',
        '[ClIII] 5538/5518'
    ])
    """    
    for diag in pn.diags_dict:
        if diag[0:7] != '[FeIII]':
            diags.addDiag(diag)
            print('Adding', diag)
    diags.addClabel('[SIII] 6312/9069', '[SIII]A')
    diags.addClabel('[OIII] 4363/5007', '[OIII]A')
    """
    # Define all the ions that are involved in the diagnostics
    adict = diags.atomDict
    pn.log_.message('Atoms built')

    obs = pn.Observation(corrected=True)
    for atom in adict:
        # Computes all the intensities of all the lines of all the ions considered
        for line in pn.LINE_LABEL_LIST[atom]:
            if line[-1] == 'm':
                wavelength = float(line[:-1]) * 1e4
            else:
                wavelength = float(line[:-1])
            elem, spec = parseAtom(atom)
            intens1 = adict[atom].getEmissivity(
                tem1, dens1, wave=wavelength) * dens1 * mass1
            intens2 = adict[atom].getEmissivity(
                tem2, dens2, wave=wavelength) * dens2 * mass2
            obs.addLine(
                pn.EmissionLine(
                    elem,
                    spec,
                    wavelength,
                    obsIntens=[intens1, intens2, intens1 + intens2],
                    obsError=[0.0, 0.0, 0.0]))

    pn.log_.message('Virtual observations computed')
    emisgrids = pn.getEmisGridDict(atomDict=adict)

    pn.log_.message('EmisGrids available')

    # Produce a diagnostic plot for each of the two regions and another one for the
    # (misanalyzed) overall region
    f, axes = plt.subplots(2, 2)

    diags.plot(emisgrids, obs, i_obs=0, ax=axes[0, 0])
    diags.plot(emisgrids, obs, i_obs=1, ax=axes[0, 1])
    diags.plot(emisgrids, obs, i_obs=2, ax=axes[1, 0])
コード例 #5
0
def p1(ion):
    # split ion into elem and spec, e.g 'O3' into 'O' and 3
    elem, spec = parseAtom(ion)
    # instanciate the corresponding Atom object
    atom = pn.Atom(elem, spec)
    # print information including transition probabilities
    #atom.printIonic(printA = True)
    # prepare a new figure
    plt.figure()
    # plot energy levels
    atom.plotGrotrian()
コード例 #6
0
def plot_2comp(tem1=1e4, tem2=1e4, dens1=3e2, dens2=5e5, mass1=1, mass2=5e-4):

    # List of diagnostics used to analyze the region
    diags = pn.Diagnostics()

    for diag in pn.diags_dict:
        if diag[0:7] != '[FeIII]':
            diags.addDiag(diag)
    diags.addClabel('[SIII] 6312/9069', '[SIII]A')
    diags.addClabel('[OIII] 4363/5007', '[OIII]A')

    # Define all the ions that are involved in the diagnostics
    all_atoms = diags.atomDict

    pn.log_.message('Atoms built')

    obs = pn.Observation(corrected=True)
    for atom in all_atoms:
        # Computes all the intensities of all the lines of all the ions considered
        for wavelength in all_atoms[atom].lineList:
            elem, spec = parseAtom(atom)
            intens1 = all_atoms[atom].getEmissivity(
                tem1, dens1, wave=wavelength) * dens1 * mass1
            intens2 = all_atoms[atom].getEmissivity(
                tem2, dens2, wave=wavelength) * dens2 * mass2
            obs.addLine(
                pn.EmissionLine(
                    elem,
                    spec,
                    wavelength,
                    obsIntens=[intens1, intens2, intens1 + intens2],
                    obsError=[0.0, 0.0, 0.0]))

    pn.log_.message('Virtual observations computed')

    emisgrids = pn.getEmisGridDict(atomDict=all_atoms)

    pn.log_.message('EmisGrids available')

    # Produce a diagnostic plot for each of the two regions and another one for the (misanalyzed) overall region

    plt.subplot(2, 2, 1)
    diags.plot(emisgrids, obs, i_obs=0)

    plt.subplot(2, 2, 2)
    diags.plot(emisgrids, obs, i_obs=1)

    plt.subplot(2, 1, 2)
    pn.log_.level = 3
    diags.plot(emisgrids, obs, i_obs=2)
コード例 #7
0
def p2(diag):
    # get the ion, and diagnostic description from the dictionary:
    ion, diag_eval, err = pn.diags_dict[diag]
    # split ion into elem and spec, e.g 'O3' into 'O' and 3
    elem, spec = parseAtom(ion)
    # prepare a new figure
    plt.figure()
    # create a grid of emissivities
    #NB: one can use a pypic file containing all the emissivities, if already made
    # in that case set restore_file to the name of the pypic file.
    grid = pn.EmisGrid(elem, spec, restore_file=None, OmegaInterp='Linear')
    # plot the contours
    grid.plotContours(to_eval=diag_eval,
                      low_level=None,
                      high_level=None,
                      n_levels=20,
                      linestyles='-',
                      clabels=True,
                      log_levels=True,
                      title='{0} {1}'.format(ion, diag_eval))
    # save the plot into pdf files
    plt.savefig('{0}_{1}.pdf'.format(ion, diag_eval.replace('/', '_')))
コード例 #8
0
ファイル: diags.py プロジェクト: kakirastern/PyNeb_devel
    def getCrossTemDen(self, diag_tem, diag_den, value_tem=None, value_den=None, obs=None, i_obs=None,
                       guess_tem=10000, tol_tem=1., tol_den=1., max_iter=5, maxError=1e-3,
                       start_tem= -1, end_tem= -1, start_den= -1, end_den= -1):
        """
        Cross-converge the temperature and density derived from two sensitive line ratios, by inputting the quantity 
        derived with one line ratio into the other and then iterating.
        The temperature- and density-sensitive ratios can be input directly or as an Observation object
    
        Parameters:
    
        - diag_tem   temperature-sensitive diagnostic line ratio
        - diag_den   density-sensitive diagnostic line ratio
        - value_tem  value of the temperature-sensitive diagnostic
        - value_den  value of the density-sensitive diagnostic
        - obs        np.Observation object. Values for observed temperature and density diagnostics are
                        taken from it if value_tem and value_den are None
        - i_obs      index of the observations to be used from obs. 
                        If None, all the observations are considered.
                        May be a boolean array
        - guess_tem  temperature assumed in the first iteration, in K
        - tol_tem    tolerance of the temperature result, in %
        - tol_den    tolerance of the density result, in %
        - max_iter   maximum number of iterations to be performed, after which the function will throw a result
        - maxError   maximum error in the calls to getTemDen, in %
        - start_tem, end_tem  lower and upper limit of the explored temperature range 
        - start_den, end_den  lower and upper limit of the explored density range 
    
        Example:
            tem, den = diags.getCrossTemDen('[OIII] 4363/5007', '[SII] 6731/6716', 0.0050, 1.0, 
                        guess_tem=10000, tol_tem = 1., tol_den = 1., max_iter = 5)

        """
        # TODO:
        # Define a lower/upper density and temperature 
        # to be used in case the diag is pointing to values out of the boundaires
        if diag_tem not in self.diags:
            self.addDiag(diag_tem)
        atom_tem = self.diags[diag_tem][0]
        elem_tem, spec_tem = parseAtom(atom_tem)
        if atom_tem not in self.atomDict:
            self.atomDict[atom_tem] = pn.Atom(elem_tem, spec_tem, self.OmegaInterp, NLevels=self.NLevels)
        atom_tem = self.atomDict[atom_tem]
        if diag_den not in self.diags:
            self.addDiag(diag_den)
        atom_den = self.diags[diag_den][0]
        elem_den, spec_den = parseAtom(self.diags[diag_den][0])
        if (atom_den) not in self.atomDict:
            self.atomDict[atom_den] = pn.Atom(elem_den, spec_den, self.OmegaInterp, NLevels=self.NLevels)
        atom_den = self.atomDict[atom_den]
        eval_tem = self.diags[diag_tem][1]
        eval_den = self.diags[diag_den][1]
        calling = 'Diag.getCrossTemDen %s %s' % (diag_tem, diag_den)
        if value_tem is None:
            def L(wave):
                if i_obs is None:
                    return obs.getLine(elem_tem, spec_tem, wave).corrIntens
                else:
                    return obs.getLine(elem_tem, spec_tem, wave).corrIntens[i_obs]
            def I(i, j):
                wave = atom_tem.wave_Ang[i - 1, j - 1]
                if i_obs is None:
                    return obs.getLine(elem_tem, spec_tem, wave).corrIntens
                else:
                    return obs.getLine(elem_tem, spec_tem, wave).corrIntens[i_obs]
            def B(label, I=I, L=L):
                full_label = elem_tem + spec_tem + '_' + label
                if i_obs is None:
                    corrIntens = obs.getLine(label=full_label).corrIntens
                else:
                    corrIntens = obs.getLine(label=full_label).corrIntens[i_obs]
                return corrIntens
            #pn.log_.debug('to eval is {0}'.format(eval_tem), calling=calling + 'TEST')
            try:
                value_tem = eval(eval_tem)
                #pn.log_.debug('to eval = {0}'.format(value_tem), calling=calling + 'TEST')
            except:
                pn.log_.warn('No value for {0} {1}: {2} in obs'.format(elem_tem, spec_tem, diag_tem), calling=calling)
                return None
        else:
            if type(value_tem) == type([]): value_tem = np.asarray(value_tem)
        if value_den is None:
            def L(wave): 
                if i_obs is None:
                    return obs.getLine(elem_den, spec_den, wave).corrIntens
                else:
                    return obs.getLine(elem_den, spec_den, wave).corrIntens[i_obs]
            def I(i, j):
                wave = atom_den.wave_Ang[i - 1, j - 1]
                pn.log_.debug('wave is {0}'.format(wave), calling=calling + 'TEST3')
                if i_obs is None:
                    return obs.getLine(elem_den, spec_den, wave).corrIntens
                else:
                    return obs.getLine(elem_den, spec_den, wave).corrIntens[i_obs]
            def B(label, I=I, L=L):
                full_label = elem_den + spec_den + '_' + label
                if i_obs is None:
                    corrIntens = obs.getLine(label=full_label).corrIntens
                else:
                    corrIntens = obs.getLine(label=full_label).corrIntens[i_obs]
                return corrIntens
            #pn.log_.debug('to eval is {0}'.format(eval_den), calling=calling + ' TEST')
            try:
                value_den = eval(eval_den)
                #pn.log_.debug('to eval = {0}'.format(value_den), calling=calling + ' TEST1')
            except:
                pn.log_.warn('No value for {0} {1}: {2} in obs'.format(elem_den, spec_den, diag_den), calling=calling)
                return None
        else:
            if type(value_den) == type([]): value_den = np.asarray(value_den)
        den = atom_den.getTemDen(value_den, tem=guess_tem, to_eval=eval_den,
                                 maxError=maxError, start_x=start_den, end_x=end_den)
        
        tem = atom_tem.getTemDen(value_tem, den=den, to_eval=eval_tem,
                                 maxError=maxError, start_x=start_tem, end_x=end_tem)
#        self.log_.debug('tem: ' + str(tem) + ' den:' + str(den), calling='getCrossTemDen')
        no_conv = np.ones_like(den).astype(bool)
        n_tot = np.asarray(value_tem).size
        for i in np.arange(max_iter):
            if type(tem) == type(1.):
                tem_old = tem
            else:
                tem_old = tem.copy()
            if type(den) == type(1.):
                den_old = den
            else:
                den_old = den.copy()
            
            if n_tot > 1:
                den[no_conv] = atom_den.getTemDen(value_den[no_conv], tem=tem_old[no_conv],
                                                  to_eval=eval_den, start_x=start_den, end_x=end_den)
                tem[no_conv] = atom_tem.getTemDen(value_tem[no_conv], den=den_old[no_conv],
                                                  to_eval=eval_tem, start_x=start_tem, end_x=end_tem)
            else:
                den = atom_den.getTemDen(value_den, tem=tem_old, to_eval=eval_den, start_x=start_den, end_x=end_den)
                tem = atom_tem.getTemDen(value_tem, den=den_old, to_eval=eval_tem, start_x=start_tem, end_x=end_tem)
                
            no_conv = ((abs(den_old - den) / den * 100) > tol_den) | ((abs(tem_old - tem) / tem * 100) > tol_tem)
            if type(no_conv) == type(True):
                n_no_conv = int(no_conv)
            else:
                n_no_conv = no_conv.sum()
            
            pn.log_.message('{0} (max={1}): not converged {2} of {3}.'.format(i, max_iter, n_no_conv, n_tot),
                            calling=calling)
            if n_no_conv == 0:
                return tem, den
        if n_tot == 1:
            tem = np.nan
            den = np.nan
        else:
            tem[no_conv] = np.nan
            den[no_conv] = np.nan
        return tem, den
コード例 #9
0
ファイル: diags.py プロジェクト: kakirastern/PyNeb_devel
    def addDiag(self, label=None, diag_tuple=None, atom=None, wave_range=None):
        """
        Add diagnostics to the list of available diagnostics. It can either be one of the built-in diagnostics,
        a new, user-defined one, or a subset of the built-in diagnostics corresponding to a given atom or wavelength.
        
        Parameters:
            - label        a string or a list of strings describing the diagnostic, e.g. '[OIII] 4363/5007'. 
                           If it is not a key of diags_dict (a diagnostic define by PyNeb), diag_tuple must also be specified
            - diag_tuple   a 3 elements tuple containing:
                           + the atom, e.g. 'Ar5'
                           + the algebraic description of the diagnostic, in terms of line wavelengths or blends or levels, 
                             e.g. '(L(6435)+L(7006))/L(4626)'
                           + the algebraic description of the error, e.g. 
                             'RMS([E(6435)*L(6435)/(L(6435)+L(7006)), E(7006)*L(7006)/(L(6435)+L(7006)), E(4626)])'
            - atom         the selected atom, e.g. 'O3'
            - wave_range   the selected wavelength range
            
        Usage:
        diags.addDiag('[OIII] 4363/5007')
        diags.addDiag('[OIII] 5007/51m', ('O3', 'L(5007)/L(51800)', 'RMS([E(51800), E(5007)])'))
        diags.addDiag(atom='O3')
        diags.addDiag(wave_range=[4000, 6000])

        """
        if type(label) is list:
            for lab in label:
                self.addDiag(lab)
        elif label in self.diags:
            self.log_.warn('{0} already in diagnostic'.format(label), calling=self.calling)
        elif label in diags_dict:
            self.log_.message('Adding diag {0}'.format(label), calling=self.calling)
            self.diags[label] = diags_dict[label]
            atom = diags_dict[label][0]
        elif type(diag_tuple) is tuple:
            if len(diag_tuple) == 3:    
                self.diags[label] = diag_tuple
                atom = diag_tuple[0]
            else:
                self.log_.error('{0} is not in the list of diagnostics. The parameter diag_tuple must be a 3-elements tuple describing the diagnostic'.format(label), calling=self.calling + '.addDiag')
        elif atom is not None:
            for label in diags_dict:
                if diags_dict[label][0] == atom:
                    self.addDiag(label)
#                    if atom not in self.atomDict:
#                        self.atomDict[atom] = pn.Atom(parseAtom(atom)[0], parseAtom(atom)[1])
        elif wave_range is not None:
# To be done: add the possibility to use several independent ranges. The following bit does the trick, but
# only if all the wavelengths of a diagnostic are included in the same range
#            if type(wave_range[0]) is list:
#                for subrange in wave_range:
#                    self.addDiag(label, diag_tuple, atom, subrange)
                 
            for label in diags_dict:
                expr = diags_dict[label][1]
                waves = []
                wave = ''
                in_wave = False
                for i in expr:
                    if i.isdigit():
                        wave = wave + i
                        in_wave = True
                    elif (i == 'm'):
                        if in_wave == True:
                            waves.append(int(wave) * 10000.)
                            in_wave = False
                            wave = ''
                    else:
                        if in_wave == True:
                            waves.append(int(wave))
                            in_wave = False
                            wave = ''
                if (min(waves) > min(wave_range)) and (max(waves) < max(wave_range)):
                    self.addDiag(label)
        else:
            self.log_.error('Bad syntax. You must either give the label of an existing diagnostic, or the label and the tuple of a new one,' + 
                            'or an ion, or a wave range. label={0}, diag_tuple={1}, atom={2}, wave_range={3}'.format(label, diag_tuple, atom, wave_range),
                            calling=self.calling + '.addDiag')
        if atom not in self.atomDict and (type(label) is not list):
            self.atomDict[atom] = pn.Atom(parseAtom(atom)[0], parseAtom(atom)[1], NLevels=self.NLevels)
コード例 #10
0
    def __init__(self,
                 elem=None,
                 spec=None,
                 all_data=[],
                 atom=None,
                 n_tem_points=10000,
                 ref_tem=None,
                 OmegaInterp='linear',
                 NLevels=None):
        """
    Parameters:
        - elem         atomic elem 
        - spec       ionization stage in spectroscopic notation (I = 1, II = 2, etc.)
        - atom        e.g. 'O3'
        - all_data       dictionary of all_data to be compared (see above for format)
        - [n_tem_points] number of points in the fit (default=100; increase if fit is not smooth)
        - [ref_tem]      array of temperature values to be signaled in the plots
        - OmegaInterp    interpolating function between Omega values ('Cheb' [default], 'Linear')
    
    Example:
        dataplot = pn.DataPlot('O', 3) # initializes the plot
        dataplot.plotA() # transition probabilities plot 
        dataplot.plotRelA() # relative transition probabilities plot
        dataplot.plotOmega() # collision strength plot    

        
        """
        colors = np.array(['r', 'g', 'b', 'm', 'c', 'y'])
        self.calling = 'DataPlot'
        if atom is not None:
            self.atom = str.capitalize(atom)
            self.elem = parseAtom(self.atom)[0]
            self.spec = int(parseAtom(self.atom)[1])
        else:
            self.elem = str.capitalize(elem)
            self.spec = int(spec)
            self.atom = self.elem + str(self.spec)

        old_data = pn.atomicData.getDataFile(self.atom)
        # Check if matplotlib installed
        if not pn.config.INSTALLED['plt']:
            pn.log_.warn('Matplotlib not installed!', calling=self.calling)

        # Separate omega and A data sets
        atom_data = []
        coll_data = []
        if all_data == []:
            all_data = pn.atomicData.getAllAvailableFiles(self.atom,
                                                          mark_current=False)
        i_colors = 0
        for file_ in all_data:
            ID = file_.split('_')[-1]
            type_ = (file_.split('_')[2]).split('.')[0]
            if type_ in ['atom', 'coll']:
                data_list = type_ + '_data'
                vars()[data_list].append({
                    'ID':
                    ID,
                    'file_':
                    file_,
                    'type':
                    type_,
                    'color':
                    colors[i_colors % len(colors)]
                })
                i_colors += 1

        self.atom_data = atom_data
        self.coll_data = coll_data

        # Temperature values to be signaled by vertical lines in omega plots. Can be changed by defining ref_tem
        if ref_tem is None:
            self.ref_tem = np.log10([5000., 10000., 20000.])
        else:
            self.ref_tem = ref_tem

        # If it's not standard effective collision strengths, we don't want it
        coll_data_temp = []
        for item in self.coll_data:
            coll_data_temp.append(item)
        for data in coll_data_temp:
            pn.atomicData.setDataFile(data['file_'])
            atom = pn.Atom(self.elem,
                           self.spec,
                           OmegaInterp=OmegaInterp,
                           NLevels=NLevels)
            """
            try:
                if 'O_UNIT' in atom.CollData.comments.keys():
                    self.coll_data.remove(data)
            except:
                pass
            """
        # For each data set, an atom is built.
        del (atom)
        for data in self.atom_data + self.coll_data:
            pn.atomicData.setDataFile(data['file_'])
            atom = pn.Atom(self.elem,
                           self.spec,
                           OmegaInterp=OmegaInterp,
                           NLevels=NLevels)
            data['atom'] = atom
        for data in old_data:
            if data is not None:
                pn.atomicData.setDataFile(data)
        self.atom_rom = (self.elem + '_' +
                         int_to_roman(int(self.spec))).lower()
        self.n_tem_points = n_tem_points

        self.atom_n_max = 0
        for at in self.atom_data:
            if at['atom'].atomFileType != 'chianti':
                if at['atom'].atomNLevels > self.atom_n_max:
                    self.atom_n_max = at['atom'].atomNLevels
        self.coll_n_max = 0
        for at in self.coll_data:
            if at['atom'].collFileType != 'chianti':
                if at['atom'].collNLevels > self.coll_n_max:
                    self.coll_n_max = at['atom'].collNLevels
コード例 #11
0
def get_atoms_by_Z(extra_file=None, atoms=None):
    if atoms is None:
        atoms = get_atoms_by_name(extra_file=extra_file)
    return sorted(atoms, key=lambda k: Z[parseAtom(remove_iso(k))[0]])
コード例 #12
0
    """
    'phyat_list_DP_01.dat'
    """
    atoms = pn.atomicData.getAllAtoms()
    atoms.extend(get_extra_atoms(extra_file, uniq=True))
    atoms.remove('3He2')
    return sorted(atoms, key=get_atom_str)


def get_atoms_by_Z(extra_file=None, atoms=None):
    if atoms is None:
        atoms = get_atoms_by_name(extra_file=extra_file)
    return sorted(atoms, key=lambda k: Z[parseAtom(remove_iso(k))[0]])


ZmI = lambda atom: Z[parseAtom(remove_iso(atom))[0]] - int(
    parseAtom(remove_iso(atom))[1])


def get_atoms_by_ZmI(extra_file=None, atoms=None):
    atoms = get_atoms_by_Z(extra_file=extra_file, atoms=atoms)
    return sorted(atoms, key=ZmI)


def get_atoms_by_conf(extra_file=None, atoms=None):
    if atoms is None:
        atoms = get_atoms_by_ZmI(extra_file=extra_file)
    res = []
    gss = {}
    for atom in atoms:
        gss[remove_iso(atom)] = gsFromAtom(remove_iso(atom))
コード例 #13
0
ファイル: manage_phyat_list.py プロジェクト: Morisset/pySSN
def get_atoms_by_Z(extra_file=None, atoms=None):
    if atoms is None:
        atoms = get_atoms_by_name(extra_file=extra_file)
    return sorted(atoms, key=lambda k:Z[parseAtom(remove_iso(k))[0]])
コード例 #14
0
ファイル: manage_phyat_list.py プロジェクト: Morisset/pySSN
def get_atoms_by_name(extra_file=None):
    """
    'phyat_list_DP_01.dat'
    """
    atoms = pn.atomicData.getAllAtoms()
    atoms.extend(get_extra_atoms(extra_file, uniq=True))
    atoms.remove('3He2')
    return sorted(atoms, key=get_atom_str)

def get_atoms_by_Z(extra_file=None, atoms=None):
    if atoms is None:
        atoms = get_atoms_by_name(extra_file=extra_file)
    return sorted(atoms, key=lambda k:Z[parseAtom(remove_iso(k))[0]])

ZmI = lambda atom: Z[parseAtom(remove_iso(atom))[0]] - int(parseAtom(remove_iso(atom))[1])

def get_atoms_by_ZmI(extra_file=None, atoms=None):
    atoms = get_atoms_by_Z(extra_file=extra_file, atoms=atoms)
    return sorted(atoms, key=ZmI)
    
def get_atoms_by_conf(extra_file=None, atoms=None):
    if atoms is None:
        atoms = get_atoms_by_ZmI(extra_file=extra_file)
    res = []
    gss = {}
    for atom in atoms:
        gss[remove_iso(atom)] = gsFromAtom(remove_iso(atom))
    for gs in ('s1', 's2', 
               'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 
               'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'd10',