예제 #1
0
 def __init__(self, args=None, log=None):
     if args is not None:
         super(Namespace, self).__init__(**args)
     if log is None:
         self._log = mu.Log(logname=None, verb=2, width=80)
     else:
         self._log = log
예제 #2
0
파일: tools.py 프로젝트: pcubillos/pyratbay
def log_error(log=None, error=None):
    """Capture exceptions into a log.error() call."""
    try:
        yield
    except Exception as e:
        if log is None:
            log = mu.Log(logname=None, verb=1, width=80)
        if error is None:
            error = str(e)
        log.error(error, tracklev=-4)
예제 #3
0
def abundance(pressure, temperature, species, elements=None,
        quniform=None, atmfile=None, punits='bar', xsolar=1.0,
        escale={}, solar_file=None, log=None, verb=1, ncpu=1):
    """
    Compute atmospheric abundaces for given pressure and
    temperature profiles with either uniform abundances or TEA.

    Parameters
    ----------
    pressure: 1D float ndarray
        Atmospheric pressure profile (barye).
    temperature: 1D float ndarray
        Atmospheric temperature profile (Kelvin).
    species: 1D string list
        Output atmospheric composition.
    elements: 1D strings list
        Input elemental composition (default to minimum list of elements
        required to form species).
    quniform: 1D float ndarray
        If not None, the output species abundances (isobaric).
    atmfile: String
        If not None, output file where to save the atmospheric model.
    punits: String
        Output pressure units.
    xsolar: Float
        Metallicity enhancement factor.
    escale: Dict
        Multiplication factor for specified atoms (dict's keys)
        by the respective values (on top of the xsolar scaling).
    solar_file: String
        Input solar elemental abundances file (Default Asplund et al. 2009).
    log: Log object
        Screen-output log handler.
    verb: Integer
        Verbosity level.
    ncpu: Integer
        Number of parallel CPUs to use in TEA calculation.

    Returns
    -------
    q: 2D float ndarray
       Atmospheric volume mixing fraction abundances of shape
       [nlayers, nspecies].

    Example
    -------
    >>> import pyratbay.atmosphere as pa
    >>> import pyratbay.constants  as pc
    >>> atmfile = "pbtea.atm"
    >>> nlayers = 100
    >>> press = np.logspace(-8, 3, nlayers) * pc.bar
    >>> temp  = 900+500/(1+np.exp(-(np.log10(press)+1.5)*1.5))
    >>> species = ['H2O', 'CH4', 'CO', 'CO2', 'NH3', 'C2H2', 'C2H4', 'HCN',
    >>>            'N2', 'H2', 'H', 'He']
    >>> # Automatically get 'elements' necessary from the list of species:
    >>> Q = pa.abundance(press, temp, species)
    """
    if solar_file is None:
        solar_file = pc.ROOT + "pyratbay/data/AsplundEtal2009.txt"
    if log is None:
        log = mu.Log(verb=verb)
    # Uniform-abundances profile:
    if quniform is not None:
        log.head("\nCompute uniform-abundances profile.")
        q = uniform(
            pressure, temperature, species, quniform, punits, log, atmfile)
        return q

    # TEA abundances:
    log.head("\nCompute TEA thermochemical-equilibrium abundances profile.")
    # Prep up files:
    atomic_file, patm = "PBatomicfile.tea", "PBpreatm.tea"
    make_atomic(xsolar, escale, atomic_file, solar_file)
    if elements is None:
       elements, dummy = stoich(species)
    specs = elements + list(np.setdiff1d(species, elements))
    make_preatm(
        pressure/pt.u(punits), temperature, atomic_file, elements, specs, patm)
    # Run TEA:
    pt.make_tea(abun_file=atomic_file, verb=verb, ncpu=ncpu)
    proc = subprocess.Popen([pc.ROOT+"pyratbay/TEA/tea/runatm.py", patm, "TEA"])
    proc.communicate()

    # Reformat the TEA output into the pyrat format:
    if atmfile is None:
        atmfile = 'TEA.tea'
    io.import_tea("TEA.tea", atmfile, species)
    q = io.read_atm(atmfile)[4]
    os.remove(atomic_file)
    os.remove(patm)
    os.remove("TEA.cfg")
    os.remove("TEA.tea")
    return q
예제 #4
0
def uniform(pressure, temperature, species, abundances, punits="bar",
            log=None, atmfile=None):
    """
    Generate an atmospheric file with uniform abundances.
    Save it into atmfile.

    Parameters
    ----------
    pressure: 1D float ndarray
        Monotonously decreasing pressure profile (in punits).
    temperature: 1D float ndarray
        Temperature profile for pressure layers (in Kelvin).
    species: 1D string ndarray
        List of atmospheric species.
    abundances: 1D float ndarray
        The species mole mixing ratio.
    punits:  String
       Pressure units.
    log: Log object
        Screen-output log handler.
    atmfile: String
        If not None, filename to save atmospheric model.

    Returns
    -------
    qprofiles: 2D Float ndarray
        Abundance profiles of shape [nlayers,nspecies]

    Examples
    --------
    >>> import pyratbay.atmosphere as pa
    >>> nlayers = 11
    >>> punits = 'bar'
    >>> pressure = pa.pressure(1e-8, 1e2, nlayers, punits)
    >>> tmodel = pa.tmodels.Isothermal(nlayers)
    >>> species    = ["H2", "He", "H2O", "CO", "CO2", "CH4"]
    >>> abundances = [0.8496, 0.15, 1e-4, 1e-4, 1e-8, 1e-4]
    >>> qprofiles = pa.uniform(pressure, tmodel(1500.0), species,
    >>>     abundances=abundances, punits=punits)
    >>> print(qprofiles)
    [[8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]
     [8.496e-01 1.500e-01 1.000e-04 1.000e-04 1.000e-08 1.000e-04]]
    """
    if log is None:
        log = mu.Log()

    nlayers = len(pressure)
    # Safety checks:
    if len(temperature) != nlayers:
        log.error(f"Pressure array length ({nlayers}) and temperature array "
            f"length ({len(temperature)}) don't match.")
    if len(species) != len(abundances):
        log.error(f"Species array length ({len(species)}) and abundances "
            f"array length ({len(abundances)}) don't match.")

    # Expand abundances to 2D array:
    qprofiles = np.tile(abundances, (nlayers,1))

    if atmfile is not None:
        header = ("# This is an atmospheric file with pressure, temperature,\n"
                  "# and uniform mole mixing ratio profiles.\n\n")
        io.write_atm(atmfile, pressure, temperature, species, qprofiles,
            punits=punits, header=header)

    return qprofiles
예제 #5
0
def parse(pyrat, cfile, no_logfile=False, mute=False):
    """
    Read the command line arguments.

    Parameters
    ----------
    cfile: String
        A Pyrat Bay configuration file.
    no_logfile: Bool
        If True, enforce not to write outputs to a log file
        (e.g., to prevent overwritting log of a previous run).
    mute: Bool
        If True, enforce verb to take a value of -1.
    """
    with pt.log_error():
        if not os.path.isfile(cfile):
            raise ValueError(f"Configuration file '{cfile}' does not exist.")
        config = configparser.ConfigParser()
        config.optionxform = str  # Enable case-sensitive variable names
        config.read([cfile])
        if "pyrat" not in config.sections():
            raise ValueError(
                f"\nInvalid configuration file: '{cfile}', no [pyrat] section."
            )
    args = dict(config.items("pyrat"))

    # Parse data type:
    with pt.log_error():
        parse_str(args, 'runmode')
        parse_int(args, 'ncpu')
        parse_int(args, 'verb')
        parse_array(args, 'dblist')
        parse_array(args, 'pflist')
        parse_array(args, 'dbtype')
        parse_array(args, 'tlifile')
        parse_array(args, 'csfile')
        parse_str(args, 'molfile')
        parse_array(args, 'extfile')
        # Spectrum sampling options:
        parse_str(args, 'wlunits')
        parse_str(args, 'wllow')
        parse_str(args, 'wlhigh')
        parse_float(args, 'wnlow')
        parse_float(args, 'wnhigh')
        parse_float(args, 'wnstep')
        parse_int(args, 'wnosamp')
        parse_float(args, 'resolution')
        # Atmospheric sampling options:
        parse_str(args, 'tmodel')
        parse_array(args, 'tpars')
        parse_str(args, 'radlow')
        parse_str(args, 'radhigh')
        parse_str(args, 'radstep')
        parse_str(args, 'runits')
        parse_str(args, 'punits')
        parse_int(args, 'nlayers')
        parse_str(args, 'ptop')
        parse_str(args, 'pbottom')
        parse_str(args, 'atmfile')
        parse_str(args, 'radmodel')
        # Variables for chemistry calculations
        parse_str(args, 'chemistry')
        parse_array(args, 'species')
        parse_array(args, 'uniform')
        parse_str(args, 'ptfile')
        parse_str(args, 'solar')
        parse_float(args, 'xsolar')
        parse_array(args, 'escale')
        parse_array(args, 'elements')
        # Extinction options:
        parse_float(args, 'tmin')
        parse_float(args, 'tmax')
        parse_float(args, 'tstep')
        parse_float(args, 'ethresh')
        # Voigt-profile options:
        parse_float(args, 'vextent')
        parse_float(args, 'vcutoff')
        parse_float(args, 'dmin')
        parse_float(args, 'dmax')
        parse_int(args, 'ndop')
        parse_float(args, 'lmin')
        parse_float(args, 'lmax')
        parse_int(args, 'nlor')
        parse_float(args, 'dlratio')
        # Hazes and clouds options:
        parse_array(args, 'clouds')
        parse_array(args, 'cpars')
        parse_array(args, 'rayleigh')
        parse_array(args, 'rpars')
        parse_float(args, 'fpatchy')
        parse_array(args, 'alkali')
        parse_float(args, 'alkali_cutoff')
        # Optical depth options:
        parse_str(args, 'rt_path')
        parse_float(args, 'maxdepth')
        parse_array(args, 'raygrid')
        parse_int(args, 'quadrature')
        # Data options:
        parse_str(args, 'dunits')
        parse_array(args, 'data')
        parse_array(args, 'uncert')
        parse_array(args, 'filters')
        # Abundances:
        parse_array(args, 'molmodel')
        parse_array(args, 'molfree')
        parse_array(args, 'molpars')
        parse_array(args, 'bulk')
        # Retrieval options:
        parse_str(args, 'mcmcfile')
        parse_array(args, 'retflag')
        parse_float(args, 'qcap')
        parse_array(args, 'params')
        parse_array(args, 'pstep')
        parse_float(args, 'tlow')
        parse_float(args, 'thigh')
        parse_array(args, 'pmin')
        parse_array(args, 'pmax')
        parse_array(args, 'prior')
        parse_array(args, 'priorlow')
        parse_array(args, 'priorup')
        parse_str(args, 'sampler')
        parse_int(args, 'nsamples')
        parse_int(args, 'nchains')
        parse_int(args, 'burnin')
        parse_int(args, 'thinning')
        parse_float(args, 'grbreak')
        parse_float(args, 'grnmin')
        parse_int(args, 'resume')  # False, action='store_true')
        # Stellar models:
        parse_str(args, 'starspec')
        parse_str(args, 'kurucz')
        parse_str(args, 'marcs')
        parse_str(args, 'phoenix')
        # System parameters:
        parse_str(args, 'rstar')
        parse_float(args, 'gstar')
        parse_float(args, 'tstar')
        parse_str(args, 'mstar')
        parse_str(args, 'rplanet')
        parse_str(args, 'refpressure')
        parse_str(args, 'mplanet')
        parse_str(args, 'mpunits')
        parse_float(args, 'gplanet')
        parse_str(args, 'smaxis')
        parse_float(args, 'tint')
        # Outputs:
        parse_str(args, 'specfile')
        parse_str(args, 'logfile')
        parse_array(args, 'logxticks')
        parse_array(args, 'yran')

    # Cast into a Namespace to make my life easier:
    args = Namespace(args)
    args.configfile = cfile

    if mute:
        args.verb = -1
    pyrat.verb = args.get_default('verb', 'Verbosity', 2, lt=5)
    runmode = pyrat.runmode = args.get_choice('runmode',
                                              'running mode',
                                              pc.rmodes,
                                              take_none=False)

    # Define logfile name and initialize log object:
    pyrat.lt.tlifile = args.get_path('tlifile', 'TLI')
    pyrat.atm.atmfile = args.get_path('atmfile', 'Atmospheric')
    pyrat.spec.specfile = args.get_path('specfile', 'Spectrum')
    pyrat.ex.extfile = args.get_path('extfile', 'Extinction-coefficient')
    pyrat.ret.mcmcfile = args.get_path('mcmcfile', 'MCMC')

    outfile_dict = {
        'tli': args.tlifile,
        'atmosphere': args.atmfile,
        'spectrum': args.specfile,
        'opacity': args.extfile,
        'mcmc': args.mcmcfile,
    }
    outfile = outfile_dict[args.runmode]
    if args.logfile is None and outfile is not None:
        if args.runmode in ['tli', 'opacity']:
            outfile = outfile[0]
        args.logfile = os.path.splitext(outfile)[0] + '.log'

    args.logfile = args.get_path('logfile', 'Log')

    # Override logfile if requested:
    if no_logfile:
        args.logfile = None

    args.logfile = pt.path(args.logfile)
    log = pyrat.log = mu.Log(logname=args.logfile,
                             verb=pyrat.verb,
                             width=80,
                             append=args.resume)
    args._log = log

    # Welcome message:
    log.head(
        f"{log.sep}\n"
        "  Python Radiative Transfer in a Bayesian framework (Pyrat Bay).\n"
        f"  Version {__version__}.\n"
        f"  Copyright (c) 2021-{date.today().year} Patricio Cubillos.\n"
        "  Pyrat Bay is open-source software under the GNU GPLv2 lincense "
        "(see LICENSE).\n"
        f"{log.sep}\n\n")

    log.head(f"Read command-line arguments from configuration file: '{cfile}'")

    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    # Parse valid inputs and defaults:
    pyrat.inputs = args

    phy = pyrat.phy
    spec = pyrat.spec
    atm = pyrat.atm

    pyrat.lt.dblist = args.get_path('dblist', 'Opacity database', exists=True)
    pyrat.mol.molfile = args.get_path('molfile', 'Molecular data', exists=True)
    pyrat.cs.files = args.get_path('csfile', 'Cross-section', exists=True)
    pyrat.atm.ptfile = args.get_path('ptfile', 'Pressure-temperature')

    spec.wlunits = args.get_default('wlunits', 'Wavelength units')
    if spec.wlunits is not None and not hasattr(pc, spec.wlunits):
        log.error(f'Invalid wavelength units (wlunits): {spec.wlunits}')
    spec.wllow = args.get_param('wllow',
                                spec.wlunits,
                                'Wavelength lower boundary',
                                gt=0.0)
    spec.wlhigh = args.get_param('wlhigh',
                                 spec.wlunits,
                                 'Wavelength higher boundary',
                                 gt=0.0)
    if spec.wlunits is None:
        spec.wlunits = args.get_units('wllow')

    spec.wnlow = args.get_default('wnlow', 'Wavenumber lower boundary', gt=0.0)
    spec.wnhigh = args.get_default('wnhigh',
                                   'Wavenumber higher boundary',
                                   gt=0.0)

    spec.wnstep = args.get_default('wnstep',
                                   'Wavenumber sampling step',
                                   gt=0.0)
    spec.wnosamp = args.get_default('wnosamp',
                                    'Wavenumber oversampling factor',
                                    ge=1)
    spec.resolution = args.get_default('resolution',
                                       'Spectral resolution',
                                       gt=0.0)

    atm.runits = args.get_default('runits', 'Planetary-radius units')
    if atm.runits is not None and not hasattr(pc, atm.runits):
        log.error(f'Invalid radius units (runits): {atm.runits}')
    phy.rplanet = args.get_param('rplanet',
                                 atm.runits,
                                 'Planetary radius',
                                 gt=0.0)
    if atm.runits is None:
        atm.runits = args.get_units('rplanet')

    atm.nlayers = args.get_default('nlayers',
                                   'Number of atmospheric layers',
                                   gt=1)

    atm.rmodelname = args.get_choice('radmodel', 'Radius-profile model',
                                     pc.radmodels)

    # Pressure inputs:
    atm.punits = args.get_default('punits', 'Pressure units')
    if atm.punits is not None and not hasattr(pc, atm.punits):
        log.error(f'Invalid pressure units (punits): {atm.punits}')

    atm.pbottom = args.get_param('pbottom',
                                 atm.punits,
                                 'Pressure at bottom of atmosphere',
                                 gt=0.0)
    atm.ptop = args.get_param('ptop',
                              atm.punits,
                              'Pressure at top of atmosphere',
                              gt=0.0)
    atm.refpressure = args.get_param('refpressure',
                                     atm.punits,
                                     'Planetary reference pressure level',
                                     gt=0.0)

    if atm.punits is None and atm.pbottom is not None:
        atm.punits = args.get_units('pbottom')
    elif atm.punits is None and atm.ptop is not None:
        atm.punits = args.get_units('ptop')
    elif atm.punits is None and atm.refpressure is not None:
        atm.punits = args.get_units('refpressure')
    # else, set atm.punits from atmospheric file in read_atm().

    # Radius boundaries:
    atm.radlow = args.get_param('radlow',
                                atm.runits,
                                'Radius at bottom of atmosphere',
                                ge=0.0)
    atm.radhigh = args.get_param('radhigh',
                                 atm.runits,
                                 'Radius at top of atmosphere',
                                 gt=0.0)
    atm.radstep = args.get_param('radstep',
                                 atm.runits,
                                 'Radius sampling step',
                                 gt=0.0)

    # Chemistry:
    atm.chemistry = args.get_choice('chemistry', 'Chemical model',
                                    pc.chemmodels)

    escale = args.get_default('escale', 'Elemental abundance scaling factors',
                              [])
    atm.escale = {
        atom: float(fscale)
        for atom, fscale in zip(escale[::2], escale[1::2])
    }

    # System physical parameters:
    phy.mpunits = args.get_default('mpunits', 'Planetary-mass units')
    if phy.mpunits is not None and not hasattr(pc, phy.mpunits):
        log.error(f'Invalid planet mass units (mpunits): {phy.mpunits}')
    phy.mplanet = args.get_param('mplanet',
                                 phy.mpunits,
                                 'Planetary mass',
                                 gt=0.0)
    if phy.mpunits is None:
        phy.mpunits = args.get_units('mplanet')

    phy.gplanet = args.get_default('gplanet',
                                   'Planetary surface gravity (cm s-2)',
                                   gt=0.0)
    phy.tint = args.get_default('tint',
                                'Planetary internal temperature',
                                100.0,
                                ge=0.0)

    phy.smaxis = args.get_param('smaxis',
                                None,
                                'Orbital semi-major axis',
                                gt=0.0)
    phy.rstar = args.get_param('rstar', None, 'Stellar radius', gt=0.0)
    phy.mstar = args.get_param('mstar', None, 'Stellar mass', gt=0.0)
    phy.gstar = args.get_default('gstar', 'Stellar surface gravity', gt=0.0)
    phy.tstar = args.get_default('tstar',
                                 'Stellar effective temperature (K)',
                                 gt=0.0)

    pyrat.voigt.extent = args.get_default('vextent',
                                          'Voigt profile extent in HWHM',
                                          100.0,
                                          ge=1.0)
    pyrat.voigt.cutoff = args.get_default('vcutoff',
                                          'Voigt profile cutoff in cm-1',
                                          25.0,
                                          ge=0.0)
    pyrat.voigt.ndop = args.get_default('ndop',
                                        'Number of Doppler-width samples',
                                        40,
                                        ge=1)
    pyrat.voigt.dmin = args.get_default('dmin',
                                        'Minimum Doppler HWHM (cm-1)',
                                        gt=0.0)
    pyrat.voigt.dmax = args.get_default('dmax',
                                        'Maximum Doppler HWHM (cm-1)',
                                        gt=0.0)
    pyrat.voigt.nlor = args.get_default('nlor',
                                        'Number of Lorentz-width samples',
                                        40,
                                        ge=1)
    pyrat.voigt.lmin = args.get_default('lmin',
                                        'Minimum Lorentz HWHM (cm-1)',
                                        gt=0.0)
    pyrat.voigt.lmax = args.get_default('lmax',
                                        'Maximum Lorentz HWHM (cm-1)',
                                        gt=0.0)
    pyrat.voigt.dlratio = args.get_default(
        'dlratio', 'Doppler/Lorentz-width ratio threshold', 0.1, gt=0.0)

    pyrat.ex.tmin = args.get_param('tmin',
                                   'kelvin',
                                   'Minimum temperature of opacity grid',
                                   gt=0.0)
    pyrat.ex.tmax = args.get_param('tmax',
                                   'kelvin',
                                   'Maximum temperature of opacity grid',
                                   gt=0.0)
    pyrat.ex.tstep = args.get_default(
        'tstep', "Opacity grid's temperature sampling step in K", gt=0.0)

    pyrat.rayleigh.pars = args.rpars
    pyrat.cloud.pars = args.cpars
    pyrat.rayleigh.model_names = args.get_choice('rayleigh', 'Rayleigh model',
                                                 pc.rmodels)
    pyrat.cloud.model_names = args.get_choice('clouds', 'cloud model',
                                              pc.cmodels)
    pyrat.alkali.model_names = args.get_choice('alkali', 'alkali model',
                                               pc.amodels)
    pyrat.alkali.cutoff = args.get_default(
        'alkali_cutoff',
        'Alkali profiles hard cutoff from line center (cm-1)',
        4500.0,
        gt=0.0)
    pyrat.cloud.fpatchy = args.get_default('fpatchy',
                                           'Patchy-cloud fraction',
                                           ge=0.0,
                                           le=1.0)

    pyrat.od.rt_path = args.get_choice(
        'rt_path', 'radiative-transfer observing geometry', pc.rt_paths)
    pyrat.spec._rt_path = pyrat.od.rt_path

    pyrat.ex.ethresh = args.get_default('ethresh',
                                        'Extinction-cofficient threshold',
                                        1e-15,
                                        gt=0.0)
    pyrat.od.maxdepth = args.get_default('maxdepth',
                                         'Maximum optical-depth',
                                         10.0,
                                         ge=0.0)

    phy.starspec = args.get_path('starspec', 'Stellar spectrum', exists=True)
    phy.kurucz = args.get_path('kurucz', 'Kurucz model', exists=True)
    phy.marcs = args.get_path('marcs', 'MARCS model', exists=True)
    phy.phoenix = args.get_path('phoenix', 'PHOENIX model', exists=True)

    spec.raygrid = args.get_default('raygrid', 'Emission raygrid (deg)',
                                    np.array([0, 20, 40, 60, 80.]))
    spec.quadrature = args.get_default('quadrature',
                                       'Number of Gaussian-quadrature points',
                                       ge=1)

    pyrat.obs.units = args.get_default('dunits',
                                       'Data units',
                                       'none',
                                       wflag=args.data is not None)
    if not hasattr(pc, pyrat.obs.units):
        log.error(f'Invalid data units (dunits): {pyrat.obs.units}')
    pyrat.obs.data = args.get_param('data', pyrat.obs.units, 'Data')
    pyrat.obs.uncert = args.get_param('uncert', pyrat.obs.units,
                                      'Uncertainties')
    pyrat.obs.filters = args.get_path('filters',
                                      'Filter pass-bands',
                                      exists=True)

    pyrat.ret.retflag = args.get_choice('retflag', 'retrieval flag',
                                        pc.retflags)
    if pyrat.ret.retflag is None:
        pyrat.ret.retflag = []

    pyrat.ret.params = args.params
    pyrat.ret.pstep = args.pstep
    pyrat.ret.pmin = args.pmin
    pyrat.ret.pmax = args.pmax
    pyrat.ret.prior = args.prior
    pyrat.ret.priorlow = args.priorlow
    pyrat.ret.priorup = args.priorup

    pyrat.ret.qcap = args.get_default('qcap',
                                      'Metals volume-mixing-ratio cap',
                                      1.0,
                                      gt=0.0,
                                      le=1.0)
    pyrat.ret.params = args.params
    pyrat.ret.pstep = args.pstep
    pyrat.ret.tlow = args.get_default('tlow',
                                      'Retrieval low-temperature (K) bound',
                                      0,
                                      wflag=(runmode == 'mcmc'))
    pyrat.ret.thigh = args.get_default('thigh',
                                       'Retrieval high-temperature (K) bound',
                                       np.inf,
                                       wflag=(runmode == 'mcmc'))
    pyrat.ret.sampler = args.sampler
    pyrat.ret.nsamples = args.get_default('nsamples',
                                          'Number of MCMC samples',
                                          gt=0)
    pyrat.ret.burnin = args.get_default('burnin',
                                        'Number of burn-in samples per chain',
                                        gt=0)
    pyrat.ret.thinning = args.get_default('thinning',
                                          'MCMC posterior thinning',
                                          1,
                                          ge=1)
    pyrat.ret.nchains = args.get_default('nchains',
                                         'Number of MCMC parallel chains',
                                         ge=1)
    pyrat.ret.grbreak = args.get_default('grbreak',
                                         'Gelman-Rubin convergence criteria',
                                         0.0,
                                         ge=0)
    pyrat.ret.grnmin = args.get_default('grnmin',
                                        'Gelman-Rubin convergence fraction',
                                        0.5,
                                        gt=0.0)

    atm.molmodel = args.get_choice('molmodel', 'molecular-abundance model',
                                   pc.molmodels)
    atm.molfree = args.molfree
    atm.molpars = args.molpars
    atm.bulk = args.bulk
    atm.tmodelname = args.get_choice('tmodel', 'temperature model', pc.tmodels)
    atm.tpars = args.tpars
    pyrat.ncpu = args.get_default('ncpu', 'Number of processors', 1, ge=1)

    return
예제 #6
0
def exomol(pf_files, outfile=None):
    """
    Extract ExoMol partition-function values from input files.
    If requested, write the partition-function into a file for use
    with Pyrat Bay.

    Parameters
    ----------
    pf_files: String or List of strings
        Input Exomol partition-function filenames.  If there are
        multiple isotopes, all of them must correspond to the same
        molecule.
    outfile: String
        If not None, save output to file.
        If outfile == 'default', save output to file named as
        PF_exomol_molecule.dat

    Returns
    -------
    pf: 2D float ndarray
        TIPS partition function for input molecule.
    isotopes: 1D string list
        List of isotopes.
    temp: 1D float ndarray
        Partition-function temperature samples (K).

    Examples
    --------
    >>> # First, download ExoMol data to current dictory, e.g.:
    >>> # wget http://www.exomol.com/db/NH3/14N-1H3/BYTe/14N-1H3__BYTe.pf
    >>> # wget http://www.exomol.com/db/NH3/15N-1H3/BYTe-15/15N-1H3__BYTe-15.pf
    >>> import pyratbay.opacity.partitions as pf
    >>> # A single file:
    >>> pf_data, isotopes, temp = pf.exomol('14N-1H3__BYTe.pf',
    >>>     outfile='default')
    Written partition-function file:
      'PF_exomol_NH3.dat'
    for molecule NH3, with isotopes ['4111'],
    and temperature range 1--1600 K.

    >>> # Multiple files (isotopes) for a molecule:
    >>> pf_data, isotopes, temp = pf.exomol(
    >>>     ['14N-1H3__BYTe.pf', '15N-1H3__BYTe-15.pf'], outfile='default')

    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
      Warning:
        Length of PF files do not match.  Trimming to shorter size.
    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    Written partition-function file:
      'PF_exomol_NH3.dat'
    for molecule NH3, with isotopes ['4111', '5111'],
    and temperature range 1--1600 K.
    """
    # Put into list if necessary:
    if isinstance(pf_files, str):
        pf_files = [pf_files]

    # Read and extract data from files:
    isotopes = []
    data, temps = [], []
    molecule = ''
    for pf_file in pf_files:
        # Get info from file name:
        mol, iso = pt.get_exomol_mol(pf_file)

        # Check all files correspond to the same molecule.
        if molecule == '':
            molecule = mol
        elif molecule != mol:
            raise ValueError('All files must correspond to the same molecule.')

        isotopes.append(iso)
        # Read data:
        temp, z = np.loadtxt(pf_file).T
        data.append(z)
        temps.append(temp)

    # Number of isotopes:
    niso = len(isotopes)
    # Check temp sampling:
    minlen = min(len(temp) for temp in temps)
    maxlen = max(len(temp) for temp in temps)
    ntemp = minlen
    if minlen != maxlen:
        for temp in temps:
            if np.any(temp[0:minlen] - temps[0][0:minlen] != 0):
                raise ValueError(
                    'Temperature sampling in PF files are not compatible.')
        with mu.Log() as log:
            log.warning('Length of PF files do not match.  Trimming to '
                        'shorter size.')

    pf = np.zeros((niso, ntemp), np.double)
    for i,z in enumerate(data):
        pf[i] = z[0:minlen]
    temp = temps[i][0:minlen]

    # Write output file:
    if outfile == 'default':
        outfile = f'PF_exomol_{molecule}.dat'

    if outfile is not None:
        header = (f'# This file incorporates the tabulated {molecule} '
                  'partition-function data\n# from Exomol\n\n')
        io.write_pf(outfile, pf, isotopes, temp, header)
        print("\nWritten partition-function file:\n  '{:s}'\nfor molecule "
              "{:s}, with isotopes {},\nand temperature range {:.0f}--{:.0f} "
              "K.".format(outfile, molecule, isotopes, temp[0], temp[-1]))
    return pf, isotopes, temp