source: TI03-DataExtractor/branches/old_stuff/dx-0.1.0/products.py @ 793

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI03-DataExtractor/branches/old_stuff/dx-0.1.0/products.py@793
Revision 793, 10.3 KB checked in by astephen, 13 years ago (diff)

Put all the old code in the old_stuff branch.

Line 
1"""
2products.py
3===========
4
5Procucts module for the extractor package.
6
7This module holds the Product, Plot, DataFile and other classes
8used to generate products from the data extractor.
9
10Version history
11===============
12
13Version 1.0:  - 01/03/2004
14              - Ag Stephens, British Atmospheric Data Centre.
15              - First version.
16
17"""
18
19# Import required modules
20import os
21import cdms
22import vcs
23import sys
24from exceptions import *
25#import subset  # This made the grib tables incorrect!!!
26
27# Import global variables
28from shared_data import *
29from config import *
30from errorHandler import *
31
32# Define global variables
33
34
35def getValuesInRange(start, end, array):
36    """
37    getValuesInRange function - takes a start and end value and returns the
38    values in the array that are between them.
39    """
40    # check all are floats
41    array=map(lambda x: float(x), list(array))
42    if array[0]>array[-1]: array.reverse()
43    if start>end:  (start, end)=(end, start)
44    (start, end)=(float(start), float(end))
45    print start, end
46    rtarray=[]
47    for i in array:
48        if i>=start and i<=end:
49            rtarray.append(i)
50    print rtarray
51    return rtarray
52
53
54class Product:
55 
56    def __init__(self, filenames, request):
57        self.files=[]
58        self.request=request
59        self.variables={}
60        self.user=request["user"]
61        self.outputDir=os.path.join("/requests", self.user, "extractor_output")
62        if not os.path.isdir(self.outputDir):
63            makeDirsAndPerms("/requests", (self.user, "extractor_output"), 0750, "badc.byacl") 
64            accessControl="""
65<Limit READ>
66         AllowUser %s 
67         AllowUser badc
68         DenyAll
69</Limit>
70<Limit WRITE>
71         AllowUser %s 
72         DenyAll
73         </Limit>
74<Limit DIRS>
75         AllowUser %s
76         AllowUser badc
77         DenyAll
78</Limit>
79""" % ((self.user,)*3)
80            accessControlFile=os.path.join(self.outputDir, ".ftpaccess")
81            acf=open(accessControlFile, 'w')
82            acf.write(accessControl)
83            acf.close()
84            os.chmod(accessControlFile, 0644)
85            os.system('/bin/chown badc.byacl %s' % accessControlFile)
86             
87        for n in range(1, self.request["num_datasets"]+1):
88            self.files.append(cdms.open(filenames[n-1]))
89
90    def getParam(self, estimateDuration="no"):
91        """
92        getParam method - uses the requests information and data/metadata files
93        to extract the data objects requested by the user. These are then stored in
94        the list self.variables.
95        """
96       
97        # Copy standard out to local variable ready for redirection
98        stdout=sys.stdout
99
100        # Define a list to put variable names in
101        varnames=[]
102
103        # Begin looping through for each dataset
104        for n in range(1, self.request["num_datasets"]+1):
105
106            # Define values for start and end for this request - these are in CDMS compatible format
107            start="%.4d-%.2d-%.2d %.2d:00" % (int(self.request["start_year_%s" % n]), 
108                        int(self.request["start_month_%s" % n]), int(self.request["start_day_%s" % n]),
109                        int(self.request["start_hour_%s" % n]))
110            end="%.4d-%.2d-%.2d %.2d:00" % (int(self.request["end_year_%s" % n]), 
111                        int(self.request["end_month_%s" % n]), int(self.request["end_day_%s" % n]),
112                        int(self.request["end_hour_%s" % n]))
113
114            # Define time interval variables
115            (interval_value, interval_units)=(self.request["time_interval_value_%s" % n], 
116                        self.request["time_interval_units_%s" % n])
117               
118            # Define horizontal spatial bounds
119            (north, west, south, east)=(float(self.request["north"]), float(self.request["west"]), 
120                                        float(self.request["south"]), float(self.request["east"]))
121
122            # If a vertical domain is defined then get from request
123            if self.request.has_key("vertical_domain_%s" % n) and self.request["vertical_domain_%s" % n]!="Single level":
124                vertical_domain=self.request["vertical_domain_%s" % n]
125                try:
126                    levels=float(vertical_domain)
127                except:
128                    vertical_domain=map(lambda x: float(x), vertical_domain)
129                    levels=(min(vertical_domain), max(vertical_domain))
130            else:
131                levels=None
132
133            # Get the variable id from the variable name string in the request
134            variable_name=(self.request["variable_%s" % n].split('[')[-1].split(']'))[0].strip("]\"()' ")
135            # Add 'no' if first character is a number
136            if variable_name[0] in "0123456789": variable_name="no"+variable_name
137            # Add to variable names list
138            varnames.append(variable_name)
139
140            # A quick fix to make sure that the time interval value is an integer
141            try:
142                interval_value=int(str(interval_value).split(".")[0])
143            except:
144                pass
145 
146            # Redirect standard output so output is not sent to screen
147            sys.stdout=RedirectStdout()
148
149            # If an estimate of duration is required and we are on the first dataset
150            # then do some estimating based on assumptions such as 1 time step per file
151            if estimateDuration=="yes" and n==1:
152
153                # Scale factor depends on whether caching is involved (ERA-40 at BADC)
154                # If high then we estimate a request will take longer
155                scale_factor=1
156                if self.request["dataset_1"]=="ERA-40" or (self.request.has_key("dataset_2") and self.request["dataset_2"]=="ERA-40"):
157                    scale_factor=3
158
159                # Use cdtime to determine the number of time steps
160                import cdtime
161                start_cdtime=cdtime.s2c(start)
162                end_cdtime=cdtime.s2c(end)
163                timestep_count=1
164
165                while start_cdtime<end_cdtime:
166                    timestep_count=timestep_count+1
167                    start_cdtime=start_cdtime.add(interval_value,  getattr(cdtime, interval_units.capitalize()))
168
169                # estimated duration in seconds
170                estimatedDuration=timestep_count*5*scale_factor*self.request["num_datasets"] # seconds, random
171                rtvalue=estimatedDuration
172
173                # Also estimate volume here by getting a variable's metadata, then go
174                # through each dimension to muliply out the number of points.
175                npoints=1
176                var_metadata=self.files[n-1][variable_name]
177                # Get list of axes
178                axis_list=var_metadata.getAxisList()
179
180                # Work through all axes capturing the number of points within the interval
181                for axis in axis_list:
182
183                    if axis.isTime():   # we already have the number of time steps
184                        npoints=npoints*timestep_count
185                    else:
186
187                        if axis.isLongitude(): 
188                            npoints=npoints*len(getValuesInRange(west, east, axis))
189
190                        if axis.isLatitude():
191                            npoints=npoints*len(getValuesInRange(south, north, axis))
192
193                        if axis.isLevel():
194                            if type(levels)!=type([]) and type(levels)!=type((0,)):
195                                templevels=[levels]
196                            else:
197                                templevels=levels
198                            npoints=npoints*len(getValuesInRange(templevels[0], templevels[-1], axis))
199
200                # Now we have the number of points we can multiply that by 4 bytes per float
201                size=4.*npoints
202                sizeLimit=100*2L**20  # 100 MB limit
203
204                # If size is over limit then send a tidy error to the user
205                if size>sizeLimit:
206                    errorString="""Your request of %.1f MB is over the current %s MB size limit.
207The Data Extractor cannot yet deal with such large requests.
208Consider mailing <A HREF="mailto:%s">%s</A> for advice or submitting multiple smaller requests.""" % ((size/(2L**20)), (sizeLimit/(2L**20)), admin_mail_address, admin_mail_address)
209                    sys.stdout=stdout
210                    handleError(errorString, noheader=1, request_id=self.request["req_id"],
211                                user=self.request["user"])
212                               
213            # Else if estimate duration is not set we need to create the variable(s)
214            elif estimateDuration=="no":
215                # If there is a vertical dimension then select user options
216                if levels:
217                    self.variables[n]=self.files[n-1](variable_name, time=(start, end), 
218                                      longitude=(west, east), latitude=(south, north), level=levels)
219                # Else do not need to define levels in the call
220                else:
221                    self.variables[n]=self.files[n-1](variable_name, time=(start, end), 
222                                      longitude=(west, east), latitude=(south, north))
223                # Return value is undefined (only needed if estimate duration is required).
224                rtvalue=None
225
226        # Back to the top level now. If we are delivering data (i.e. not estimating duration) and
227        # the user has requested 2 datasets then subtract variable 2 from variable 1 and define the
228        # result as variable 0.
229        if estimateDuration=="no":
230
231            if self.request["num_datasets"]==2:
232                    # Use the standard CDAT regridder to interpolate variable 2 to variable 1 grid
233                    import regrid
234                    print "<P><B>Differencing datasets...<B><P>"
235                    self.variables[0]=self.variables[1]-self.variables[2].regrid(self.variables[1].getGrid())
236                    #sys.stdout=stdout
237                    print "<H1>",varnames
238                    # Create some metadata
239                    self.variables[0].long_name="Differenced dataset: %s - %s" % (varnames[0], varnames[1])
240                    self.variables[0].id="new_var" # "%s_minus_%s" % (self.variables[1].id, self.variables[2].id)
241
242            # If just one dataset then define that variable as variable 0 (the one we write to a file).
243            else:
244                    self.variables[0]=self.variables[1]
245
246        sys.stdout=stdout
247        return rtvalue
248
249
250class DataFile(Product):
251
252    def process(self):
253         self.ext=".nc"
254         pid=os.getpid()
255         filename="output_"+str(pid)+self.ext
256         outfile=os.path.join(self.outputDir, filename)
257         if os.path.isfile(outfile): os.unlink(outfile)
258         fout=cdms.open(outfile, "w")
259         fout.write(self.variables[0])
260         fout.close()
261         os.chmod(outfile, 0640)
262         os.system("chown badc.byacl %s" % outfile)
263         fileByBrowser=os.path.join(BADC_DATA_BROWSER, outfile[1:])
264         return (fileByBrowser, outfile)
265   
266     
Note: See TracBrowser for help on using the repository browser.