source: nappy/trunk/nappy/nc_interface/cdms_to_na.py @ 3390

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/nappy/trunk/nappy/nc_interface/cdms_to_na.py@3390
Revision 3390, 6.5 KB checked in by astephen, 12 years ago (diff)
Line 
1#   Copyright (C) 2004 CCLRC & NERC( Natural Environment Research Council ).
2#   This software may be distributed under the terms of the
3#   Q Public License, version 1.0 or later. http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
4
5"""
6cdms_to_na.py
7=============
8
9Holds the class CDMSToNA that converts a set of CDMS variables and global attributes.
10
11"""
12
13# Imports from python standard library
14import sys
15
16# Import from nappy package
17import nappy
18from nappy.na_error import na_error
19import nappy.utils
20import nappy.utils.common_utils
21import nappy.cdms_utils.var_utils
22import nappy.na_file.na_core
23import nappy.nc_interface.na_content_collector
24
25# Import external packages (if available)
26if sys.platform.find("win") > -1:
27    raise na_error.NAPlatformError("Windows does not support CDMS. CDMS is required to convert to CDMS objects and NetCDF.")
28try:
29    import cdms, Numeric
30except:
31    raise Exception("Could not import third-party software. Nappy requires the CDMS and Numeric packages to be installed to convert to CDMS and NetCDF.")
32
33cdms.setAutoBounds("off") 
34
35# Define global variables
36permitted_overwrite_metadata = ("DATE",  "RDATE", "ANAME", "MNAME",
37           "ONAME", "ORG", "SNAME", "VNAME")
38items_as_lists = ["DATE", "RDATE", "ANAME", "VNAME"]
39var_limit = 5000 # surely never going to get this many vars in a file!
40DEBUG = nappy.utils.DEBUG
41
42class NCToNA:
43    """
44    Converts a NetCDF file to one or more NASA Ames files.
45    """
46
47    def __init__(self, cdms_variables, global_atts={}, na_items_to_override={}, 
48                 only_return_file_names=False):
49        """
50        Sets up instance variables.     
51        """
52        self.cdms_variables = cdms_variables
53        self.global_atts = global_atts
54        self.na_items_to_override = na_items_to_override
55        self.only_return_file_names = only_return_file_names
56        self.converted = False
57        self.output_message = []
58   
59    def convert(self):
60        """
61        Reads the CDMS objects and convert to a set of dictionaries that
62        provide the structure for a NA File object.
63        Returns [(na_dict, var_ids), (na_dict, var_ids), ....]
64        All these na_dict dictionaries can be readily written to a NA File object.
65
66        Note that NASA Ames is not as flexible as NetCDF so you cannot just send any
67        set of variables to write to a NASA Ames file. Essentially there is one
68        multi-dimensional structure and all variables must be defined against it.
69
70        Otherwise variables must be auxiliary variables within that structure (i.e. only
71        defined once per the least changing dimension.
72        """
73        if self.converted == True:
74            print "Already converted to NA dictionary objects."
75            return self.na_dict_list
76       
77        msg = "Reading data from: %s\n" % self.nc_file
78        if DEBUG: print msg
79        self.output_message.append(msg)
80
81        # Convert any singleton variables to CDMS variables
82        variables = self._convertSingletonVars(self.cdms_variables)
83
84        # Re-order variables if they have the attribute "nasa_ames_var_number" which means they came from a NASA Ames file originally
85        variables = self._reorderVars(variables)
86
87        # Make first call to collector class that creates NA dict from CDMS variables and global atts dicts
88        collector = NAContentCollector(variables, self.global_atts)
89        collected_dict = collector.collectNAContent()
90        # NOTE: collected_dict has attributes: na_dict, var_ids, unused_vars
91
92        # Return if no files returned
93        if collected.var_ids == None:
94            msg = "\nNo files created after variables parsed."
95            if DEBUG: print msg
96            self.output_message.append(msg)
97            return 
98
99        # Set up a list to collect multiple calls to content collector
100        na_dict_list = []
101        na_dict_list.append((collected_dict.na_dict, collected_dict.var_ids))
102
103        # If there are variables that were not captured (i.e. unused) by NAContentCollector then loop through these
104        # in attempt to convert all to a set of na_dicts
105        while len(collector.unused_vars) > 0:
106            collector = NAContentCollector(collector.unused_vars, self.global_atts)
107            collected_dict = collector.collectNAContent()           
108            self.output_message += collector.output_message
109            # Append to list if more variables were captured
110            if collector.var_ids != None: 
111                na_dict_list.append((collected_dict.na_dict, collected_dict.var_ids))
112
113        self.na_dict_list = na_dict_list
114        self.converted = True
115        return self.na_dict_list
116
117    def _convertSingletonVars(self, variables):
118        """
119        Loops through variables to convert singleton variables (i.e. Masked Arrays/Numeric Arrays)
120        to proper CDMS variables. Then code won't break when asking for rank attribute later.
121        Returns a list of CDMS variable objects
122        """
123        vars = []
124
125        for variable in variables:
126            var_obj = variable
127
128            # If singleton variable then convert into proper CDMS variables so code doesn't break later
129            if not hasattr(var_obj, "rank"):
130                var_metadata = var_obj.attributes       
131                var_value = var_obj
132                        var_obj = cdms.createVariable(Numeric.array(var_obj), 
133                    id=nappy.cdms_utils.var_utils.getBestName(var_metadata).replace(" ", "_"), 
134                    attributes=var_metadata)
135                        var_obj.value = var_obj._data[0]                 
136               
137            vars.append(var_obj)
138            return vars
139
140    def _reorderVars(self, variables):
141        """
142        Returns a reordered list of variables. Any that have the attribute
143        "nasa_ames_var_number" get ordered first in the list (according to numbering).
144        """
145        # Set up a long list (longer than number of vars)
146        if len(variables) > var_limit:
147            raise Exception("Can only handle converting less than " + `var_limit` + " variables in any batch.")
148
149        # Collect up those that are ordered and unordered
150        ordered_vars = [None] * var_limit
151        unordered_vars = []
152        for var in variables:
153            var_metadata = var.attributes
154            if hasattr(var_metadata, "nasa_ames_var_number"):
155                num = var_metadata.nasa_ames_var_number
156                ordered_vars[num] = var
157            else:
158                unordered_vars.append(var)
159   
160        vars = []
161        # Clear any None values in ordered_vars and place in final vars list
162        for var in ordered_vars + unordered_vars:
163            if var != None: vars.append(var)
164           
165        return vars
166
Note: See TracBrowser for help on using the repository browser.