__all__ = ['bc', 'runcfg', 'exprlib', 'options', 'cmaq', 'report', 'models']
import os
import io
import pandas as pd
import PseudoNetCDF as pnc
import numpy as np
import warnings
import configparser
from .bcon import bc
from . import exprlib
from . import options
from . import report
from . import cmaq
from . import models
__doc__ = """
Code for making CMAQ-ready boundary condition files
Contents
========
bcon : module
module with functions for making boundary condition files
cmaq : module
module for making files CMAQ ready
report : module
module with convenience functions for reporting
defnpath : str
Path to all definition files available in examples.
runcfg : function
Function to run aqmbc from configuration files
Examples
========
Ex Python GEOS-Chem
-------------------
import PseudoNetCDF as pnc
import aqmbc
inpath = '/path/to/geoschem.nc'
bcpath = 'BCON.nc'
# using built in expressions
exprpaths = [f'{aqmbc.defnpath}/gc/gcnc_airmolden.expr',
f'{aqmbc.defnpath}/gc/gc14_to_cb6r5.expr']
metaf = pnc.pncopen('/path/to/METCRO3D_ANYDATE', format='ioapi')
bc(inpath, bcpath, metaf, vmethod='linear', exprpaths=exprpaths,
dimkeys=dict(TSTEP='time', LAY='lev', ROW='lat', COL='lon'),
format_kw='gcnc', history='just a test')
Ex Scripting
------------
python -m aqmbc -t runfolder
cd runfolder
# copy a cfg file as run.cfg
# edit run.cfg for your application.
python -m aqmbc run.cfg
Version History
===============
* 0.4.1: Update bc and cmaqready to apply a minimum value and allow inpaths
to be provided by a template. The minimum value (minvalue) can be
set in the common configuration section. Also, improved reporting
functions and automated reporting via the config file.
* 0.4.0: Change verbose to string for newer configparser compat
Update FILEDESC and HISTORY outputs to limit to 60*80 char
Added cmaq module with cmaqready to stack, interpolate, and
when necessary clean metadata.
Added some reporting tools, expression library and examples.
Needs better documentation on many objects.
"""
__version__ = '0.4.1'
defnpath = os.path.join(os.path.dirname(__file__), 'examples', 'definitions')
def loadcfg(cfgobjs, cfgtype='path'):
config = configparser.ConfigParser(
default_section='common',
interpolation=configparser.ExtendedInterpolation()
)
vglvlstxt = """[
1.0, 0.9975, 0.995, 0.99, 0.985, 0.98, 0.97, 0.96, 0.95, 0.94,
0.93, 0.92, 0.91, 0.9, 0.88, 0.86, 0.84, 0.82, 0.8, 0.77,
0.74, 0.7, 0.65, 0.6, 0.55, 0.5, 0.45, 0.4, 0.35, 0.3,
0.25, 0.2, 0.15, 0.1, 0.05, 0.0
]
"""
defopts = {
'common': {
'overwrite': False, 'verbose': '0', 'PWD': os.getcwd(),
'vgtop': '5000', 'vglvls': vglvlstxt, 'vinterp': 'linear',
'expressions': '[]', 'griddesc': 'GRIDDESC', 'minvalue': '1e-30'
},
'REPORT': {
'summaryspcs': '[]', 'vprofspcs': '[]', 'standardfigs': 'Y',
'summary': 'bcon_summary.csv', 'vprofmean': 'bcon_mean.nc4',
'vprofmin': 'bcon_min.nc4', 'vprofmax': 'bcon_max.nc4',
'debug': '0'
},
'BCON': {
'freq': 'd', 'output': 'BCON_%Y-%m-%d.nc',
},
'ICON': {
'output': 'ICON_%Y-%m-%d.nc',
}
}
if cfgtype == 'path':
defopts['common']['rcpath'] = os.path.dirname(
os.path.realpath(cfgobjs[0])
)
print('Default options:')
print(defopts)
config.read_dict(defopts)
if cfgtype == 'path':
config.read(cfgobjs)
elif cfgtype == 'file':
for cfgf in cfgobjs:
config.read_file(cfgf)
elif cfgtype == 'dict':
for cfgd in cfgobjs:
config.read_dict(cfgd)
else:
raise ValueError(
'cfgtype must be either path, file or dict; got {cfgtype}'
)
return config
[docs]def runcfg(
cfgobjs, cfgtype='path', warningfilter='ignore', dryrun=False, speedup=None
):
"""
Arguments
---------
cfgobjs : list
List of paths, files, or dictionaries
cfgtype : str
'path', 'file', or 'dict'
warningfilter : str
str accepted by warnings.simplefilter
dryrun : bool
If True, return config after testing parsing.
speedup : bool or None
If True, use more memory but run faster. If None, heursitcally decide.
"""
import json
warnings.simplefilter(warningfilter)
config = loadcfg(cfgobjs, cfgtype=cfgtype)
infmt = config.get('source', 'format')
infmt = json.loads(infmt)
intmpl = config.get('source', 'input')
dimkeys = config.get('source', 'dims')
dimkeys = json.loads(dimkeys)
ictmpl = config.get('ICON', 'output')
bctmpl = config.get('BCON', 'output')
overwrite = config.getboolean('common', 'overwrite')
interpopt = config.get('common', 'vinterp')
gdnam = config.get('common', 'gdnam')
minvalue = eval(config.get('common', 'minvalue'))
vgtop = config.getfloat('common', 'vgtop')
vgtop = np.float32(vgtop)
vglvls = json.loads(config.get('common', 'vglvls'))
vglvls = np.array(vglvls, dtype='f')
# config parser does not allow number-first strings, so they
# are explicitly quoted, which is interpreted as part of the string
freq = config.get('BCON', 'freq')
if freq.startswith('"') or freq.startswith("'"):
freq = freq[1:-1]
bdates = pd.date_range(
config.get('BCON', 'start_date'),
config.get('BCON', 'end_date'),
freq=freq
)
tslice = None
exprpaths = config.get('common', 'expressions')
exprpaths = json.loads(exprpaths)
if dryrun:
return config
bmetaf = pnc.pncopen(
config.get('common', 'GRIDDESC'),
format='griddesc', GDNAM=gdnam, FTYPE=2,
VGLVLS=vglvls, VGTOP=vgtop
)
for bdate in bdates:
inpath = bdate.strftime(intmpl)
outpath = bdate.strftime(bctmpl)
outdir = os.path.dirname(outpath)
os.makedirs(outdir, exist_ok=True)
opts = dict(
inpath=inpath, outpath=outpath, metaf=bmetaf,
tslice=tslice, vmethod=interpopt,
exprpaths=exprpaths, clobber=overwrite,
dimkeys=dimkeys, format_kw=infmt, speedup=speedup,
minvalue=minvalue
)
tmp = io.StringIO()
config.write(tmp)
tmp.seek(0, 0)
opts['history'] = tmp.read()
print(opts['history'])
bc(**opts)
imetaf = pnc.pncopen(
config.get('common', 'griddesc'),
format='griddesc', GDNAM=gdnam, FTYPE=1,
VGLVLS=vglvls, VGTOP=vgtop
)
idates = pd.to_datetime(json.loads(config.get('ICON', 'dates')))
tslice = 0
for idate in idates:
inpath = idate.strftime(intmpl)
outpath = idate.strftime(ictmpl)
outdir = os.path.dirname(outpath)
os.makedirs(outdir, exist_ok=True)
opts = dict(
inpath=inpath, outpath=outpath, metaf=imetaf,
tslice=tslice, vmethod=interpopt,
exprpaths=exprpaths, clobber=overwrite,
dimkeys=dimkeys, format_kw=infmt, speedup=speedup
)
tmp = io.StringIO()
config.write(tmp)
tmp.seek(0, 0)
opts['history'] = tmp.read()
bc(**opts)