示例#1
0
def waterFraction1StepProfiler(model_id,path_gastronoom,fraction,rfrac):

    '''
    Create a 1-step fractional profile for water.
    
    The original water abundance profile is taken from the output of the 
    original model without fractional abundances. 
    
    These fraction profiles can be used for CHANGE_ABUNDANCE_FRACTION in mline
    
    @param model_id: The model id of the original cooling model
    @type model_id: string
    @param path_gastronoom: The model subfolder in ~/GASTRoNOoM/
    @type path_gastronoom: string
    @param fraction: the fraction used
    @type fraction: float
    @param rfrac: the radius at the step to the fractional abundance [cm]
    @type rfrac: float
    
    '''
    
    rfrac = float(rfrac)
    fraction = float(fraction)
    filename = os.path.join(cc.path.gastronoom,path_gastronoom,'models',\
                            model_id,'coolfgr_all%s.dat'%model_id)
    rad = Gastronoom.getGastronoomOutput(filename=filename,keyword='RADIUS',\
                                         return_array=1)
    fraction_profile = np.ones(len(rad))
    step_index = np.argmin(abs(rad-rfrac))
    fraction_profile[step_index:] = fraction
    output_filename = os.path.join(cc.path.gastronoom,path_gastronoom,\
                                   'profiles',\
                                   'water_fractions_%s_%.2f_r%.3e.dat'\
                                   %(model_id,fraction,rfrac))
    DataIO.writeCols(output_filename,[rad,fraction_profile])
示例#2
0
def waterFraction1StepProfiler(model_id, path_gastronoom, fraction, rfrac):
    '''
    Create a 1-step fractional profile for water.
    
    The original water abundance profile is taken from the output of the 
    original model without fractional abundances. 
    
    These fraction profiles can be used for CHANGE_ABUNDANCE_FRACTION in mline
    
    @param model_id: The model id of the original cooling model
    @type model_id: string
    @param path_gastronoom: The model subfolder in ~/GASTRoNOoM/
    @type path_gastronoom: string
    @param fraction: the fraction used
    @type fraction: float
    @param rfrac: the radius at the step to the fractional abundance [cm]
    @type rfrac: float
    
    '''

    rfrac = float(rfrac)
    fraction = float(fraction)
    filename = os.path.join(cc.path.gastronoom,path_gastronoom,'models',\
                            model_id,'coolfgr_all%s.dat'%model_id)
    rad = Gastronoom.getGastronoomOutput(filename=filename,keyword='RADIUS',\
                                         return_array=1)
    fraction_profile = np.ones(len(rad))
    step_index = np.argmin(abs(rad - rfrac))
    fraction_profile[step_index:] = fraction
    output_filename = os.path.join(cc.path.gastronoom,path_gastronoom,\
                                   'profiles',\
                                   'water_fractions_%s_%.2f_r%.3e.dat'\
                                   %(model_id,fraction,rfrac))
    DataIO.writeCols(output_filename, [rad, fraction_profile])
示例#3
0
def combineRedLaw(ofn, chiar_curve='ism', power=-1.8):
    '''
    A method to combine the Fitzpatrick 2004 and Chiar & Tielens 2006 reddening
    laws as well as to extrapolate Chiar and Tielens 2006 to longer wavelengths.
    
    The result is saved in a file and used by the IvS repository as a valid 
    reddening law. 
    
    @param ofn: The output filename with path
    @type ofn: str
    
    @keyword chiar_curve: The curve type for Chiar & Tielens 2004. Either 'gc' 
                          or 'ism'.
                          
                          (default: 'ism')
    @type chiar_curve: str
    @keyword power: The power for the power law extrapolation. Default is taken
                    from Chiar and Tielens 2006, as a typical value for local
                    ISM between 2 and 5 micron. gc may require different value
                    but not very important.
                    
                    (default: -1.8)
    @type power: float

    '''

    chiar_curve = chiar_curve.lower()

    #-- Extract the two relevant extinction laws.
    xchiar, a_ak_chiar = red.get_law('chiar2006',norm='Ak',wave_units='micron',\
                                     curve=chiar_curve)
    xfitz, a_ak_fitz = red.get_law('fitzpatrick2004',norm='Ak',\
                                   wave_units='micron')

    #-- Define a power law for the extrapolation
    def power_law(x, scale, power):
        return scale * (x)**power

    #-- Determine the scaling factor from specific chiar/tielens law
    scale = a_ak_chiar[-1] / (xchiar[-1]**power)

    #-- Create an x grid for longer wavelengths.
    xlong = np.linspace(xchiar[-1] + 0.1, 1000, 1000)
    a_ak_long = power_law(xlong, scale, power)

    #-- Combine the three sections
    xcom = hstack([xfitz[xfitz < xchiar[0]], xchiar, xlong])
    a_ak_com = hstack([a_ak_fitz[xfitz < xchiar[0]], a_ak_chiar, a_ak_long])

    #-- Write the result to a file
    comments = '#-- wavelength (micron)   A_lambda/A_k\n'
    DataIO.writeCols(filename=ofn, cols=[[comments]])
    DataIO.writeCols(filename=ofn, cols=[xcom, a_ak_com], mode='a')
示例#4
0
def combineRedLaw(ofn, chiar_curve="ism", power=-1.8):

    """
    A method to combine the Fitzpatrick 2004 and Chiar & Tielens 2006 reddening
    laws as well as to extrapolate Chiar and Tielens 2006 to longer wavelengths.
    
    The result is saved in a file and used by the IvS repository as a valid 
    reddening law. 
    
    @param ofn: The output filename with path
    @type ofn: str
    
    @keyword chiar_curve: The curve type for Chiar & Tielens 2004. Either 'gc' 
                          or 'ism'.
                          
                          (default: 'ism')
    @type chiar_curve: str
    @keyword power: The power for the power law extrapolation. Default is taken
                    from Chiar and Tielens 2006, as a typical value for local
                    ISM between 2 and 5 micron. gc may require different value
                    but not very important.
                    
                    (default: -1.8)
    @type power: float

    """

    chiar_curve = chiar_curve.lower()

    # -- Extract the two relevant extinction laws.
    xchiar, a_ak_chiar = red.get_law("chiar2006", norm="Ak", wave_units="micron", curve=chiar_curve)
    xfitz, a_ak_fitz = red.get_law("fitzpatrick2004", norm="Ak", wave_units="micron")

    # -- Define a power law for the extrapolation
    def power_law(x, scale, power):
        return scale * (x) ** power

    # -- Determine the scaling factor from specific chiar/tielens law
    scale = a_ak_chiar[-1] / (xchiar[-1] ** power)

    # -- Create an x grid for longer wavelengths.
    xlong = np.linspace(xchiar[-1] + 0.1, 1000, 1000)
    a_ak_long = power_law(xlong, scale, power)

    # -- Combine the three sections
    xcom = hstack([xfitz[xfitz < xchiar[0]], xchiar, xlong])
    a_ak_com = hstack([a_ak_fitz[xfitz < xchiar[0]], a_ak_chiar, a_ak_long])

    # -- Write the result to a file
    comments = "#-- wavelength (micron)   A_lambda/A_k\n"
    DataIO.writeCols(filename=ofn, cols=[[comments]])
    DataIO.writeCols(filename=ofn, cols=[xcom, a_ak_com], mode="a")
示例#5
0
def mergeOpacity(species,lowres='nom_res',highres='high_res'):
    
    '''
    Merge high-res opacities into a grid of low-res opacities.
    
    The wavelength range of the inserted high res opacities is taken from the 
    given high res grid. 
        
    @param species: The dust species for which this is done. This is also the 
                    name of the folder in ~/MCMax/DustOpacities/ that contains
                    the data files.
    @type species: string
    @keyword lowres: The subfolder in ~/MCMax/DustOpacities/species containing
                     the low resolution datafiles.
                   
                     (default: low_res)
    @type lowres: string
    @keyword highres: The subfolder in ~/MCMax/DustOpacities/species containing
                      the high resolution datafiles.
                      
                      (default: high_res)
    @type highres: string
    
    '''
    
    path = os.path.join(cc.path.mopac,species)
    lowres_files = [f 
                    for f in glob(os.path.join(path,lowres,'*')) 
                    if f[-5:] == '.opac']
    highres_files = [f 
                     for f in glob(os.path.join(path,highres,'*')) 
                     if f[-5:] == '.opac']
    files = set([os.path.split(f)[1] for f in lowres_files] + \
                [os.path.split(f)[1] for f in highres_files])
    
    for f in files:
        hdfile = os.path.join(path,highres,f)
        ldfile = os.path.join(path,lowres,f)
        if os.path.isfile(ldfile) and os.path.isfile(hdfile):
            hd = DataIO.readCols(hdfile)
            ld = DataIO.readCols(ldfile)
            hdw = hd[0]
            ldw = ld[0]
            wmin = hdw[0]
            wmax = hdw[-1]
            ld_low = [list(col[ldw<wmin]) for col in ld]
            ld_high = [list(col[ldw>wmax]) for col in ld]
            hd = [list(col) for col in hd]
            merged = [ld_low[i] + hd[i] + ld_high[i] 
                      for i in range(len(hd))]
            DataIO.writeCols(filename=os.path.join(path,f),cols=merged)
示例#6
0
def mergeOpacity(species,lowres='nom_res',highres='high_res'):
    
    '''
    Merge high-res opacities into a grid of low-res opacities.
    
    The wavelength range of the inserted high res opacities is taken from the 
    given high res grid. 
        
    @param species: The dust species for which this is done. This is also the 
                    name of the folder in ~/MCMax/DustOpacities/ that contains
                    the data files.
    @type species: string
    @keyword lowres: The subfolder in ~/MCMax/DustOpacities/species containing
                     the low resolution datafiles.
                   
                     (default: low_res)
    @type lowres: string
    @keyword highres: The subfolder in ~/MCMax/DustOpacities/species containing
                      the high resolution datafiles.
                      
                      (default: high_res)
    @type highres: string
    
    '''
    
    path = os.path.join(cc.path.mopac,species)
    lowres_files = [f 
                    for f in glob(os.path.join(path,lowres,'*')) 
                    if f[-5:] == '.opac']
    highres_files = [f 
                     for f in glob(os.path.join(path,highres,'*')) 
                     if f[-5:] == '.opac']
    files = set([os.path.split(f)[1] for f in lowres_files] + \
                [os.path.split(f)[1] for f in highres_files])
    
    for f in files:
        hdfile = os.path.join(path,highres,f)
        ldfile = os.path.join(path,lowres,f)
        if os.path.isfile(ldfile) and os.path.isfile(hdfile):
            hd = DataIO.readCols(hdfile)
            ld = DataIO.readCols(ldfile)
            hdw = hd[0]
            ldw = ld[0]
            wmin = hdw[0]
            wmax = hdw[-1]
            ld_low = [list(col[ldw<wmin]) for col in ld]
            ld_high = [list(col[ldw>wmax]) for col in ld]
            hd = [list(col) for col in hd]
            merged = [ld_low[i] + hd[i] + ld_high[i] 
                      for i in range(len(hd))]
            DataIO.writeCols(filename=os.path.join(path,f),cols=merged)
示例#7
0
    def writeChi2(self,fn,sort=1,parameters=[]):
        
        '''
        Write the Chi^2 values to a file. Lists the model id in the first column
        with the chi^2 value in the second. 
        
        The chi^2 values can be requested to be sorted.
        
        Parameters from the Star() objects can be added as additional columns.
        Given parameters must be valid.
        
        @param fn: The output filename
        @type fn: str
        
        @keyword sort: Sort the star_grid according to the chi^2 values from 
                       lowest to highest. Requires calcChi2 to be ran first.
        
                       (default: 1)
        @type sort: bool
        @keyword parameters: The additional model parameters to be added as 
                             columns in the file. 
                             
                             (default: [])
        @type parameters: list(str)
        
        '''
        
        #-- If no chi^2 was calculated, do nothing
        if not self.chi2.size:
            return
            
        #-- Write the header
        comments = ['# '] + ['ID','RedChi^2'] + parameters + ['\n']
        DataIO.writeFile(filename=fn,input_lines=comments,delimiter='\t')
        
        #-- Define the columns
        cols = [[s['LAST_MCMAX_MODEL'] for s in self.getStarGrid(sort=sort)]]
        if sort: 
            isort = np.argsort(self.chi2) 
            cols.append(self.chi2[isort])
        else: 
            cols.append(self.chi2)
        
        #-- Add additional model parameters if requested
        for par in parameters:
            cols.append([s[par] for s in self.getStarGrid(sort=sort)])

        #-- Append the columns to the file after the header
        DataIO.writeCols(filename=fn,cols=cols,mode='a')
示例#8
0
def prepInput(star,path,repl_str=''):
    
    '''
    Prepare inputfiles for LIME from a GASTRoNOoM and MCMax model. 
    
    Input is taken from the model ouput and written into files. The output
    folder can be chosen. The model_id tag can be replaced with an arbitrary
    string.
    
    Input is converted to SI units, except opacities, which are in cm2/g.
    
    @param star: The model object including the GASTRoNOoM and MCMax model ids.
    @type star: Star()
    @param path: The target folder for the files.
    @type path: str
    
    @keyword repl_str: Replacement string for the model_id tag.
    
                       (default: '')
    @type repl_str: str
    
    '''
    
    #-- First opacities and t_dust, which are not part of the GASTRoNOoM output
    mcmid = star['LAST_MCMAX_MODEL']
    fnopac = os.path.join(path,'opac_%s.dat'%(repl_str and repl_str or mcmid))
    DataIO.writeCols(fnopac,star.getWeightedKappas())
    
    #-- Write dust temperature to a file.
    fntd = os.path.join(path,'td_%s.dat'%(repl_str and repl_str or mcmid))
    rad = star.getDustRad(unit='m')
    td = star.getDustTemperature()
    DataIO.writeCols(fntd,[rad,td])
    
    #-- Finally the gas properties
    if not repl_str: repl_str = star['LAST_GASTRONOOM_MODEL']
    
    #-- Radius for nh2 and vel
    rad = star.getGasRad(unit='m',ftype='fgr_all')
    if rad.size > 10000:
        icutoff = np.argmin(abs(rad-1e14))
        rad = Data.reduceArray(rad,20,1e14,'remove')
    else: 
        icutoff = None
    
    #-- h2 number density
    nh2 = star.getGasNumberDensity(ftype='fgr_all')
    nh2 = nh2*10**6
    if not icutoff is None: 
        nh2 = Data.reduceArray(nh2,20,nh2[icutoff],'remove')
    fnnh2 = os.path.join(path,'nh2_%s.dat'%repl_str)
    DataIO.writeCols(fnnh2,[rad,nh2])
    
    #-- Velocity profile
    vel = star.getGasVelocity(ftype='fgr_all')
    vel = vel*10**-2
    if not icutoff is None: 
        vel = Data.reduceArray(vel,20,vel[icutoff],'remove')
    fnvel = os.path.join(path,'vg_%s.dat'%repl_str)
    DataIO.writeCols(fnvel,[rad,vel])
    
    #-- CO abundance
    rad = star.getGasRad(ftype='1',mstr='12C16O',unit='m')
    nh2 = star.getGasNumberDensity(ftype='1',mstr='12C16O')
    nco = star.getGasNumberDensity(ftype='1',mstr='12C16O',molecule=1)
    aco = nco/nh2
    fnaco = os.path.join(path,'aco_%s.dat'%repl_str)
    DataIO.writeCols(fnaco,[rad,aco])

    
示例#9
0
   def __convolveSphinx(self,star):
       
       '''
       Check if sphinx output has already been convolved (pacs db) and do so 
       if not.
       
       @param star: The parameter set
       @type star: Star()
       
       '''     
       
       #- check for which filenames the convolution has already been done
       finished_conv_filenames = self.checkStarDb(star)
       finished_conv_filenames = [this_f 
                                  for this_f in finished_conv_filenames 
                                  if this_f in [os.path.split(f)[1] 
                                                for f in self.data_filenames]]
       
       #- if after db check pacs id is undefined, then there is no pacs model,
       #- and the convolution will be done anyway
       if self.redo_convolution and star['LAST_PACS_MODEL']:    
           for filename in finished_conv_filenames:
               ori = os.path.join(cc.path.gout,'stars',self.star_name,\
                                  'PACS_results',star['LAST_PACS_MODEL'],\
                                  '_'.join(['sphinx',filename]))
               backup = os.path.join(cc.path.gout,'stars',self.star_name,\
                                     'PACS_results',star['LAST_PACS_MODEL'],\
                                     '_'.join(['backup','sphinx',filename]))
               subprocess.call(['mv %s %s'%(ori,backup)],shell=True)
           self.db[star['LAST_PACS_MODEL']]['filenames'] = []
           finished_conv_filenames = []
       filenames_to_do = [this_f 
                          for this_f in [os.path.split(f)[1] 
                                         for f in self.data_filenames]
                          if this_f not in finished_conv_filenames]     
       
       #-Get sphinx model output and merge, for all star models in star_grid
       if filenames_to_do:        
           #- if list is not empty, some filenames still need a convolution    
           print '* Reading Sphinx model and merging.'
           merged = star['LAST_GASTRONOOM_MODEL'] \
                       and self.mergeSphinx(star) \
                       or [[],[]]
           sphinx_wave = merged[0]
           sphinx_flux = merged[1]
           if not sphinx_wave: 
               print '* No Sphinx data found.'
               return
 
           #- convolve the model fluxes with a gaussian at central wavelength 
           #- from data_wave_list for every star, and appropriate sigma
           print '* Convolving Sphinx model, after correction for v_lsr.'
           if not self.data_delta_list:
               self.setDataResolution()
           for filename in filenames_to_do:
               i_file = [os.path.split(f)[1] 
                         for f in self.data_filenames].index(filename)
               if not star['LAST_PACS_MODEL']:
                   star['LAST_PACS_MODEL'] = \
                       'pacs_%.4i-%.2i-%.2ih%.2i-%.2i-%.2i'\
                       %(gmtime()[0],gmtime()[1],gmtime()[2],\
                         gmtime()[3],gmtime()[4],gmtime()[5])
                   self.db[star['LAST_PACS_MODEL']] = \
                       dict([('filenames',[]),\
                             ('trans_list',star.getTransList(dtype='PACS')),\
                             ('cooling_id',star['LAST_GASTRONOOM_MODEL'])])
                   DataIO.testFolderExistence(\
                       os.path.join(cc.path.gout,'stars',self.star_name,\
                                    'PACS_results',star['LAST_PACS_MODEL']))
               #-- Correct for the v_lsr of the central source
               sphinx_wave_corr = array(sphinx_wave)*(1./(1-self.vlsr/self.c))
               sph_conv = Data.doConvolution(\
                                       x_in=sphinx_wave_corr,\
                                       y_in=sphinx_flux,\
                                       x_out=self.data_wave_list[i_file],\
                                       widths=self.data_delta_list[i_file],\
                                       oversampling=self.oversampling)
               sph_fn = os.path.join(cc.path.gout,'stars',self.star_name,\
                                     'PACS_results',star['LAST_PACS_MODEL'],\
                                     '_'.join(['sphinx',filename])) 
               DataIO.writeCols(filename=sph_fn,\
                                cols=[self.data_wave_list[i_file],sph_conv])
               self.db[star['LAST_PACS_MODEL']]['filenames'].append(filename)
               self.db.addChangedKey(star['LAST_PACS_MODEL'])        
示例#10
0
   def __convolveSphinx(self,star):
       
       '''
       Check if sphinx output has already been convolved (pacs db) and do so 
       if not.
       
       @param star: The parameter set
       @type star: Star()
       
       '''     
       
       #- check for which filenames the convolution has already been done
       finished_conv_filenames = self.checkStarDb(star)
       finished_conv_filenames = [this_f 
                                  for this_f in finished_conv_filenames 
                                  if this_f in [os.path.split(f)[1] 
                                                for f in self.data_filenames]]
       
       #- if after db check pacs id is undefined, then there is no pacs model,
       #- and the convolution will be done anyway
       if self.redo_convolution and star['LAST_PACS_MODEL']:    
           for filename in finished_conv_filenames:
               ori = os.path.join(cc.path.gout,'stars',self.star_name,\
                                  'PACS_results',star['LAST_PACS_MODEL'],\
                                  '_'.join(['sphinx',filename]))
               backup = os.path.join(cc.path.gout,'stars',self.star_name,\
                                     'PACS_results',star['LAST_PACS_MODEL'],\
                                     '_'.join(['backup','sphinx',filename]))
               subprocess.call(['mv %s %s'%(ori,backup)],shell=True)
           self.db[star['LAST_PACS_MODEL']]['filenames'] = []
           finished_conv_filenames = []
       filenames_to_do = [this_f 
                          for this_f in [os.path.split(f)[1] 
                                         for f in self.data_filenames]
                          if this_f not in finished_conv_filenames]     
       
       #-Get sphinx model output and merge, for all star models in star_grid
       if filenames_to_do:        
           #- if list is not empty, some filenames still need a convolution    
           print '* Reading Sphinx model and merging.'
           merged = star['LAST_GASTRONOOM_MODEL'] \
                       and self.mergeSphinx(star) \
                       or [[],[]]
           sphinx_wave = merged[0]
           sphinx_flux = merged[1]
           if not sphinx_wave: 
               print '* No Sphinx data found.'
               return
 
           #- convolve the model fluxes with a gaussian at central wavelength 
           #- from data_wave_list for every star, and appropriate sigma
           print '* Convolving Sphinx model, after correction for v_lsr.'
           if not self.data_delta_list:
               self.setDataResolution()
           for filename in filenames_to_do:
               i_file = [os.path.split(f)[1] 
                         for f in self.data_filenames].index(filename)
               if not star['LAST_PACS_MODEL']:
                   star['LAST_PACS_MODEL'] = \
                       'pacs_%.4i-%.2i-%.2ih%.2i-%.2i-%.2i'\
                       %(gmtime()[0],gmtime()[1],gmtime()[2],\
                         gmtime()[3],gmtime()[4],gmtime()[5])
                   self.db[star['LAST_PACS_MODEL']] = \
                       dict([('filenames',[]),\
                             ('trans_list',star.getTransList(dtype='PACS')),\
                             ('cooling_id',star['LAST_GASTRONOOM_MODEL'])])
                   DataIO.testFolderExistence(\
                       os.path.join(cc.path.gout,'stars',self.star_name,\
                                    'PACS_results',star['LAST_PACS_MODEL']))
               #-- Correct for the v_lsr of the central source
               sphinx_wave_corr = array(sphinx_wave)*(1./(1-self.vlsr/self.c))
               sph_conv = Data.doConvolution(\
                                       x_in=sphinx_wave_corr,\
                                       y_in=sphinx_flux,\
                                       x_out=self.data_wave_list[i_file],\
                                       widths=self.data_delta_list[i_file],\
                                       oversampling=self.oversampling)
               sph_fn = os.path.join(cc.path.gout,'stars',self.star_name,\
                                     'PACS_results',star['LAST_PACS_MODEL'],\
                                     '_'.join(['sphinx',filename])) 
               DataIO.writeCols(filename=sph_fn,\
                                cols=[self.data_wave_list[i_file],sph_conv])
               self.db[star['LAST_PACS_MODEL']]['filenames'].append(filename)
               self.db.addChangedKey(star['LAST_PACS_MODEL'])        
示例#11
0
def runEB_ALI(afn,ai=0,ei=0,iTmax=200,iter_conv=0,ALI_args=[],iterT_kwargs={},\
              runALIinit=0,iter_texguess=0.9,eb=None,*args,**kwargs):

    '''
    Run the energy balance module concurrently with the ALI RT code. 
    
    The method iterates between EB and ALI, updating the temperature profile
    and level populations between different iterations.
    
    ALI always starts first, so that the EB always has level populations to work
    with. Make sure the inputfile for ALI and for EB have the same initial T 
    profile.
    
    If more than one molecule is requested, ALI is ran for each molecule, then 
    EB takes into account all molecules in one go, after which the method goes
    back to ALI for one molecule at a time. 
    
    Automatically updates the ALI inputfile with filename for the temperature 
    input. Adds '_iter' to the ALI input filename
    and the level populations as well as temperature output files to help
    differentiate between the initial calculation and the iterations. Note that
    the .pop file is used by the EB, while ALI works with the .popiter file 
    (that doesn't change name at all). They should give the same populations 
    between iterations. The .popiter file is the one given in the ALI inputfile,
    while the .pop file is based on the inputfilename and is created at the end
    of an ALI run. The .pop filenames (one for every molecule) are the input for 
    EB's pop keyword and must be set by the user in either an EB inputfile or as
    keyword in kwargs.
    
    Input includes 
        - ALI input filename
        - Number of iterations to run in ALI and EB (0 if convergence is needed)
        - Less strict ALI convergence criterion for iterations 1 up to n-1
        - Additional arguments for the ALI execution
        - Additional keyword arguments for the temperature iteration (iterT)
        - Additional args/kwargs for the EnergyBalance object initialisation
    
    The EB input template is MCP by default, but a different template can be 
    passed to kwargs. Anything defined in the EB input template can be redefined
    in args and kwargs as well as by passing fn to this function call, which 
    points to the EB inputfile (not the template!).
    
    Note that ALI iterations do not share information, while the EB keeps track
    of all previous iterations. At the end of the method, the EB object is 
    returned in case you want to use it to check the different iterations after
    the calculation is done, including temperature, level populations, heating 
    and cooling terms, etc.
    
    In terms of initial populations, the user can specify whether to use 
    TexGuess = -1 or TexGuess = 0.9, i.e. start from the pops calculated in the 
    previous iteration (keep_pop on), or start from the standard initial 
    conditions.
    
    If you have an EnergyBalance object from a previous iteration or call to 
    this function, you can also pass this, and the method will continue with 
    that object instead. Make sure to adapt your iTmax and such to this object.
    
    @param afn: The inputfile name for ALI. One filename given as a string in 
                case of one molecule, multiple filenames given as strings in a 
                list in case of multiple molecules. 
    @type afn: str/list[str]
    
    @keyword ai: The amount of iterations to do in the ALI calculation. Set to 
                 the default of 0 if you wish to let ALI converge to whatever 
                 convergence criterion set in the ALI input OR by the iter_conv
                 keyword. This is not applicable to the first run of ALI.
                 
                 (default: 0)
    @type ai: int
    @keyword ei: The amount of iterations to do in the temperature calculation
                 during the energy balance. Set to the default of 0 if you wish
                 to let the energy balance converge to whatever convergence 
                 criterion is determined for/by iterT. This is not applicable to
                 the first run of EB (use imax in iterT_kwargs in that case). 
                 Cannot be 1. 
    
                 (default: 0)
    @type ei: int
    @keyword iTmax: The maximum total number of iterations allowed for the EB.
                    This is the sum of all iterations between ALI and EB, and so
                    is different from imax in EB.iterT! It is primarily used to
                    put an upper limit on the while loop of the ALI vs EB 
                    iteration.
                    
                    (default: 200)
    @type iTmax: int
    @keyword iter_conv: The convergence criterion to use in ALI during iteration
                        with the energy balance. Reverts to the requested value
                        in the ALI inputfile at the end of the iteration. If 
                        more strict than the criterion given in the ALI 
                        inputfile this keyword is ignored. Default in case the
                        same convergence criterion should be used as in the ALI
                        inputfile during the iteration.
                        
                        (default: 0)
    @type iter_conv: float 
    @keyword runALIinit: Run the initial iteration of ALI. In some cases, this 
                         model is already calculated, in which case the code 
                         starts from the existing files. 
                         
                         (default: 0)
    @type runALIinit: bool
    @keyword iter_texguess: The TexGuess value for the iterations (ie not the 
                            first calculation, in which case the value is given
                            in the inputfile). Typically this is 0.9 for 
                            standard initial conditions, but alternative could 
                            be -1 to start off from the populations calculated 
                            in the previous iteration.
                       
                            (default: 0.9)
    @type iter_texguess: float
    @keyword ALI_args: Additional arguments that are appended to the execute
                       command separated by spaces. If a single string is given,
                       only that string is added to the command. Default if no 
                       extra arguments are needed.
                       
                       (default: [])
    @type ALI_args: list[str]
    @keyword iterT_kwargs: Additional arguments for the energy balance 
                           temperature iteration. See method iterT in 
                           EnergyBalance for more information.
    
                           (default: {})
    @type iterT_kwargs: dict
    @keyword eb: An EnergyBalance object from a previous call. The method will
                 simply continue with this object rather than making a new one.
                 By default runALIinit will be off. 
                 
                 (default: None)
    @type eb: EnergyBalance()
    
    @return: The EnergyBalance object is returned with all its properties.
    @rtype: EnergyBalance()
    
    '''
    
    #-- 1) Calculate the zeroth ALI iteration for initial level populations
    #-- 2) Set up the EnergyBalance object
    #-- 3) Calculate the first EB iteration and create new input for ALI
    #-- 4) Run ALI with the new input. 
    #-- 5) Rinse and repeat until converged. 
    
    #-- Step 1: Calculate ALI given the pre-defined inputfile for all molecules
    if isinstance(afn,str): afn = [afn]
    if not eb is None: runALIinit = 0
    if runALIinit:
        print('--------------------------------------------')
        print('Running first guess for ALI.')
        print('--------------------------------------------')
        for ifn in afn: execALI(ifn,args=ALI_args)
    
    #-- Step 2: Set up the EnergyBalance object. 
    if eb is None:
        pars = {'template': 'mcp'}
        pars.update(kwargs)
        eb = EB.EnergyBalance(*args,**pars)
            
    #-- Remember the maximum requested iterations if it is present. Otherwise, 
    #   set it to the default in the EnergyBalance. 
    ei = int(ei)
    imax = iterT_kwargs['imax'] if iterT_kwargs.has_key('imax') else 50
    if eb is None:
        iterT_kwargs['imax'] = ei if int(ei) else imax
    else: 
        iterT_kwargs['imax'] = eb.i + ei if int(ei) else eb.i+imax
    
    #-- Step 3: Run the EnergyBalance
    m = 'next' if not eb is None else 'first'
    print('--------------------------------------------')
    print('Running {} guess of EnergyBalance (EB).'.format(m))
    print('--------------------------------------------')
    eb.iterT(**iterT_kwargs)
    
    #-- Prep step 4: Create new filenames for ALI input and Tkin
    fnT = afn[0].replace('.inp','_iter.temp')
    fn_new = [ifn.replace('.inp','_iter.inp') for ifn in afn]
    
    #-- Step 4: Iterate between steps 2 and 3 until the temperature iteration
    #           needs only 1 step to converge. Note that ei cannot be 1 for this
    #           to work.
    ei = 2 if int(ei) == 1 else int(ei)    
    i, iT, iter_texguess = 1, -2, float(iter_texguess)
    while eb.i != iT + 1 and eb.i <= iTmax:
        print('--------------------------------------------')
        print('Running iteration {} of ALI + EB. Current EB'.format(i) + \
              ' iteration index: {}.'.format(eb.i))
        print('--------------------------------------------')
        
        #-- Remember the current temperature iteration
        iT = eb.i
        
        #-- Step 2':
        #-- Update the ALI inputfiles and run. This overwrites the previous temp
        #   and pop files. You have access to both pops and temps of all 
        #   iterations through eb.pop[i] and eb.T_iter[i], respectively, with i  
        #   the iteration. Also sets the (maybe) new convergence criterion, new 
        #   Tkin/pop filename, and TexGuess to -1.
        DataIO.writeCols(filename=fnT,cols=[eb.r,eb.T.eval()])
        for ifn,ifn_new in zip(afn,fn_new):
            updateInputfile(fn=ifn,fnT=fnT,ai=ai,conv=iter_conv,\
                            texguess=iter_texguess,fn_new=ifn_new)
            execALI(ifn_new,args=ALI_args)
        
        #-- Step 3':
        #-- Update the level populations for all molecules. 
        for m,ifn_new in zip(eb.molecules,fn_new):
            eb.updatePop(m=m,fn=ifn_new.replace('.inp','.pop'))
        
        #-- Only if ei is not zero, limit the maximum amount of iterations.
        #   Otherwise set it to imax or the default of imax, in addition to the
        #   current iteration
        iterT_kwargs['imax'] = eb.i+ei if int(ei) else eb.i+imax
        eb.iterT(**iterT_kwargs)

        #-- Increase the EB+ALI iteration index.
        i += 1
    
    #-- If the temperature iteration reaches convergence, run the original ALI
    #   inputfile again, with updated T and pop files (ie with the original 
    #   convergence criterion and number of iterations.
    print('--------------------------------------------')
    print('Running final ALI iteration.')
    print('--------------------------------------------')
    DataIO.writeCols(filename=fnT,cols=[eb.r,eb.T.eval()])
    for ifn,ifn_new in zip(afn,fn_new):
        updateInputfile(fn=ifn,fnT=fnT,texguess=iter_texguess,fn_new=ifn_new)
        execALI(ifn_new,args=ALI_args)

    return eb