def read_gc(fname,varname,cat='IJ-AVG-$', gc_dir = '/short/m19/jaf574/GC.v11-01/runs.v11-02e/geosfp_025x03125_tropchem_au.base/', **kwargs): # Some species involve multiple GEOS-Chem species... varname_gc = gcname_to_names(varname) # Expand wildcard if necessary and link to directory if '*' in fname: fname = glob(gc_dir+fname) else: fname = [gc_dir + f for f in fname] # Put files in order! fname.sort() # Read using xbpch # one file if isinstance(fname,str): ds = open_bpchdataset(fname,categories=[cat,],fields=varname_gc, diaginfo_file=gc_dir+'diaginfo.dat', tracerinfo_file=gc_dir+'tracerinfo.dat',**kwargs) # multiple files else: ds = open_mfbpchdataset(fname,dask=True,categories=[cat,],fields=varname_gc, diaginfo_file=gc_dir+'diaginfo.dat', tracerinfo_file=gc_dir+'tracerinfo.dat',**kwargs) # load dataset ds.load() # extract variables cat=cat.replace('$','S').replace('-','_') dfg = ds[[cat+'_'+v for v in varname_gc]] # If needed, sum GEOS-Chem variables if len(varname_gc) > 1: dfg = sum_gc_vars(dfg, [cat+'_'+v for v in varname_gc], varname=cat+'_'+varname) return dfg
# Print month print('Processing month: {}'.format(mstr)) # File names (EDIT THESE ACCORDINGLY) bpch_file = join(bpchdir, 'trac_avg.GC_12.4.0.2016{}01'.format(mstr)) tinfo_file = join(bpchdir, 'tracerinfo.dat') dinfo_file = join(bpchdir, 'diaginfo.dat') ncfile = join(ncdir, 'GEOSChem.TagCOInputs.2016{}01.nc'.format(mstr)) # Open the bpch file and save it into an xarray Dataset object # NOTE: For best results, also specify the corresponding # tracerinfo.dat diaginfo.dat metadata files. try: ds = xb.open_bpchdataset(filename=bpch_file, tracerinfo_file=tinfo_file, diaginfo_file=dinfo_file) except FileNotFoundError: print('Could not find file {}'.format(bpch_file)) raise # ------------------------------------------------------------------ # Further manipulate the Dataset # ------------------------------------------------------------------ # Only extract the bpch fields we need for the tagged CO varlist = ['PORL_L_S_PCO', 'PORL_L_S_LCO', 'PORL_L_S_PCO_CH4', 'PORL_L_S_PCO_NMVO'] ds = ds[varlist] # Transpose the order of the xarray Dataset object read by
For a given field timeseries, compute seasonal averages over all data and plot each average on a four-panel figure. """ # Author: Daniel Rothenberg # Version: June 2, 2017 import matplotlib.pyplot as plt plt.style.use(['seaborn-talk', 'seaborn-ticks']) import xbpch # First we read in a sample dataset containing GEOS-Chem output ds = xbpch.open_bpchdataset( "/Users/daniel/workspace/bpch/test_data/ref_e2006_m2008.bpch", diaginfo_file="/Users/daniel/Desktop/sample_nd49/diaginfo.dat", tracerinfo_file="/Users/daniel/Desktop/sample_nd49/tracerinfo.dat", dask=True, memmap=True) # Compute seasonal averages by doing splitting along the "seasons" corresponding # to each timestep, and taking the average over time in that group seasonal_o3 = ( ds['IJ_AVG_S_O3'].isel(lev=0) # select just surface values .groupby('time.season').mean('time')) print(seasonal_o3) # Note that we now have a new dimension, "season", corresponding to the groups # we split the dataset by. We can use this dimension as a 'facet' to assemble # a collection of plots. # TODO: Cleanup axis proportions import cartopy.crs as ccrs
print("ERROR: Couldn't find the following input files:") for fn in bad_files: print(" " + fn) sys.exit(1) # Else, we should be good to read in and concatenate open_kws = { "tracerinfo_file": args.tracerinfo, "diaginfo_file": args.diaginfo, "memmap": True, "dask": True } print("\nReading in file(s)...") if len(args.bpch_files) == 1: ds = open_bpchdataset(args.bpch_files[0], **open_kws) else: ds = open_mfbpchdataset(args.bpch_files, **open_kws) # This block of code is hack to fix the encoding of attributes # on the DataArrays in this Dataset. They are being # set at a very low level when we read in the data, and manually # specifying the encoding doesn't work. # However, deleting them from the attributes dict # doesn't end up removing them from the final output file - they get # written just fine. print("\nDecoding variables...") for v in ds.data_vars: da = ds[v] da = _maybe_del_attr(da, 'scale_factor') da = _maybe_del_attr(da, 'units')
def bpch_2_netcdf( bpchfile, # Name of Bpch file tinfo_file, # tracerinfo.dat and diaginfo,dat fiels dinfo_file, ncfile, # Name of netCDF file interval=86400.0 * 31.0 ): # Number of seconds in the diagnostic interval (assume 1-month), does not matter for CO2 # ---------------------------------------------------------------------- # Open the bpch file and save it into an xarray Dataset object # NOTE: For best results, also specify the corresponding # tracerinfo.dat diaginfo.dat metadata files. # ---------------------------------------------------------------------- try: ds = xb.open_bpchdataset(filename=bpchfile, tracerinfo_file=tinfo_file, diaginfo_file=dinfo_file) except FileNotFoundError: print('Could not find file {}'.format(bpchfile)) raise # ---------------------------------------------------------------------- # Further manipulate the Dataset # ---------------------------------------------------------------------- # Transpose the order of the xarray Dataset object read by # xbpch so that its dimensions will be in the same order as # Dataset objects read from netCDF files. ds = ds.transpose() # Convert the bpch variable names to the same naming # convention as the netCDF ("History") diagnostics. ds = core.convert_bpch_names_to_netcdf_names(ds) # xbpch does not include a time dimension, so we'll add one here coords = ds.coords coords['time'] = 0.0 # ------------------------------------------------------------------ # Further edit variable attributes # ------------------------------------------------------------------ for v in ds.data_vars.keys(): # Append time to the data array ds[v] = xr.concat([ds[v]], 'time') # Add long_name attribute for COARDS netCDF compliance ds[v].attrs['long_name'] = ds[v].attrs['full_name'] # Remove some extraneous attributes that xbpch sets del ds[v].attrs['name'] del ds[v].attrs['full_name'] del ds[v].attrs['scale_factor'] del ds[v].attrs['hydrocarbon'] del ds[v].attrs['tracer'] del ds[v].attrs['category'] del ds[v].attrs['chemical'] del ds[v].attrs['original_shape'] del ds[v].attrs['origin'] del ds[v].attrs['number'] del ds[v].attrs['molwt'] del ds[v].attrs['C'] # Make the units attribute consistent with the units # attribute from the GEOS-Chem History diagnostics # NOTE: There probably is a more Pythonic way to code # this, but this will work for sure. if 'ug/m3' in ds[v].units: ds[v].attrs['units'] = 'ug m-3' if 'ug Celsius/m3' in ds[v].units: ds[v].attrs['units'] = 'ug C m-3' if 'count/cm3' in ds[v].units: ds[v].attrs['units'] = 'molec m-3' if 'cm/s' in ds[v].units: ds[v].attrs['units'] = 'cm s-1' if 'count/cm2/s' in ds[v].units: ds[v].attrs['units'] = 'molec cm-2 s-1' if 'kg/m2s' in ds[v].units: ds[v].attrs['units'] = 'kg m-2 s-1' if 'kg/m2/s' in ds[v].units: ds[v].attrs['units'] = 'kg m-2 s-1' if 'kg/s' in ds[v].units: ds[v].attrs['units'] = 'kg s-1' if 'W/m2' in ds[v].units: ds[v].attrs['units'] = 'W m-2' if 'm/s' in ds[v].units: ds[v].attrs['units'] = 'm s-1' if 'Pa/s' in ds[v].units: ds[v].attrs['units'] = 'Pa s-1' if 'g/kg' in ds[v].units: ds[v].attrs['units'] = 'g kg-1' if v.strip() == 'TotalOC': ds[v].attrs['units'] = 'ug m-3' if v.strip() in ['HO2concAfterChem']: ds[v].attrs['units'] = 'ppb' if v.strip() in [ 'O1DconcAfterChem', 'O3PconcAfterChem', 'OHconcAfterChem' ]: ds[v].attrs['units'] = 'molec cm-3' if v.strip() in [ 'Loss_CO', 'Prod_CO', 'Loss_Ox', 'Prod_Ox', 'Prod_SO4' ]: ds[v].attrs['units'] = 'molec/cm3/s' if v.strip() in 'Met_CLDTOPS': ds[v].attrs['units'] = 'level' if v.strip() in 'Met_PHIS': ds[v].attrs['units'] = 'm2 s-1' if v.strip() in ['Met_PRECCON', 'Met_PRECTOT']: ds[v].attrs['units'] = 'kg m-2 s-1' if v.strip() in 'Met_AVGW': ds[v].attrs['units'] = 'vol vol-1' if v.strip() in 'Met_AIRNUMDEN': ds[v].attrs['units'] = 'molec cm-3' if v.strip() in ['ProdCOfromCH4', 'ProdCOfromNMVOC']: ds[v].attrs['units'] = 'molec cm-3 s-1' # Convert these prodloss diagnostics from kg (bpch) to kg/s # to be consistent with the GEOS-Chem History diagnostics # NOTE: Assume a 1-month interval ( if v.strip() in [ 'ProdSO4fromH2O2inCloud', 'ProdSO4fromO3inCloud', 'ProdSO4fromO2inCloudMetal', 'ProdSO4fromO3inSeaSalt', 'ProdSO4fromHOBrInCloud', 'ProdSO4fromSRO3', 'ProdSO4fromSRHObr', 'ProdSO4fromO3s' ]: ds[v].attrs['units'] = 'kg S s-1' ds[v] = ds[v] / interval if v.strip() in ['LossHNO3onSeaSalt']: ds[v].attrs['units'] = 'kg s-1' ds[v] = ds[v] / interval # ------------------------------------------------------------------ # Edit attributes for coordinate dimensions # ------------------------------------------------------------------ # Time ds['time'].attrs['long_name'] = 'time' #ds['time'].attrs['units'] = \ # 'hours since {} 00:00:00.00 UTC'.format(datestr) ds['time'].attrs['calendar'] = 'standard' ds['time'].attrs['axis'] = 'T' # "lon", "lat", "lev" ds['lon'].attrs['axis'] = 'X' ds['lat'].attrs['axis'] = 'Y' ds['lev'].attrs['axis'] = 'Z' ds['lev'].attrs['units'] = 'level' # Global title ds.attrs['title'] = 'Created by bpch2nc.py' ds.attrs['conventions'] = 'COARDS' ds.attrs['references'] = 'www.geos-chem.org; wiki.geos-chem.org' # ------------------------------------------------------------------ # Create the netCDF file # ------------------------------------------------------------------ ds.to_netcdf(ncfile)
def read_bpch(path,keys): ''' Read generic bpch file into dictionary keys = keys you want to read ''' paths=path if __VERBOSE__: print('GC_fio.read_bpch called on paths:') print(path) multi=False if isinstance(path,list): path=path[0] if len(path) > 1: multi=True if '*' in path: multi=True # make sure coordinates are in keys list keys = list(set(keys + GC_coords)) # set removes any duplicates # assume tracerinfo and diaginfo in same folder: # otherwise use my generic one with coalesced info splt=path.split('/') splt[-1]='tracerinfo.dat' tracinf='/'.join(splt) if not os.path.isfile(tracinf): tracinf='Data/GC_Output/tracerinfo.dat' splt[-1]='diaginfo.dat' diaginf='/'.join(splt) if not os.path.isfile(diaginf): diaginf='Data/GC_Output/diaginfo.dat' # Improve read performance by only reading requested fields: fields=set(); categories=set() for key in keys: if '_' in key: # Split key on the underscores: Category_Field c,_,f = key.rpartition('_') categories.add(c) fields.add(f) else: fields.add(key) if __VERBOSE__: print("categories: ",categories) print("fields: ",fields) # get bpch file: data={} attrs={} bpchargs={'fields':list(fields), 'categories':list(categories), 'tracerinfo_file':tracinf,'diaginfo_file':diaginf, 'decode_cf':False,'dask':True} mod_times=[] if multi: ds=open_mfbpchdataset(paths,**bpchargs) for p in paths: mod_times.append(time.ctime(os.path.getmtime(p))) else: ds=open_bpchdataset(path,**bpchargs) mod_times=[time.ctime(os.path.getmtime(path))] data,attrs=dataset_to_dicts(ds,keys) data['modification_times']=np.array(mod_times) attrs['modification_times']={'desc':'When was file last modified'} return data,attrs
# lets check for a few dimensions: for dims in [ [10,1,1], [100,100,100]]: print("testing dummy trop column calculation for dims %s"%str(dims)) ppbv=np.zeros(dims)+100. # molec/1e9molecA N_air=np.zeros(dims)+1e13 # molecA/m3 boxH=np.zeros(dims)+1. # m tplev=np.zeros([dims[1],dims[2]]) + 4.4 # should give 100molec/cm2 per level : tropcol = 440molec/cm2 out= determine_trop_column(ppbv, N_air, boxH, tplev) assert out.shape==(dims[1],dims[2]), 'trop column calc shape is wrong' print("PASS: shape is ok from intputs to output") assert np.isclose(out[0,0],440.0), 'trop column calc value is wrong, out=%f'%out[0,0] print("PASS: Calculated trop column is OK") assert np.isclose(np.min(out), np.max(out)), 'Trop column calc has some problem' print("PASS: every box matches") if __name__=='__main__': #get_tropchem_data() _test_trop_column_calc() # How to check fields sat_fold='Data/GC_Output/geos5_2x25_tropchem/satellite_output/' sat_file=sat_fold+'ts_satellite_omi.20050101.bpch' tracinf=sat_fold+'tracerinfo.dat' diaginf=sat_fold+'diaginfo.dat' ds=open_bpchdataset(sat_file,tracerinfo_file=tracinf,diaginfo_file=diaginf) ds.keys()