| #!/usr/bin/env python |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| import cdms2 |
| import cdtime |
| import cmor |
| import sys |
| import getopt |
| import factory |
| import numpy |
| from factory.formats import import_equation |
| from Toolbox.ESGFresources import * |
| from Toolbox.ESGFexcel import * |
| from Toolbox.CMORresources import CMORTable |
| |
| |
| # ************************************************************************ |
| # process() |
| # |
| # Convert to obs4MIPS file format. |
| # ************************************************************************ |
| def process( rc ): |
| ''' |
| Convert netcdf/matlab/grads files into CMIP5 format. |
| ''' |
| pdb.set_trace() |
| # ---------------------------- |
| # Loop yearly on file list. |
| # ---------------------------- |
| file_template = rc['file_template'].split(","); |
| if( len(file_template) == 2 ): |
| template_parameter = file_template[1] |
| rc['file_template'] = file_template[0] |
| else: |
| template_parameter = 'years' |
| |
| for year in rc[template_parameter].split(","): |
| if(year == ''): |
| files= os.popen( "ls " + rc['file_template'] ).readlines() |
| else: |
| # ------------------------------------------------ |
| # Use string formating for path with same argument |
| # ------------------------------------------------ |
| try: |
| tmplFile = rc['file_template'] % (year) |
| except: |
| tmplFile = rc['file_template'].format(year) |
| if( not os.path.isfile( tmplFile) ) : |
| print "**** Warning %s not found\n" % ( tmplFile ) |
| continue |
| files= os.popen( "ls " + tmplFile).readlines() |
| |
| if( files == [] ): |
| print "No file found: Check your resource file" |
| return -1 |
| # ------------------------------------------------ |
| # Get the right handler to manage this file format |
| # ------------------------------------------------ |
| Handler = factory.HandlerFormats(files[0].strip()) |
| |
| # ----------------------------------- |
| # Take care of cmor initialization. |
| # ----------------------------------- |
| cmor.setup(inpath=rc['inpath'], |
| netcdf_file_action = cmor.CMOR_REPLACE) |
| |
| cmor.dataset(experiment_id = rc['experiment_id'], |
| institution = rc['institution' ], |
| calendar = rc['calendar' ], |
| institute_id = rc['institute_id' ], |
| model_id = rc['model_id' ], |
| source = rc['source' ], |
| contact = rc['contact' ], |
| references = rc['references' ]) |
| |
| |
| # ----------------------------------------- |
| # add extra Global Attributes for obs4MIPs. |
| # ----------------------------------------- |
| cmor.set_cur_dataset_attribute( 'instrument', rc['instrument' ]) |
| cmor.set_cur_dataset_attribute( 'mip_specs', rc['mip_specs' ]) |
| cmor.set_cur_dataset_attribute( 'data_structure', rc['data_structure']) |
| cmor.set_cur_dataset_attribute( 'source_type', rc['source_type' ]) |
| cmor.set_cur_dataset_attribute( 'source_id', rc['source_id' ]) |
| cmor.set_cur_dataset_attribute( 'realm', rc['realm' ]) |
| cmor.set_cur_dataset_attribute( 'obs_project', rc['obs_project' ]) |
| cmor.set_cur_dataset_attribute( 'processing_version', |
| rc['processing_version'] ) |
| cmor.set_cur_dataset_attribute( 'processing_level', |
| rc['processing_level'] ) |
| |
| cmor.load_table(rc['table']) |
| |
| # --------------------------------------------------------------------- |
| # We loop on each file found, a new cmor file will be create on each |
| # iteration. If you want to aggregate, you need to use Grads ctl file |
| # or NeCDF list of file. |
| # --------------------------------------------------------------------- |
| for file in files: |
| |
| fnm=file.strip() # Get rid of \n |
| aVariable = eval(rc['original_var']) |
| nbVariable = len(aVariable) |
| |
| # ----------------------------------------------------- |
| # ECMWF needs synoptic time 00z and 12z in he filename. |
| # We extract it from the first file in the list. |
| # ----------------------------------------------------- |
| if( rc['source_fn'] == 'SYNOPTIC' ): |
| index = fnm.find('z.') |
| rc['SYNOPTIC'] = fnm[index-2:index] |
| |
| # ----------------------- |
| # Work on all variables |
| # ------------------------- |
| for j in arange(nbVariable): |
| # ---------------------------------------------------- |
| # Fetch the variable directly or excute equation. |
| # ---------------------------------------------------- |
| try: |
| variable=aVariable[j] |
| Handler.open(fnm, variable=variable) |
| rc['cvrt_original_var'] = aVariable[j] |
| print "Working on variable %s " % variable |
| except: |
| if( aVariable[j] != 'equation' ) : |
| print "Variable %s can't open" % variable |
| continue |
| else: |
| print "Executing %s " % eval(rc['equation'])[j] |
| |
| # pdb.set_trace() |
| rc['cvrt_original_units'] = eval(rc['original_units'])[j] |
| rc['cvrt_cmor_var'] = eval(rc['cmor_var'])[j] |
| rc['cvrt_equation'] = eval(rc['equation'])[j] |
| rc['cvrt_level'] = eval(rc['level'])[j] |
| |
| data=Handler.getData() |
| |
| # ---------------------------------------------------------- |
| # Evaluate equation if needed. Usually used to change units |
| # ---------------------------------------------------------- |
| if( rc['cvrt_equation'][0] == '@' ): |
| fncName = rc['cvrt_equation'][1:] |
| fnc = import_equation( "equations.%s" % fncName ) |
| data[:]= fnc(Handler) |
| else: |
| data[:]=eval(rc['cvrt_equation']) |
| |
| # ------------------------------------------------------------- |
| # Save filled value in case data type is changed in createAxes |
| # ------------------------------------------------------------- |
| fill_value = data.fill_value |
| |
| # --------------------------------------------- |
| # Extract latitude/longitude |
| # --------------------------------------------- |
| lonvals=Handler.getLongitude() |
| latvals=Handler.getLatitude() |
| # --------------------- |
| # Create cmor time axis |
| # ---------------------- |
| (rel_time, rel_time_bnds) = createTime(Handler, rc) |
| |
| # --------------------------------------------------- |
| # Create cmor axes and add an axis to data if needed |
| # --------------------------------------------------- |
| (axes, data) = createAxes( rc, latvals, lonvals, data ) |
| |
| axis_ids = list() |
| for axis in axes: |
| axis_id = cmor.axis(**axis) |
| axis_ids.append(axis_id) |
| |
| # ---------------------------------------------------------- |
| # Create cmor variable |
| # Note: since this is in the loop, a new cmor file will be |
| # create for each cmor write command. |
| # ---------------------------------------------------------- |
| varid = cmor.variable(table_entry = rc['cvrt_cmor_var'], |
| axis_ids = axis_ids, |
| history = '', |
| missing_value = fill_value, |
| original_name = rc['cvrt_original_var'], |
| units = rc['cvrt_original_units'] |
| ) |
| # ------------------------------- |
| # Write data for this time frame. |
| # ------------------------------- |
| cmor.write(varid,data,\ |
| time_vals=rel_time,time_bnds=rel_time_bnds) |
| cmor.close(varid,file_name=True) |
| |
| # --------------------------------------- |
| # Rename cmor files according to project. |
| # --------------------------------------- |
| if( movefiles(rc) ): |
| return -2 |
| |
| cmor.close() |
| return 0 |
| |
| |
| |
| # ******************************************************************** |
| # |
| # createTime() |
| # |
| # Define Time and Time bound axes for cmor |
| # |
| # ******************************************************************** |
| def createTime(Handler, rc): |
| ''' |
| InputtimeUnits: specified from resource file or from first file |
| in a list of file. |
| |
| return relative time and time bounds using OutputTimeUnits from |
| resource file. |
| ''' |
| # ---------------------------------------------------- |
| # Retrieve time units from file if not provided in the |
| # resource file. |
| # ---------------------------------------------------- |
| InputTimeUnits = Handler.getTimeUnits(rc['InputTimeUnits']) |
| |
| # -------------------------------------------------------- |
| # Create time relative to January 1st 1900 to facilitate |
| # Threds software file handling. |
| # ------------------------------------------------------- |
| |
| cur_time = Handler.getTime(InputTimeUnits) |
| |
| rel_time =[cur_time[i].torel(rc['OutputTimeUnits']).value |
| for i in range(len(cur_time))] |
| |
| if( len(rel_time) == 1 ) : |
| deltarel = 1 |
| else: |
| deltarel = rel_time[2] - rel_time[1] |
| |
| rel_time_bnds = rel_time[:] |
| rel_time_bnds.append(rel_time[-1]+deltarel) |
| return rel_time, rel_time_bnds |
| |
| # ******************************************************************** |
| # |
| # getCMIP5lev() |
| # |
| # Extract CMIP5 mandatory level and recreate a new data array. |
| # They are 16 mandatory levels. |
| # |
| # ******************************************************************** |
| def getCMIP5lev(data,rc): |
| ''' |
| ''' |
| oTable = CMORTable(rc['inpath'], rc['table'], "plevs") |
| # ---------------------- |
| # Extract spefied levels |
| # ---------------------- |
| if( 'levels' in oTable.dico.keys() ): |
| #pdb.set_trace() |
| dataLevels = data.getLevel()[:] |
| if( data.getLevel().units == "millibars" or |
| data.getLevel().units == "hPa" or |
| data.getLevel().units == "mbar" ): |
| # -------------------------- |
| # Change units for to Pascal |
| # --------------------------- |
| LevelScaleFactor = 100 |
| dataLevels = data.getLevel()[:] * LevelScaleFactor |
| |
| # ---------------------------------------- |
| # No level selected, return all data array |
| # ---------------------------------------- |
| if( len(rc['cvrt_level'].split(":")) == 1 ): |
| levels = [ float(item) for item in dataLevels ] |
| lev=cdms2.createAxis( levels ) |
| lev.designateLevel() |
| lev.units="pa" |
| lev.long_name=data.getLevel().long_name |
| #lev.id="lev" |
| #lev=data.getAxis(1) |
| #lev.__setattr__('_data_',dataLevels.astype(float)) |
| #lev.__setattr__('units',"Pa") |
| #lev.units="hPa" |
| data2=data.pressureRegrid(lev) |
| return data2 |
| |
| if( rc['cvrt_level'].split(':')[1] == "CMIP5" ): |
| lev=cdms2.createAxis( [ float(item/LevelScaleFactor) |
| for item in dataLevels |
| if item in oTable.dico['levels' ] ] ) |
| |
| lev.designateLevel() |
| lev.units="pa" |
| lev.long_name = data.getLevel().long_name |
| data2=data.pressureRegrid(lev) |
| lev[:]=lev[:]*LevelScaleFactor |
| return data2 |
| else: |
| # ----------------------- |
| # Assume a list of values |
| # ----------------------- |
| levels = rc['cvrt_level'].split(':')[1].split(",") |
| # -------------------------- |
| # Change units to Pascal |
| # --------------------------- |
| dataLevels = [ float(rc['cvrt_level'].split(":")[1].split(",")[i]) * \ |
| LevelScaleFactor for i in range(len(levels)) ] |
| # ----------------------------------- |
| # Match dataLevels with CMIP5 levels |
| # Use file units |
| # ----------------------------------- |
| lev=cdms2.createAxis( [ float(item/LevelScaleFactor) |
| for item in dataLevels |
| if item in oTable.dico['levels' ] ] ) |
| # ----------------------------------- |
| # Set axis metadata |
| # ----------------------------------- |
| lev.units="pa" |
| lev.long_name = data.getLevel().long_name |
| lev.designateLevel() |
| # ----------------------------------- |
| # Extract specified levels |
| # ----------------------------------- |
| data2=data.pressureRegrid(lev) |
| # ----------------------------------- |
| # Scale data back |
| # ----------------------------------- |
| lev[:]=lev[:]*LevelScaleFactor |
| return data2 |
| |
| |
| |
| |
| return data |
| # ******************************************************************** |
| # |
| # createAxes() |
| # |
| # Define axes required by cmor and add z axis to data if needed |
| # |
| # ******************************************************************** |
| def createAxes(rc, latvals, lonvals, data): |
| # --------------------------------------------- |
| # Create time/lat/lon axes using a dictionary |
| # --------------------------------------------- |
| axes = [ |
| {'table_entry' : 'time', |
| 'units' : rc['OutputTimeUnits']}, |
| |
| {'table_entry' : 'latitude', |
| 'units' : 'degrees_north', |
| 'coord_vals' : latvals, |
| 'cell_bounds' : latvals.getBounds()}, |
| |
| {'table_entry' : 'longitude', |
| 'units' : 'degrees_east', |
| 'coord_vals' : lonvals, |
| 'cell_bounds' : lonvals.getBounds()}, |
| ] |
| |
| fill_value = data.fill_value |
| |
| if( rc['cvrt_level'] == 'height2m' ): |
| axes.append({'table_entry' : 'height2m', |
| 'units' : 'm', |
| 'coord_vals' : [2.0] }) |
| data = numpy.array(data[:]) |
| data = data[:,:,:,numpy.newaxis] |
| |
| elif( rc['cvrt_level'] != '' ): |
| data = getCMIP5lev( data, rc ) |
| levels=data.getLevel()[:] |
| axes = numpy.insert(axes, 1, |
| {'table_entry' : 'plevs', |
| 'units' : 'Pa', |
| 'coord_vals' : levels }) |
| |
| |
| return axes, data |
| |
| # ******************************************************************** |
| # |
| # usage() |
| # |
| # ******************************************************************** |
| def usage(message): |
| ''' |
| Describe program synopsis. |
| ''' |
| print |
| print "*************************" |
| print message |
| print "*************************" |
| print |
| print |
| print "obs4MIPS_process.py [-h] -r resource" |
| print " resource: File containing Global attributes" |
| print "" |
| print "obs4MIPS will convert an input data file into CMIP5 format using " |
| print "CMOR. A directory path will be creating using CMOR by default or " |
| print "using a template provided in the resource file." |
| print |
| |
| # ******************************************************************** |
| # |
| # main() |
| # |
| # ******************************************************************** |
| def main(): |
| ''' |
| ''' |
| pdb.set_trace() |
| try: |
| opts, args = getopt.getopt(sys.argv[1:], "hy:r:x:", |
| ["help" ,"year=","resource=","excel="]) |
| except getopt.GetoptError, err: |
| usage(str(err))# will print something like "option -a not recognized" |
| return(2) |
| |
| # -------------------------- |
| # Verify passed arguments |
| # -------------------------- |
| year = -1 |
| resource = None |
| excel = None |
| for o, a in opts: |
| if o in ("-r", "--resource"): |
| resource = a |
| elif o in ("-x", "--excel"): |
| excel = a |
| elif o in ("-h", "--help"): |
| usage() |
| return(0) |
| elif o in ("-y", "--year"): |
| yr = a |
| else: |
| assert False, "unhandled option" |
| |
| # ------------------------------ |
| # Does the resource file exist? |
| # ------------------------------ |
| if( ((resource == None ) or ( not os.path.isfile( resource ) )) and (( excel == None ) or ( not os.path.isfile( excel ) )) ): |
| usage("bad Input Resource/Excel File") |
| return 1 |
| |
| # ----------------------- |
| # Read in "rc" file |
| # ----------------------- |
| if( resource ): |
| rc = ESGFresources( resource ) |
| if( excel ): |
| rc = ESGFexcel( excel ) |
| |
| # -------------------------------- |
| # Extract CMIP5 Table information |
| # -------------------------------- |
| oTable = CMORTable(rc['inpath'], rc['table']) |
| if( not 'original_var' in rc.resources.keys() ): |
| sys.exit(-1) |
| rc['project_id'] = oTable[ 'project_id' ] |
| rc['product'] = oTable[ 'product' ] |
| rc['modeling_realm'] = oTable[ 'modeling_realm' ] |
| rc['frequency'] = oTable[ 'frequency' ] |
| if( process(rc) ): |
| return -1 |
| |
| return 0 |
| |
| # ******************************************************************** |
| # |
| # Call main program and return exit code |
| # |
| # ******************************************************************** |
| if __name__ == '__main__': |
| sys.exit(main()) |
| |
| |