Changeset 347 for nappy/trunk/cdms2na.py


Ignore:
Timestamp:
05/11/04 12:57:38 (15 years ago)
Author:
selatham
Message:

Incorporates changes by selatham and Ag Stephens. as of 5/11/04.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • nappy/trunk/cdms2na.py

    r343 r347  
    1111===== 
    1212 
    13     cdms2na.py <infilename> <outfilename> [<options>] 
     13    cdms2na.py [<options>] -i <infilename> -o <outfilename>  
    1414 
    1515Where 
     
    1818    infilename  - name of input file (NetCDF). 
    1919    outfilename - name of output file (NASA Ames). 
    20     options     - list of options [Not yet implemented]. 
     20    options     - list of options [NOT YET IMPLEMENTED]. 
    2121     
    2222""" 
    2323 
    2424# Imports from python standard library 
    25 import sys, os, time, string, fileinput 
     25import sys, os, time, string, fileinput, re 
    2626sys.path.append("..") 
    2727 
     
    3838import version 
    3939import general 
     40import localRules 
    4041from naCore import * 
    4142from naError import * 
     
    106107 
    107108def getBestName(var):   
    108     """Returns the most appropriate variable name for a NASA Ames header""" 
     109    """ 
     110    Returns the most appropriate variable name for a NASA Ames header 
     111    """ 
    109112    if hasattr(var, "id"): name=var.id 
    110113    if hasattr(var, "name"): name=var.name 
    111114    if hasattr(var, "long_name"): name=var.long_name 
    112115    if hasattr(var, "standard_name"): name=var.standard_name 
    113     if hasattr(var, "units"): name="%s (%s)" % (name, var.units) 
     116    if hasattr(var, "units") and not re.match("^\s+$", var.units): name="%s (%s)" % (name, var.units)     
     117    # Do a check to see units are not duplicated 
     118    match=re.match("(.*\(%s\)\s*)\(%s\)(.*)$" % (var.units, var.units), name) 
     119    if match: 
     120        name=match.groups()[0]+match.groups()[1] 
     121         
     122    if name[-2:]=="()": name=name[:-2] 
    114123    return name 
    115124 
     
    126135    for line in lines: 
    127136        count=count+1 
    128         if line=="***Data Section begins on the next line***\n": 
     137        if line=="###Data Section begins on the next line###\n": 
    129138            break 
    130139    # Now replace 1st line NLHEAD with count 
     
    141150 
    142151def cdms2na(infilename, outfilename, variable=None, ffi="automatic", rules=None): 
     152    print "Reading data from: %s" % infilename 
    143153    cdmsfile=cdms.open(infilename) 
    144154    globals=cdmsfile.attributes 
     
    149159    else: 
    150160        vars=[cdmsfile(variable)] 
     161 
     162    # Re-order variables if they have the attribute 'nasa_ames_var_number' 
     163    orderedVars=[None]*1000 
     164    otherVars=[] 
     165    for var in vars: 
     166        varMetadata=cdmsfile[var] 
     167        if hasattr(varMetadata, "nasa_ames_var_number"): 
     168            num=varMetadata.nasa_ames_var_number 
     169            orderedVars[num]=var 
     170        else: 
     171            otherVars.append(var) 
     172     
     173    vars=[] 
     174    for var in orderedVars: 
     175        if var!=None: 
     176            vars.append(var) 
     177             
     178    vars=vars+otherVars 
     179     
    151180    builder=CdmsToNABuilder(vars, globals) 
    152181    #print builder.naDict["X"] 
     182     
     183    print "\nWriting output NASA Ames file: %s" % outfilename 
    153184    general.openNAFile(outfilename, 'w', builder.naDict) 
    154185    nlhead=fixHeaderLength(outfilename)    
    155     print "%s written successfully." % outfilename 
     186    print "\nNASA Ames file written successfully: %s" % outfilename 
    156187 
    157188 
    158189class CdmsToNABuilder(NACore): 
    159  
     190    """ 
     191    Class to build a NASA Ames File object from a set of  
     192    CDMS variables and global attributes (optional). 
     193    """ 
     194     
    160195    def __init__(self, vars, global_attributes={}): 
     196        """ 
     197        Sets up instance variables and calls appropriate methods to 
     198        generate sections of NASA Ames file object. 
     199        """ 
    161200        self.naDict={} 
    162201        self.vars=vars 
    163202        self.globals=global_attributes 
    164         (vars, auxVars)=self.analyseVariables() 
     203        (self.orderedVars, auxVars)=self.analyseVariables() 
    165204        self.naDict["NLHEAD"]="-999" 
    166         self.defineNAVars(vars) 
     205        self.defineNAVars(self.orderedVars) 
    167206        self.defineNAAuxVars(auxVars) 
    168207        self.defineNAGlobals() 
     
    173212 
    174213    def analyseVariables(self): 
    175         vars4NA=[self.vars[0]] 
     214        """ 
     215        Method to examine the content of CDMS variables to return 
     216        a tuple of two lists containing variables and auxiliary variables 
     217        for the NASA Ames file object. 
     218        """ 
     219        # Get largest ranked variable as the one we use as standard 
     220        rank=0 
     221        count=0 
     222        for var in self.vars: 
     223            count=count+1 
     224            if var.rank()>rank: 
     225                rank=var.rank() 
     226                bestVar=var 
     227                bestVarIndex=count 
     228            elif var.rank()==rank: 
     229                if len(var.flat)>len(bestVar.flat): 
     230                    bestVar=var 
     231                    bestVarIndex=count 
     232                 
     233        vars4NA=[bestVar] 
    176234        auxVars4NA=[] 
    177         shape=self.vars[0].shape 
     235        shape=bestVar.shape 
    178236        ndims=len(shape) 
    179237        self.naDict["NIV"]=ndims 
     
    189247            else: 
    190248                self.naDict["FFI"]=1001 
    191  
    192         axes=self.vars[0].getAxisList() 
     249        print self.naDict["FFI"] 
     250        axes=bestVar.getAxisList() 
    193251         
    194252        # Get other variable info 
    195         for var in self.vars[1:]: 
     253        for var in self.vars[:bestVarIndex]+self.vars[bestVarIndex:]: 
    196254            if len(var.shape)!=ndims or var.shape!=shape:  
    197255                # Could it be an auxiliary variable  
     
    208266                # OK, I think they are compatible 
    209267                vars4NA.append(var) 
    210  
     268                 
     269        # Re-order if they previously came from NASA Ames files (i.e. including  
     270        # the attribute 'nasa_ames_var_number') 
     271        orderedVars=[None]*1000 
     272        otherVars=[] 
     273        for var in vars4NA: 
     274            if hasattr(var, "nasa_ames_var_number"): 
     275                orderedVars[var.nasa_ames_var_number[0]]=var 
     276            else: 
     277                otherVars.append(var) 
     278        # Remake vars4NA now in order 
     279        vars4NA=[] 
     280        for var in orderedVars: 
     281            if var!=None: vars4NA.append(var) 
     282        vars4NA=vars4NA+otherVars 
     283 
     284        # Now re-order the Auxiliary variables if they previously came from NASA  
     285        # Ames files (i.e. including the attribute 'nasa_ames_aux_var_number') 
     286 
     287        orderedAuxVars=[None]*1000 
     288        otherAuxVars=[] 
     289        for var in auxVars4NA: 
     290            if hasattr(var, "nasa_ames_aux_var_number"): 
     291                orderedAuxVars[var.nasa_ames_aux_var_number[0]]=var 
     292            else: 
     293                otherAuxVars.append(var) 
     294        # Remake auxVars4NA now in order 
     295        auxVars4NA=[] 
     296        for var in orderedAuxVars: 
     297            if var!=None: auxVars4NA.append(var) 
     298        auxVars4NA=auxVars4NA+otherAuxVars       
    211299        return (vars4NA, auxVars4NA) 
    212300 
    213301    def defineNAVars(self, vars): 
     302        """ 
     303        Method to define NASA Ames file object variables and their 
     304        associated metadata. 
     305        """ 
    214306        self.naDict["NV"]=len(vars) 
    215307        self.naDict["VNAME"]=[] 
     
    255347 
    256348    def defineNAAuxVars(self, auxVars): 
     349        """ 
     350        Method to define NASA Ames file object auxiliary variables and their 
     351        associated metadata. 
     352        """ 
    257353        self.naDict["NAUXV"]=len(auxVars) 
    258354        self.naDict["ANAME"]=[] 
     
    265361            miss=getMissingValue(var) 
    266362            if type(miss)!=float:  miss=miss[0] 
    267             self.naDict["SMISS"].append(miss) 
     363            self.naDict["AMISS"].append(miss) 
    268364            self.naDict["ASCAL"].append(1) 
    269365            # AND THE ARRAY 
     
    276372 
    277373    def getAxisDefinition(self, axis): 
     374        """ 
     375        Method to create the appropriate NASA Ames file object  
     376        items associated with an axis (independent variable in  
     377        NASA Ames). 
     378        """ 
    278379        self.naDict["NX"].append(len(axis)) 
    279380        self.naDict["XNAME"].append(getBestName(axis)) 
    280381        # If only one item in axis values 
    281         if len(axis)==1: 
     382        if len(axis)<3: 
    282383            self.naDict["DX"].append(0) 
    283384            self.naDict["NXDEF"].append(len(axis)) 
     
    299400 
    300401    def defineNAGlobals(self): 
    301         globalmap=cdmsMap.cdmsMap["toNA"] 
    302         self.extra_comments=[] 
     402        """ 
     403        Maps CDMS (NetCDF) global attributes into NASA Ames Header fields. 
     404        """ 
     405        # Get the global mapping dictionary 
     406        globalmap=cdmsMap.toNA 
     407        # Check if we should add to it with locally set rules 
     408        locGlobs=localRules.localGlobalAttributes 
     409        for att in locGlobs.keys(): 
     410            if not globalmap.has_key(att): 
     411                globalmap[key]=locGlobs[key] 
     412 
     413        self.extra_comments=[[],[],[]]  # Normal comments, special comments, other comments 
    303414        for key in self.globals.keys(): 
    304             if type(self.globals[key]) not in (str, float, int): continue 
     415            if key!="first_valid_date_of_data" and type(self.globals[key]) not in (str, float, int): continue 
    305416            if key in globalmap.keys(): 
    306417                if key=="history": 
     
    310421                     
    311422                elif key=="institution": 
    312                     self.naDict["ONAME"]=self.globals[key] 
    313                     self.naDict["ORG"]=self.globals[key] 
     423                    # If fields came from NA then extract appropriate fields. 
     424                    match=re.match(r"(.*)\s+\(ONAME from NASA Ames file\);\s+(.*)\s+\(ORG from NASA Ames file\)\.", self.globals[key]) 
     425                    if match: 
     426                        self.naDict["ONAME"]=match.groups()[0] 
     427                        self.naDict["ORG"]=match.groups()[1] 
     428                    else: 
     429                        self.naDict["ONAME"]=self.globals[key] 
     430                        self.naDict["ORG"]=self.globals[key]                 
     431                                     
    314432                elif key=="comment": 
    315                     self.ncom=[self.globals[key]] 
     433                    # Need to work out if they are actually comments from NASA Ames in the first place 
     434                    #self.ncom=[self.globals[key]] 
     435                    comLines=self.globals[key].split("\n") 
     436                    normComms=[] 
     437                    normCommFlag=None 
     438                    specComms=[] 
     439                    specCommFlag=None 
     440                    for line in comLines: 
     441                        if line.find("###NASA Ames Special Comments follow###")>-1: 
     442                            specCommFlag=1 
     443                        elif line.find("###NASA Ames Special Comments end###")>-1: 
     444                            specCommFlag=None 
     445                        elif line.find("###NASA Ames Normal Comments follow###")>-1: 
     446                            normCommFlag=1 
     447                        elif line.find("###NASA Ames Normal Comments end###")>-1: 
     448                            normCommFlag=None    
     449                        elif specCommFlag==1: 
     450                            specComms.append(line) 
     451                        elif normCommFlag==1: 
     452                            normComms.append(line) 
     453                        elif line.find("###Data Section begins on the next line###")>-1: 
     454                            pass 
     455                        else: 
     456                            normComms.append(line)           
     457                     
     458                    self.extra_comments=[specComms, normComms, []]                   
     459                                     
     460                elif key=="first_valid_date_of_data": 
     461                    self.naDict["DATE"]=self.globals[key] 
     462                 
    316463                elif key in ("Conventions", "references"): 
    317464                    pass 
     
    319466                    self.naDict[globalmap[key]]=self.globals[key] 
    320467            else: 
    321                 self.extra_comments.append("%s:   %s" % (key, self.globals[key])) 
     468                self.extra_comments[2].append("%s:   %s" % (key, self.globals[key])) 
    322469        return 
    323470 
    324471 
    325472    def defineNAComments(self, normal_comments=[], special_comments=[]): 
    326         NCOM=["***NASA Ames Normal Comments follow***"] 
     473        """ 
     474        Defines the Special and Normal comments sections in the NASA Ames file  
     475        object - including information gathered from the defineNAGlobals method. 
     476        """ 
     477         
    327478        if hasattr(self, "ncom"):  normal_comments=self.ncom+normal_comments 
     479        NCOM=[] 
    328480        for ncom in normal_comments: 
    329481            NCOM.append(ncom) 
    330         NCOM.append("") 
    331         NCOM.append("Additional Global Attributes defined in Cdmsfile and not translated elsewhere:") 
    332  
    333         if hasattr(self, "extra_comments"): 
    334             for excom in self.extra_comments: 
     482        if len(NCOM)>0:   NCOM.append("") 
     483         
     484        if len(self.extra_comments[2])>0: 
     485            for excom in extra_comments[2]: 
     486                NCOM.append(excom) 
     487         
     488        if len(self.extra_comments[1])>0:   
     489            NCOM.append("Additional Global Attributes defined in the source file and not translated elsewhere:") 
     490            for excom in self.extra_comments[1]: 
    335491                NCOM.append(excom) 
    336492 
     
    338494            for h in self.history: 
    339495                NCOM.append(h) 
    340  
    341         NCOM.append("") 
    342         NCOM.append("***Data Section begins on the next line***") 
    343  
    344         SCOM=["***NASA Ames Special Comments follow***"] 
     496         
     497        if len(NCOM)>0: 
     498            NCOM.insert(0, "###NASA Ames Normal Comments follow###") 
     499            NCOM.append("") 
     500            NCOM.append("###NASA Ames Normal Comments end###") 
     501        NCOM.append("###Data Section begins on the next line###") 
     502 
     503        specCommentsFlag=None 
     504        SCOM=[] 
     505        special_comments=self.extra_comments[0] 
     506        if len(special_comments)>0:  
     507            SCOM=["###NASA Ames Special Comments follow###"] 
     508            specCommentsFlag=1 
    345509        for scom in special_comments: 
    346510            SCOM.append(scom) 
    347511 
    348         SCOM.append("Additional Variable Attributes defined in Cdmsfile and not translated elsewhere:") 
    349         used_var_atts=("name", "long_name", "standard_name", "id", "missing_value", "fill_value", "units") 
     512 
     513        used_var_atts=("name", "long_name", "standard_name", "id",  
     514                "missing_value", "fill_value", "units",  
     515                "nasa_ames_var_number", "nasa_ames_aux_var_number") 
     516        varCommentsFlag=None 
     517 
     518        for var in self.orderedVars: 
     519            varflag="unused" 
     520            name=getBestName(var) 
     521            for scom in var.attributes.keys(): 
     522                if type(scom) in (str, float, int) and scom not in used_var_atts: 
     523                    if varflag=="unused": 
     524                        if varCommentsFlag==None: 
     525                            varCommentsFlag=1 
     526                            if specCommentsFlag==None: 
     527                                SCOM=["###NASA Ames Special Comments follow###"] 
     528                            SCOM.append("Additional Variable Attributes defined in the source file and not translated elsewhere:") 
     529                            SCOM.append("###Variable attributes from source (NetCDF) file follow###") 
     530                        varflag=="using"  
     531                        SCOM.append("\tVariable (%s): %s" % (var.id, name)) 
     532                    SCOM.append("\t\t%s = %s" % (scom, var.attributes[scom])) 
     533        if varCommentsFlag==1:  SCOM.append("###Variable attributes from source (NetCDF) file end###") 
     534        if specCommentsFlag==1: 
     535            SCOM.append("###NASA Ames Special Comments end###") 
     536 
     537        """used_var_atts=("name", "long_name", "standard_name", "id", "missing_value", "fill_value", "units") 
    350538        for var in self.vars: 
    351539            for scom in var.attributes.keys(): 
    352540                name=getBestName(var) 
    353541                if type(scom) in (str, float, int) and scom not in used_var_atts: 
    354                     SCOM.append("\t%s: %s - %s" % (name, scom, var.attributes[scom])) 
    355  
    356         self.naDict["NCOM"]=NCOM 
    357         self.naDict["NNCOML"]=len(NCOM) 
    358         self.naDict["SCOM"]=SCOM 
    359         self.naDict["NSCOML"]=len(SCOM) 
     542                    SCOM.append("\t%s: %s - %s" % (name, scom, var.attributes[scom]))""" 
     543 
     544        # Strip out empty lines (or returns) 
     545        NCOM_cleaned=[] 
     546        SCOM_cleaned=[] 
     547        for c in NCOM: 
     548            if c.strip() not in ("", " ", "  "): NCOM_cleaned.append(c) 
     549        for c in SCOM: 
     550            if c.strip() not in ("", " ", "  "): SCOM_cleaned.append(c) 
     551 
     552        self.naDict["NCOM"]=NCOM_cleaned 
     553        self.naDict["NNCOML"]=len(self.naDict["NCOM"]) 
     554        self.naDict["SCOM"]=SCOM_cleaned 
     555        self.naDict["NSCOML"]=len(self.naDict["SCOM"]) 
    360556        return 
    361557 
    362558    def defineGeneralHeader(self, header_items={}): 
    363         """Defines known header items and overwrites any with header_items key/value pairs.""" 
    364         self.naDict["DATE"]=time.strftime("%Y %m %d", time.localtime(time.time())).split() 
     559        """ 
     560        Defines known header items and overwrites any with header_items  
     561        key/value pairs. 
     562        """ 
     563        # Check if DATE field previously known in NASA Ames file 
     564        time_now=time.strftime("%Y %m %d", time.localtime(time.time())).split() 
     565        if not self.naDict.has_key("RDATE"): 
     566            self.naDict["RDATE"]=time_now 
     567         
    365568        if self.ax0.isTime(): 
    366569            # Get first date in list 
    367             (unit, start_date)=re.match("(\w+)\s+?since\s+?(\d+-\d+-\d+)", self.ax0.units).groups() 
    368              
    369             comptime=cdtime.s2c(start_date) 
    370             first_day=comptime.add(self.naDict["X"][0][0], getattr(cdtime, unit.capitalize())) 
    371             self.naDict["RDATE"]=string.replace(str(first_day).split(" ")[0], "-", " ").split() 
     570            try: 
     571                (unit, start_date)=re.match("(\w+)\s+?since\s+?(\d+-\d+-\d+)", self.ax0.units).groups()             
     572                comptime=cdtime.s2c(start_date) 
     573                first_day=comptime.add(self.naDict["X"][0][0], getattr(cdtime, unit.capitalize())) 
     574                self.naDict["DATE"]=string.replace(str(first_day).split(" ")[0], "-", " ").split() 
     575            except: 
     576                print "Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file." 
     577                self.naDict["DATE"]="<DATE_UNKNOWN>" 
    372578        else:  
    373             self.naDict["RDATE"]=self.naDict["DATE"] 
     579            if not self.naDict.has_key("DATE"): 
     580                print "Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file." 
     581                self.naDict["DATE"]="<DATE_UNKNOWN>" 
    374582        self.naDict["IVOL"]=1 
    375583        self.naDict["NVOL"]=1 
     
    383591 
    384592    args=sys.argv[1:] 
    385     if len(args)<2: 
     593    if len(args)<4: 
    386594        print helpMessage 
    387595        print "Incorrect number of arguments used." 
    388596        sys.exit() 
    389     elif len(args)==3: 
    390         cdms2na(args[0], args[1], args[2])  
    391     else: 
    392         cdms2na(args[0], args[1])  
     597         
     598    for arg in args: 
     599        if arg=="-i": 
     600            infile=args[args.index(arg)+1] 
     601        elif arg=="-o": 
     602            outfile=args[args.index(arg)+1] 
     603 
     604    cdms2na(infile, outfile)  
Note: See TracChangeset for help on using the changeset viewer.