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()