pism-exp-gsw

ice stream and sediment transport experiments
git clone git://src.adamsgaard.dk/pism-exp-gsw # fast
git clone https://src.adamsgaard.dk/pism-exp-gsw.git # slow
Log | Files | Refs | README | LICENSE Back to index

PISMNC.py (5381B)


      1 #!/usr/bin/env python3
      2 
      3 try:
      4     import netCDF4 as netCDF
      5 except:
      6     print("netCDF4 is not installed!")
      7     sys.exit(1)
      8 
      9 
     10 class PISMDataset(netCDF.Dataset):
     11 
     12     def create_time(self, use_bounds=False, length=None, units=None):
     13         self.createDimension('time', size=length)
     14         t_var = self.createVariable('time', 'f8', ('time',))
     15 
     16         t_var.axis = "T"
     17         t_var.long_name = "time"
     18         if not units:
     19             t_var.units = "seconds since 1-1-1"  # just a default
     20         else:
     21             t_var.units = units
     22 
     23         if use_bounds:
     24             self.createDimension('n_bounds', 2)
     25             self.createVariable("time_bounds", 'f8', ('time', 'n_bounds'))
     26             t_var.bounds = "time_bounds"
     27 
     28     def create_dimensions(self, x, y, time_dependent=False, use_time_bounds=False):
     29         """
     30         Create PISM-compatible dimensions in a NetCDF file.
     31         """
     32 
     33         if time_dependent and not 'time' in list(self.variables.keys()):
     34             self.create_time(use_time_bounds)
     35 
     36         self.createDimension('x', x.size)
     37         self.createDimension('y', y.size)
     38 
     39         x_var = self.createVariable('x', 'f8', ('x',))
     40         x_var[:] = x
     41 
     42         y_var = self.createVariable('y', 'f8', ('y',))
     43         y_var[:] = y
     44 
     45         x_var.axis = "X"
     46         x_var.long_name = "X-coordinate in Cartesian system"
     47         x_var.units = "m"
     48         x_var.standard_name = "projection_x_coordinate"
     49 
     50         y_var.axis = "Y"
     51         y_var.long_name = "Y-coordinate in Cartesian system"
     52         y_var.units = "m"
     53         y_var.standard_name = "projection_y_coordinate"
     54 
     55         self.sync()
     56 
     57     def append_time(self, value, bounds=None):
     58         if 'time' in list(self.dimensions.keys()):
     59             time = self.variables['time']
     60             N = time.size
     61             time[N] = value
     62 
     63             if bounds:
     64                 self.variables['time_bounds'][N, :] = bounds
     65 
     66     def set_attrs(self, var_name, attrs):
     67         """attrs should be a list of (name, value) tuples."""
     68         if not attrs:
     69             return
     70 
     71         for (name, value) in attrs.items():
     72             if name == "_FillValue":
     73                 continue
     74             setattr(self.variables[var_name], name, value)
     75 
     76     def define_2d_field(self, var_name, time_dependent=False, dims=None, nc_type='f8', attrs=None):
     77         """
     78         time_dependent: boolean
     79 
     80         dims: an optional list of dimension names. use this to override the
     81               default order ('time', 'y', 'x')
     82 
     83         attrs: a dictionary of attributes
     84         """
     85         if not dims:
     86             if time_dependent:
     87                 dims = ('time', 'y', 'x')
     88             else:
     89                 dims = ('y', 'x')
     90 
     91         try:
     92             var = self.variables[var_name]
     93         except:
     94             if attrs is not None and '_FillValue' in list(attrs.keys()):
     95                 var = self.createVariable(var_name, nc_type, dims,
     96                                           fill_value=attrs['_FillValue'])
     97             else:
     98                 var = self.createVariable(var_name, nc_type, dims)
     99 
    100             self.set_attrs(var_name, attrs)
    101 
    102         return var
    103 
    104     def define_timeseries(self, var_name, attrs=None):
    105         try:
    106             if attrs is not None and '_FillValue' in list(attrs.keys()):
    107                 var = self.createVariable(var_name, 'f8', ('time',),
    108                                           fill_value=attrs['_FillValue'])
    109             else:
    110                 var = self.createVariable(var_name, 'f8', ('time',))
    111         except:
    112             var = self.variables[var_name]
    113 
    114         self.set_attrs(var_name, attrs)
    115 
    116         return var
    117 
    118     def write(self, var_name, data, time_dependent=False, attrs=None):
    119         """
    120         Write time-series or a 2D field to a file.
    121         """
    122 
    123         if data.ndim == 1:
    124             return self.write_timeseries(var_name, data, attrs=attrs)
    125         elif data.ndim == 2:
    126             return self.write_2d_field(var_name, data, time_dependent, attrs=attrs)
    127         else:
    128             return None
    129 
    130     def write_2d_field(self, var_name, data, time_dependent=False, attrs=None):
    131         """
    132         Write a 2D numpy array to a file in a format PISM can read.
    133         """
    134 
    135         var = self.define_2d_field(var_name, time_dependent, attrs=attrs)
    136 
    137         if time_dependent:
    138             last_record = self.variables['time'].size - 1
    139             var[last_record, :, :] = data
    140         else:
    141             var[:] = data
    142 
    143         return var
    144 
    145     def write_timeseries(self, var_name, data, attrs=None):
    146         """Write a 1D (time-series) array to a file."""
    147 
    148         var = self.define_timeseries(var_name, attrs=attrs)
    149         var[:] = data
    150 
    151         return var
    152 
    153 
    154 if __name__ == "__main__":
    155     # produce a NetCDF file for testing
    156     from numpy import linspace, meshgrid
    157 
    158     nc = PISMDataset("foo.nc", 'w')
    159 
    160     x = linspace(-100, 100, 101)
    161     y = linspace(-100, 100, 201)
    162 
    163     xx, yy = meshgrid(x, y)
    164 
    165     nc.create_dimensions(x, y, time_dependent=True, use_time_bounds=True)
    166 
    167     nc.define_2d_field("xx", time_dependent=True,
    168                        attrs={"long_name": "xx",
    169                               "comment": "test variable",
    170                               "valid_range": (-200.0, 200.0)})
    171 
    172     for t in [0, 1, 2, 3]:
    173         nc.append_time(t, (t - 1, t))
    174 
    175         nc.write("xx", xx + t, time_dependent=True)
    176         nc.write("yy", yy + 2 * t, time_dependent=True)
    177 
    178     nc.close()