| tPISMNC.py - pism-exp-gsw - ice stream and sediment transport experiments | |
| git clone git://src.adamsgaard.dk/pism-exp-gsw | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| tPISMNC.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_bou… | |
| 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,… | |
| 77 """ | |
| 78 time_dependent: boolean | |
| 79 | |
| 80 dims: an optional list of dimension names. use this to override … | |
| 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, a… | |
| 127 else: | |
| 128 return None | |
| 129 | |
| 130 def write_2d_field(self, var_name, data, time_dependent=False, attrs… | |
| 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() |