|
11 | 11 | relevant provenance information and input files to facilitate better |
12 | 12 | data management techniques to streamline later analyses. |
13 | 13 | ''' |
14 | | -import datetime |
15 | | -import io |
| 14 | +import datetime, calendar |
| 15 | +import io, os |
16 | 16 | import itertools |
17 | 17 | import numpy as np |
| 18 | +import pandas as pd |
| 19 | +import matplotlib.pyplot as plt |
18 | 20 |
|
| 21 | +from matplotlib.backends.backend_pdf import PdfPages |
| 22 | +from mpl_toolkits.axes_grid1 import make_axes_locatable |
19 | 23 | from collections import OrderedDict |
20 | 24 |
|
21 | | - |
22 | | -def modify_params(params_in, params_out, param_mods=None): |
23 | | - ''' |
24 | | - Given a parameter file in and a dictionary of param_mods, write modified |
25 | | - parameters to params_out. |
26 | | -
|
27 | | - Example: |
28 | | -
|
29 | | - Below we modify the monthly jh_coef by increasing it 10% for every month. |
30 | | -
|
31 | | - >>> params_in = 'models/lbdc/params' |
32 | | - >>> params_out = 'scenarios/jh_coef_1.1/params' |
33 | | - >>> scale_10pct = lambda x: x * 1.1 |
34 | | - >>> modify_params(params_in, params_out, {'jh_coef': scale_10pct}) |
35 | | -
|
36 | | - So param_mods is a dictionary of with keys being parameter names and |
37 | | - values a function that operates on a single value. Currently we only |
38 | | - accept functions that operate on single values without reference to any |
39 | | - other parameters. The function will be applied to every cell, month, or |
40 | | - cascade routing rule for which the parameter is defined. |
41 | | -
|
42 | | - Arguments: |
43 | | - params_in (str): location on disk of the base parameter file |
44 | | -
|
45 | | - params_out (str): location on disk where the modified parameters will be written |
46 | | -
|
47 | | - param_mods (dict): param name-keyed, param modification function-valued |
48 | | -
|
49 | | - Returns: |
50 | | - None |
51 | | -
|
52 | | - ''' |
53 | | - p_in = Parameters(params_in) |
54 | | - |
55 | | - for k in param_mods: |
56 | | - p_in[k] = param_mods[k](p_in[k]) |
57 | | - |
58 | | - p_in.write(params_out) |
59 | | - |
| 25 | +OPJ = os.path.join |
60 | 26 |
|
61 | 27 | class Parameters(object): |
62 | 28 | ''' |
@@ -138,6 +104,196 @@ def write(self, out_name): |
138 | 104 | out_file.writelines([str(a) + '\n' |
139 | 105 | for a in new_arr.flatten()]) |
140 | 106 |
|
| 107 | + def plot(self, nrows, which='all', out_dir=None, xlabel=None,\ |
| 108 | + ylabel=None, cbar_label=None, title=None, mpl_style=None): |
| 109 | + """ |
| 110 | + Plot PRMS parameters as time series or 2D spatial grid depending on |
| 111 | + parameter dimension. The PRMS parameter file is assumed to represent |
| 112 | + a model that was set up on a uniform rectangular grid with the spatial |
| 113 | + index of HRUs starting in the upper left corner and moving left to |
| 114 | + right across columns and down rows. |
| 115 | + |
| 116 | + Args: |
| 117 | + params (prms_python.Parameters): An instance of Parameters that |
| 118 | + corresponds with the PRMS parameter file to plot. |
| 119 | + nrows (int): The number of rows in the PRMS model grid for plotting spatial |
| 120 | + parameters. Function will only work for rectangular gridded models |
| 121 | + with HRU indices starting in the upper left cell moving left to right |
| 122 | + across columns and down across rows. |
| 123 | + |
| 124 | + Kwargs: |
| 125 | + which (str): name of PRMS parameter to plot or 'all'. If 'all' then |
| 126 | + the function will print 3 multipage pdfs: one for nhru dimensional |
| 127 | + parameters, one for nhru by monthly parameters, one for other parameters |
| 128 | + of length > 1, and one html file containing single valued parameters |
| 129 | + out_dir (str): path to an output dir, default: current directory |
| 130 | + xlabel (str): x label for plot(s) |
| 131 | + ylabel (str): y label for plot(s) |
| 132 | + cbar_label (str): label for colorbar on spatial plot(s) |
| 133 | + title (str): plot title |
| 134 | + mpl_style (str, list): name or list of names of matplotlib style sheets |
| 135 | + to use for plot(s) |
| 136 | + |
| 137 | + Returns: None |
| 138 | + """ |
| 139 | + params = self |
| 140 | + |
| 141 | + if not isinstance(params, Parameters): |
| 142 | + raise TypeError('params must be instance of Parameters, not '\ |
| 143 | + + str(type(params))) |
| 144 | + if not out_dir: |
| 145 | + out_dir = os.getcwd() |
| 146 | + |
| 147 | + if not os.path.isdir(out_dir): |
| 148 | + os.mkdir(out_dir) |
| 149 | + |
| 150 | + nhru = params.dimensions['nhru'] |
| 151 | + ncols = nhru//nrows |
| 152 | + |
| 153 | + if not mpl_style: |
| 154 | + mpl_style = 'classic' |
| 155 | + plt.style.use(mpl_style) |
| 156 | + |
| 157 | + # make pdfs and html of all parameters seperated in 4 files based on dimension |
| 158 | + if which == 'all': |
| 159 | + ## spatial parameters with dimension of length nhru |
| 160 | + p_names = [param['name'] for param in params.base_params if\ |
| 161 | + param['length'] == nhru and len(param['dimnames'])==1 ] |
| 162 | + with PdfPages(OPJ(out_dir,'nhru_param_maps.pdf')) as pdf: |
| 163 | + for p in p_names: |
| 164 | + try: |
| 165 | + plt.figure() |
| 166 | + ax = plt.gca() |
| 167 | + im = ax.imshow(params['{}'.format(p)].reshape(nrows,ncols), origin='upper') |
| 168 | + # origin upper- assumes indices of parameters starts in upper left |
| 169 | + divider = make_axes_locatable(ax) |
| 170 | + cax = divider.append_axes("right", size="5%", pad=0.05) |
| 171 | + plt.colorbar(im, cax=cax) |
| 172 | + ax.set_title('{}'.format(p)) |
| 173 | + ax.tick_params(left='off', bottom='off', labelleft='off', labelbottom='off') |
| 174 | + pdf.savefig() |
| 175 | + plt.close() |
| 176 | + except: |
| 177 | + print('{param} parameter failed to plot'.format(param=p)) |
| 178 | + |
| 179 | + ## monthly spatial parameters (on plot per month) |
| 180 | + p_names = [param['name'] for param in params.base_params if\ |
| 181 | + param['dimnames'][0] == 'nhru' and len(param['dimnames'])==2 ] |
| 182 | + with PdfPages(OPJ(out_dir,'nhru_by_nmonths_param_maps.pdf')) as pdf: |
| 183 | + for p in p_names: |
| 184 | + try: |
| 185 | + for i in range(12): #month |
| 186 | + plt.figure() |
| 187 | + ax = plt.gca() |
| 188 | + im = ax.imshow(params['{}'.format(p)][i].reshape(nrows, ncols), origin='upper') |
| 189 | + divider = make_axes_locatable(ax) |
| 190 | + cax = divider.append_axes("right", size="5%", pad=0.05) |
| 191 | + plt.colorbar(im, cax=cax) |
| 192 | + ax.set_title('{} {}'.format(p, calendar.month_name[i+1])) |
| 193 | + ax.tick_params(left='off', bottom='off', labelleft='off', labelbottom='off') |
| 194 | + pdf.savefig() |
| 195 | + plt.close() |
| 196 | + except: |
| 197 | + print('{param} for {month} failed to plot'.\ |
| 198 | + format(param=p, month=calendar.month_name[i+1])) |
| 199 | + |
| 200 | + ## non spatial parameters with dimension length > 1 to be plotted as time series |
| 201 | + p_names = [param['name'] for param in params.base_params if\ |
| 202 | + ( 1 < param['length'] <= 366 )\ |
| 203 | + and param['dimnames'][0] != 'nhru' ] |
| 204 | + with PdfPages(OPJ(out_dir,'non_spatial_param_plots.pdf')) as pdf: |
| 205 | + for p in p_names: |
| 206 | + try: |
| 207 | + param_dict = [param for param in params.base_params if param['name'] == p][0] |
| 208 | + plt.plot(np.arange(1, param_dict['length']+1, 1), params[p]) |
| 209 | + plt.xlabel(param_dict['dimnames'][0]) |
| 210 | + plt.ylabel(p) |
| 211 | + plt.xlim(0.5, param_dict['length']+0.5) |
| 212 | + pdf.savefig() |
| 213 | + plt.close() |
| 214 | + except: |
| 215 | + print('{param} parameter failed to plot'.format(param=p)) |
| 216 | + |
| 217 | + ## html table of parameters with dimension length = 1 |
| 218 | + p_names = [param['name'] for param in params.base_params if param['length'] == 1] |
| 219 | + df = pd.DataFrame() |
| 220 | + df.index.name = 'parameter' |
| 221 | + for p in p_names: |
| 222 | + df.set_value(p, 'value', params[p]) |
| 223 | + df.to_html(OPJ(out_dir,'single_valued_params.html')) |
| 224 | + ################################################################ |
| 225 | + # plot single parameter, in case of nhru by monthly param, |
| 226 | + # save multi-page pdf |
| 227 | + else: |
| 228 | + param_name = which |
| 229 | + try: |
| 230 | + params[which] |
| 231 | + except: |
| 232 | + print('{param} is not a valid PRMS parameter'.format(param=param_name)) |
| 233 | + return |
| 234 | + |
| 235 | + param_dict = [param for param in params.base_params if param['name'] == param_name][0] |
| 236 | + |
| 237 | + # labels for single plots |
| 238 | + if not cbar_label: |
| 239 | + cbar_label = param_name |
| 240 | + if not title: |
| 241 | + title = '' |
| 242 | + |
| 243 | + # if parameter is not spatial, one dimensional, with length greater than one, plot as line |
| 244 | + if param_dict['ndims'] == 1 and ( 1 < param_dict['length'] <= 366 )\ |
| 245 | + and param_dict['dimnames'][0] != 'nhru': |
| 246 | + if not xlabel: |
| 247 | + xlabel = param_dict['dimnames'][0] |
| 248 | + if not ylabel: |
| 249 | + ylabel = param_name |
| 250 | + plt.plot(np.arange(1, param_dict['length']+1,1), params[param_name]) |
| 251 | + plt.xlim(0.5, param_dict['length']+0.5) |
| 252 | + plt.xlabel(xlabel) |
| 253 | + plt.ylabel(ylabel) |
| 254 | + plt.title(title) |
| 255 | + # if spatial and one dimensional, plot |
| 256 | + elif param_dict['ndims'] == 1 and param_dict['length'] == params.dimensions['nhru']: |
| 257 | + if not xlabel: |
| 258 | + xlabel = '' |
| 259 | + if not ylabel: |
| 260 | + ylabel = '' |
| 261 | + plt.figure() |
| 262 | + ax = plt.gca() |
| 263 | + im = ax.imshow(params[param_name].reshape(nrows,ncols), origin='upper') |
| 264 | + divider = make_axes_locatable(ax) |
| 265 | + cax = divider.append_axes("right", size="5%", pad=0.05) |
| 266 | + plt.colorbar(im, cax=cax,label=cbar_label) |
| 267 | + ax.tick_params(left='off', bottom='off', labelleft='off', labelbottom='off') |
| 268 | + ax.set_title(title) |
| 269 | + ax.set_ylabel(ylabel) |
| 270 | + ax.set_xlabel(xlabel) |
| 271 | + # spatial monthly parameter |
| 272 | + elif param_dict['dimnames'][0] == 'nhru' and param_dict['dimnames'][1] == 'nmonths'\ |
| 273 | + and param_dict['ndims'] == 2: |
| 274 | + if not xlabel: |
| 275 | + xlabel = '' |
| 276 | + if not ylabel: |
| 277 | + ylabel = '' |
| 278 | + file_name = '{}.pdf'.format(param_name) |
| 279 | + with PdfPages(OPJ(out_dir, file_name)) as pdf: |
| 280 | + for i in range(12): #month |
| 281 | + plt.figure() |
| 282 | + ax = plt.gca() |
| 283 | + im = ax.imshow(params['{}'.format(param_name)][i].reshape(nrows,ncols), origin='upper') |
| 284 | + divider = make_axes_locatable(ax) |
| 285 | + cax = divider.append_axes("right", size="5%", pad=0.05) |
| 286 | + plt.colorbar(im, cax=cax) |
| 287 | + ax.set_title('{} {}'.format(param_name, calendar.month_name[i+1])) |
| 288 | + ax.set_ylabel(ylabel) |
| 289 | + ax.set_xlabel(xlabel) |
| 290 | + ax.tick_params(left='off', bottom='off', labelleft='off', labelbottom='off') |
| 291 | + pdf.savefig() |
| 292 | + plt.close() |
| 293 | + else: |
| 294 | + val = params[param_name] |
| 295 | + print('{param} is single valued with value: {v}'.format(param=param_name, v=val)) |
| 296 | + |
141 | 297 | def __read_base(self, base_file): |
142 | 298 | "Read base file returning 2-tuple of dimension and params dict" |
143 | 299 |
|
@@ -307,3 +463,43 @@ def __setitem__(self, key, value): |
307 | 463 | raise ValueError('New array does not match existing') |
308 | 464 |
|
309 | 465 | self.param_arrays[key] = value |
| 466 | + |
| 467 | + |
| 468 | + |
| 469 | +def modify_params(params_in, params_out, param_mods=None): |
| 470 | + ''' |
| 471 | + Given a parameter file in and a dictionary of param_mods, write modified |
| 472 | + parameters to params_out. |
| 473 | +
|
| 474 | + Example: |
| 475 | +
|
| 476 | + Below we modify the monthly jh_coef by increasing it 10% for every month. |
| 477 | +
|
| 478 | + >>> params_in = 'models/lbdc/params' |
| 479 | + >>> params_out = 'scenarios/jh_coef_1.1/params' |
| 480 | + >>> scale_10pct = lambda x: x * 1.1 |
| 481 | + >>> modify_params(params_in, params_out, {'jh_coef': scale_10pct}) |
| 482 | +
|
| 483 | + So param_mods is a dictionary of with keys being parameter names and |
| 484 | + values a function that operates on a single value. Currently we only |
| 485 | + accept functions that operate on single values without reference to any |
| 486 | + other parameters. The function will be applied to every cell, month, or |
| 487 | + cascade routing rule for which the parameter is defined. |
| 488 | +
|
| 489 | + Arguments: |
| 490 | + params_in (str): location on disk of the base parameter file |
| 491 | +
|
| 492 | + params_out (str): location on disk where the modified parameters will be written |
| 493 | +
|
| 494 | + param_mods (dict): param name-keyed, param modification function-valued |
| 495 | +
|
| 496 | + Returns: |
| 497 | + None |
| 498 | +
|
| 499 | + ''' |
| 500 | + p_in = Parameters(params_in) |
| 501 | + |
| 502 | + for k in param_mods: |
| 503 | + p_in[k] = param_mods[k](p_in[k]) |
| 504 | + |
| 505 | + p_in.write(params_out) |