00001
00002 """
00003 Created on Thu Dec 29 19:58:19 2011
00004
00005 @author: Sat Kumar Tomer
00006 @website: www.ambhas.com
00007 @email: satkumartomer@gmail.com
00008
00009 Copuled Surface-Ground water Lumped hydrological Model
00010 """
00011
00012 from __future__ import division
00013
00014 import numpy as np
00015 import xlrd, xlwt
00016 import os
00017 import gdal
00018 from gdalconst import *
00019 from scipy.interpolate import Rbf
00020 from Scientific.IO import NetCDF as nc
00021 import datetime
00022 np.seterr(all='raise')
00023
00024 class CSGLM:
00025 """
00026 This is the main class of the CGLSM.
00027 This will read the input data,
00028 do the processing
00029 and then write the output files
00030
00031 """
00032
00033
00034 def __init__(self, input_file):
00035 """
00036 Input:
00037 input_file: the file which contains all the information
00038 including forcing and parameters.
00039 """
00040
00041 self.input_file = input_file
00042
00043
00044 self._read_input()
00045
00046
00047 max_t = int(self.final_time/self.dt)
00048 self.max_t = max_t
00049
00050
00051
00052 self.actual_evap = np.empty(max_t)
00053 self.actual_trans = np.empty(max_t)
00054 self.E_In = np.empty(max_t)
00055 self.horton_runoff = np.empty(max_t)
00056 self.recharge = np.empty(max_t)
00057 self.runoff = np.empty(max_t)
00058 self.gw_level = np.empty(max_t+1)
00059 self.gw_level[0] = self.initial_gwl
00060 self.sm = np.empty((self.no_layer,max_t+1))
00061 self.sm[:,0] = self.initial_sm.flatten()
00062 self.surface_storage = np.zeros(max_t+1)
00063
00064 for t in range(max_t):
00065 self.t = t
00066
00067
00068 self._get_forcing()
00069
00070
00071 self._interception_fun()
00072
00073
00074 self._runoff_fun()
00075
00076
00077 self._soil_fun()
00078
00079
00080 self._surface_storage_fun()
00081
00082
00083 self._gw_fun()
00084
00085
00086 self._write_output()
00087
00088 def _read_input(self):
00089 """
00090 This checks if all the required input sheets are present in the xls file,
00091 read the data from input file, which can be used later in other functions
00092 """
00093
00094
00095 input_sheets = ['ind', 'forcing', 'initial_condition', 'gw_par',
00096 'runoff_par', 'units', 'root_info', 'temporal_info',
00097 'spatial_info', 'ET_par', 'soil_hyd_par', 'output_par']
00098
00099
00100 self._check_sheets(input_sheets, self.input_file)
00101
00102
00103 self._read_ind()
00104
00105
00106 self._read_spatial()
00107
00108
00109 self._read_temporal()
00110
00111
00112 self._read_root_distribution()
00113
00114
00115 self._read_units()
00116
00117
00118 self._read_initial_condition()
00119
00120
00121 self._read_shp()
00122
00123
00124 self._read_runoff_par()
00125
00126
00127 self._surface_storage_par()
00128
00129
00130 self._read_gw_par()
00131
00132
00133 self._read_ET_par()
00134
00135
00136 self._read_forcing()
00137
00138
00139 self._read_ofile_name()
00140
00141
00142 output_message = 'Input data reading completed sucessfully'
00143 self._colored_output(output_message, 32)
00144
00145 def _check_sheets(self, check_sheets, check_file):
00146 """
00147 This functions check if all the sheets needed to model are present
00148 in check_file
00149
00150 """
00151
00152 foo = xlrd.open_workbook(check_file)
00153 check_sheet_names = foo.sheet_names()
00154
00155 for check_sheet_names in check_file:
00156 if check_sheet_names not in check_file:
00157 output_message = check_sheet_names + ' is missing'
00158 self._colored_output(output_message,31)
00159
00160 def _read_ind(self):
00161 """
00162 Read the ind sheet
00163 legend stores the information about the indices of other properties,
00164 which would be used by all other properties reading functions
00165 """
00166 book = xlrd.open_workbook(self.input_file)
00167 sheet = book.sheet_by_name('ind')
00168
00169 ind = {}
00170 for i in range(sheet.nrows-1):
00171 ind[str(sheet.cell_value(i+1,0))] = int(sheet.cell_value(i+1,1))
00172
00173 self.ind = ind
00174
00175 def _read_spatial(self):
00176 """
00177 Read the spatial info
00178 """
00179 book = xlrd.open_workbook(self.input_file)
00180 sheet = book.sheet_by_name('spatial_info')
00181
00182 j = self.ind['spatial_info']
00183 no_layer = int(sheet.cell_value(j,1))
00184 z = sheet.row_values(j,2)
00185 if no_layer != len(z):
00186 raise ValueError('The length of the thickness_layers\
00187 should be equal to the No_layer')
00188
00189 self.no_layer = no_layer
00190 self.z = z
00191
00192 depth = np.zeros(no_layer+1)
00193 depth[1:] = np.cumsum(z)
00194 self.mid_z = 0.5*(depth[1:]+depth[:-1])
00195
00196
00197 def _read_temporal(self):
00198 """
00199 Read the temporal info
00200 """
00201 book = xlrd.open_workbook(self.input_file)
00202 sheet = book.sheet_by_name('temporal_info')
00203
00204 j = self.ind['temporal_info']
00205 dt = sheet.cell_value(j,1)
00206 final_time = sheet.cell_value(j,2)
00207
00208 self.dt = dt
00209 self.final_time = final_time
00210
00211 def _read_root_distribution(self):
00212 """
00213 read the root distribution factors
00214 """
00215 book = xlrd.open_workbook(self.input_file)
00216 sheet = book.sheet_by_name('root_info')
00217
00218 j = self.ind['root_info']
00219 self.ndvi_max = sheet.cell_value(j,1)
00220 self.ndvi_min = sheet.cell_value(j,2)
00221 self.fapar_max = sheet.cell_value(j,3)
00222 self.lai_max = sheet.cell_value(j,4)
00223 self.Rd_max = sheet.cell_value(j,5)
00224 self.Lrd = sheet.cell_value(j,6)
00225
00226
00227 def _read_units(self):
00228 """
00229 read the units of the forcing data
00230 """
00231 book = xlrd.open_workbook(self.input_file)
00232 sheet = book.sheet_by_name('units')
00233
00234 j = self.ind['units']
00235 forcing_units = {}
00236 for i in range(sheet.ncols-1):
00237 forcing_units[str(sheet.cell_value(0,i+1))] = str(sheet.cell_value(j,i+1))
00238 self.forcing_units = forcing_units
00239
00240 def _read_initial_condition(self):
00241 """
00242 read initial condition
00243 """
00244
00245 j = self.ind['initial_condition']
00246
00247 book = xlrd.open_workbook(self.input_file)
00248 sheet = book.sheet_by_name('initial_condition')
00249 theta_0 = sheet.row_values(j,2)
00250 self.initial_gwl = sheet.cell_value(j,1)
00251 self.initial_sm = np.array(theta_0)
00252
00253 try:
00254 self.initial_sm.shape = self.no_layer,1
00255 except:
00256 raise ValueError('The length of the theta_0 should be \
00257 equal to the no_layer')
00258
00259 def _read_shp(self):
00260 """
00261 read the soil hydraulic parameters
00262 """
00263
00264 j = self.ind['soil_hyd_par']
00265
00266 book = xlrd.open_workbook(self.input_file)
00267 sheet = book.sheet_by_name('soil_hyd_par')
00268 soil_par = {}
00269 soil_par['qr'] = sheet.cell_value(j,1)
00270 soil_par['f'] = sheet.cell_value(j,2)
00271 soil_par['a'] = sheet.cell_value(j,3)
00272 soil_par['n'] = sheet.cell_value(j,4)
00273 soil_par['Ks'] = sheet.cell_value(j,5)
00274 soil_par['l'] = sheet.cell_value(j,6)
00275
00276
00277 soil_par['zl'] = sheet.cell_value(j,9)
00278 soil_par['fl'] = sheet.cell_value(j,10)
00279
00280 m = 1-1/soil_par['n']
00281
00282 soil_par['evap_fc'] = self.psi2theta(-0.33, soil_par['qr'], soil_par['f'],
00283 soil_par['a'], m, soil_par['n'])
00284
00285 soil_par['evap_wp'] = self.psi2theta(-15, soil_par['qr'], soil_par['f'],
00286 soil_par['a'], m, soil_par['n'])
00287
00288 self.soil_par = soil_par
00289
00290 def psi2theta(self,psi, thetar, thetas, alpha, m, n):
00291 """
00292 psi2theta: given the theta calculate the pressure head
00293 """
00294 if (psi>=0):
00295 theta = thetas
00296 elif psi<-1e6:
00297 theta = 1.01*thetar
00298 else:
00299 theta = thetar+(thetas-thetar)*pow(1+pow(abs(alpha*psi),n),-m)
00300 return theta
00301
00302 def _read_runoff_par(self):
00303 """
00304 read the parameters related to runoff
00305 """
00306
00307 j = self.ind['runoff_par']
00308
00309 book = xlrd.open_workbook(self.input_file)
00310 sheet = book.sheet_by_name('runoff_par')
00311 runoff_par = {}
00312 for i in range(sheet.ncols-1):
00313 runoff_par[str(sheet.cell_value(0,i+1))] = float(sheet.cell_value(j,i+1))
00314 self.runoff_par = runoff_par
00315
00316 def _surface_storage_par(self):
00317 """
00318 read the parameters related to surface storage
00319 """
00320
00321 j = self.ind['surface_storage_par']
00322
00323 book = xlrd.open_workbook(self.input_file)
00324 sheet = book.sheet_by_name('surface_storage_par')
00325 surface_storage_par = {}
00326 surface_storage_par['a'] = float(sheet.cell_value(j,1))
00327 surface_storage_par['b'] = float(sheet.cell_value(j,2))
00328 self.surface_storage_par = surface_storage_par
00329
00330 def _read_gw_par(self):
00331 """
00332 read the parameters related to groundwater
00333 """
00334
00335 j = self.ind['gw_par']
00336
00337 book = xlrd.open_workbook(self.input_file)
00338 sheet = book.sheet_by_name('gw_par')
00339 gw_par = {}
00340 for i in range(sheet.ncols-1):
00341 gw_par[str(sheet.cell_value(0,i+1))] = float(sheet.cell_value(j,i+1))
00342
00343 self.gw_par = gw_par
00344
00345 def _read_ET_par(self):
00346 """
00347 read the parameters related to evaporation
00348 """
00349
00350
00351
00352
00353
00354 ET_par = {}
00355
00356
00357 qr = self.soil_par['qr']
00358 f = self.soil_par['f']
00359 a = self.soil_par['a']
00360 n = self.soil_par['n']
00361 m = 1-1/n
00362 fl = self.soil_par['fl']
00363 mid_z = self.mid_z
00364 ET_par['trans_fc'] = self.psi2theta(-0.33, qr, f, a, m, n)*np.exp(-mid_z/fl)
00365 ET_par['trans_wp'] = self.psi2theta(-15, qr, f, a, m, n)*np.exp(-mid_z/fl)
00366
00367 self.ET_par = ET_par
00368
00369 def _read_forcing(self):
00370 """
00371 read the forcing data from xls file
00372 """
00373 book = xlrd.open_workbook(self.input_file)
00374 sheet = book.sheet_by_name('forcing')
00375
00376 data_len = sheet.nrows-1
00377 year = np.zeros(data_len)
00378 doy = np.zeros(data_len)
00379 rain = np.zeros(data_len)
00380 pet = np.zeros(data_len)
00381 ndvi = np.zeros(data_len)
00382 pumping = np.zeros(data_len)
00383
00384 for i in xrange(data_len):
00385 year[i] = sheet.cell_value(i+1,0)
00386 doy[i] = sheet.cell_value(i+1,1)
00387 rain[i] = sheet.cell_value(i+1,2)
00388 pet[i] = sheet.cell_value(i+1,3)
00389 ndvi[i] = sheet.cell_value(i+1,4)
00390 pumping[i] = sheet.cell_value(i+1,5)
00391
00392 self.year = year
00393 self.doy = doy
00394
00395
00396 if self.forcing_units['rain'] == 'mm':
00397 self.rain = rain/1000.0
00398 elif self.forcing_units['rain'] == 'm':
00399 self.rain = rain
00400 else:
00401 raise ValueError("The units of rain should be either 'mm' or 'm' ")
00402
00403 if self.forcing_units['pet'] == 'mm':
00404 self.pet = pet/1000.0
00405 elif self.forcing_units['pet'] == 'm':
00406 self.pet = pet
00407 else:
00408 raise ValueError("The units of PET should be either 'mm' or 'm' ")
00409
00410 if self.forcing_units['pumping'] == 'mm':
00411 self.pumping = pumping/1000.0
00412 elif self.forcing_units['pumping'] == 'm':
00413 self.pumping = pumping
00414 else:
00415 raise ValueError("The units of pumping should be either 'mm' or 'm' ")
00416
00417
00418 ndvi_max = self.ndvi_max
00419 ndvi_min = self.ndvi_min
00420 ndvi[ndvi>ndvi_max] = ndvi_max
00421 ndvi[ndvi<ndvi_min] = ndvi_min
00422
00423 fapar = 1.60*ndvi-0.02
00424 fapar_max = self.fapar_max
00425
00426 lai_max = self.lai_max
00427 lai = lai_max*np.log(1-fapar)/np.log(1-fapar_max)
00428
00429 Rd_max = self.Rd_max
00430 Rd = Rd_max*lai/lai_max
00431 fc = ((ndvi-ndvi_max)/(ndvi_max-ndvi_min))**2
00432
00433 self.kc = 0.8+0.4*(1-np.exp(-0.7*lai))
00434 self.ndvi = ndvi
00435 self.lai = lai
00436 self.Rd = Rd
00437 self.fc = fc
00438
00439
00440 def _read_ofile_name(self):
00441 """
00442 read the forcing data from xls file
00443 """
00444 book = xlrd.open_workbook(self.input_file)
00445 sheet = book.sheet_by_name('output_par')
00446 self.ofile_name = str(sheet.cell_value(0,1))
00447
00448
00449 def _colored_output(self, output_message, color):
00450 """
00451 This functions print the output_message in the color
00452 Input:
00453 output_messgae: the text you want to print
00454 color: the color in which you want to print text, it could be one of:
00455 30: Gray
00456 31: Red
00457 32: Green
00458 33: Yellow
00459 34: Blue
00460 35: Magneta
00461 66: Cyan
00462 37: White
00463 38: Crimson
00464 41: Highlighted Red
00465 42: Highlighted Green
00466 43: Highlighted Brown
00467 44: Highlighted Blue
00468 45: Highlighted Magenta
00469 46: Highlighted Cyan
00470 47: Highlighted Gray
00471 48: Highlighted Crimson
00472 Output:
00473 This returns None, but print the output in python shell
00474 """
00475
00476 print(("\033[31m" +output_message+ "\033[0m").replace('31',str(color)))
00477
00478 def _get_forcing(self):
00479 """
00480 this will give the forcing at time t
00481 """
00482
00483 self.rain_cur = self.rain[self.t]
00484 self.pet_cur = self.pet[self.t]*self.kc[self.t]
00485 self.lai_cur = self.lai[self.t]
00486 self.pumping_cur = self.pumping[self.t]
00487
00488 self.cur_year = self.year[self.t]
00489 self.cur_doy = self.doy[self.t]
00490
00491
00492 def _interception_fun(self):
00493 """
00494 Input:
00495 lai_cur: LAI at the current time step
00496 pet_cur: PET at the current time step
00497 rain_cur: Rainfall at the current time step
00498
00499 Output:
00500 E_In: Evaporation from Interception
00501 T: Transpiration
00502 E: Evaporation
00503 net_rain_cur: Net rainfall (precipitation-interception loss) at
00504 current time step
00505 """
00506 In = self.lai[self.t]*0.2/1000.0
00507 soil_cover = np.exp(-0.5*self.lai_cur)
00508 veg_cover = 1 - soil_cover
00509
00510 E_In = np.min([veg_cover*self.rain_cur, veg_cover*self.pet_cur, veg_cover*In])
00511 T = np.min([veg_cover*self.pet_cur - 0.2*E_In, 1.2*self.pet_cur - E_In])
00512 E = np.min([soil_cover*self.pet_cur, 1.2*self.pet_cur-T-E_In])
00513 net_rain_cur = self.rain_cur - E_In
00514
00515 self.E_In[self.t] = E_In
00516 self.trans = T
00517 self.evap = E
00518 self.net_rain_cur = net_rain_cur
00519
00520 def _runoff_fun(self):
00521 """
00522 this module will calculate the runoff based on the initial soil moisture
00523 and net precipitation
00524
00525 Input:
00526 C: Average soil moisture
00527 Pn: Precipitation after interception loss
00528 runoff_par: runoff parameters ['Cm','B']
00529
00530 Output:
00531 runoff_cur: Runoff at current time step
00532 """
00533
00534
00535
00536
00537
00538
00539
00540 Kdt_ref = 3.0
00541 Kref = 2e-6
00542 theta_s = self.soil_par['f']*np.exp(-self.mid_z/self.soil_par['fl'])
00543 Dx = theta_s - self.sm[:,self.t]
00544 Dx = Dx*self.mid_z
00545 Dx = Dx[:3].sum()
00546 Kdt = Kdt_ref*self.soil_par['Ks']/Kref
00547
00548 Pn = self.net_rain_cur
00549 Imax = Pn*(Dx*(1-np.exp(-Kdt)))/(Pn+Dx*(1-np.exp(-Kdt)))
00550
00551 self.runoff_cur = Pn - Imax
00552 self.runoff[self.t] = self.runoff_cur
00553
00554
00555
00556 def _soil_fun(self):
00557 """
00558 Input:
00559 soil_par : soil hydraulic parameters
00560 z : thicknes of layers
00561 R : runoff
00562 no_layer : no. of layers
00563 theta_0 : initial soil moisture
00564 root_frac : root fraction in each layer
00565 T : transpiration
00566 Pn : net precipitation (precipitation - interception)
00567 E : soil evaporation
00568 Pu : pumping
00569 dt : time step
00570 Output:
00571 theta_1: soil moisture for next time step
00572 Re: recharge (L)
00573 """
00574
00575 self.runoff_cur /= self.dt
00576 self.evap /= self.dt
00577 self.trans /= self.dt
00578 self.net_rain_cur /= self.dt
00579 self.pumping_cur /= self.dt
00580
00581
00582 theta_1_mat = np.zeros(self.no_layer)
00583
00584
00585 K = np.zeros((self.no_layer,1))
00586 D = np.zeros((self.no_layer,1))
00587 for i in range(self.no_layer):
00588 if i<self.no_layer-1:
00589
00590
00591
00592 K[i], D[i] = self._shp(0.5*(self.sm[i, self.t]+self.sm[i+1, self.t]),i)
00593 else:
00594 K[i], D[i] = self._shp(self.sm[i,self.t],i)
00595 K = K.flatten()*np.exp(-self.mid_z/self.soil_par['zl'])
00596 D = D.flatten()*np.exp(-self.mid_z/self.soil_par['zl'])
00597
00598
00599
00600 self._smi_fun()
00601 AE = self.evap*self.SSMI
00602 self._transpiration_fun()
00603 AT = self.AT
00604
00605
00606 A = np.zeros((self.no_layer, self.no_layer))
00607 U = np.zeros((self.no_layer,1))
00608 z = self.z
00609 for i in range(self.no_layer):
00610 if i == 0:
00611 A[0,0] = -D[1]/(0.5*z[1]*(z[1]+z[2]))
00612 A[0,1] = D[1]/(0.5*z[1]*(z[1]+z[2]))
00613 U[0] = (-AT[0] - K[0] + self.net_rain_cur - AE - self.runoff_cur \
00614 + self.pumping_cur)/z[0]
00615 elif i == self.no_layer-1:
00616 A[i,i] = -D[i-1]/(0.5*z[i]*(z[i-1]+z[i]))
00617 A[i,i-1] = D[i-1]/(0.5*z[i]*(z[i-1]+z[i]))
00618 U[i] = (-AT[i] + K[i-1] - K[i])/z[i]
00619 else:
00620 A[i,i-1] = D[i-1]/(0.5*z[i]*(z[i-1]+z[i]))
00621 A[i,i+1] = D[i]/(0.5*z[i]*(z[i]+z[i+1]))
00622 A[i,i] = -A[i,i-1] -A[i,i+1]
00623 U[i] = (-AT[i] +K[i-1] - K[i])/z[i]
00624
00625
00626 F = np.eye(self.no_layer) + A*self.dt
00627
00628
00629 G = np.dot(F,U)*self.dt
00630
00631
00632 theta_1 = np.dot(F, self.sm[:,self.t]) + G.flatten()
00633
00634
00635 Re = K[-1]*self.dt
00636
00637
00638
00639 if theta_1[0] >= self.soil_par['f']:
00640 HR = (theta_1[0]-self.soil_par['f'])*z[0]
00641 theta_1[0] = self.soil_par['f']
00642 else:
00643 HR = 0
00644
00645
00646 theta_s = self.soil_par['f']*np.exp(-self.mid_z/self.soil_par['fl'])
00647 wp = self.ET_par['trans_wp']*np.exp(-self.mid_z/self.soil_par['fl'])
00648 for j in range(self.no_layer):
00649 if theta_1[j]>theta_s[j]:
00650 theta_1[j] = theta_s[j]
00651 if theta_1[j]<wp[j]:
00652 theta_1[j] = wp[j]
00653
00654
00655 self.sm[:,self.t+1] = theta_1.flatten()
00656 self.G = G
00657 self.F = F
00658 self.theta_1 = theta_1
00659 self.recharge[self.t] = Re
00660 self.actual_evap[self.t] = AE*self.dt
00661 self.actual_trans[self.t] = AT.sum()*self.dt
00662 self.horton_runoff[self.t] = HR
00663
00664 def _smi_fun(self):
00665 """
00666 this module computes the surface soil moisture stress index, and root zone soil moisture
00667 stress index
00668 """
00669
00670
00671 SSMI = (self.sm[0,self.t] - self.soil_par['evap_wp'])/(
00672 self.soil_par['evap_fc'] - self.soil_par['evap_wp'])
00673
00674 if SSMI > 1:
00675 SSMI = 1
00676 elif SSMI<0:
00677 SSMI = 0
00678
00679
00680 RZSMI = np.zeros((self.no_layer,))
00681
00682 for i in range(self.no_layer):
00683 trans_wp = self.ET_par['trans_wp'][i]
00684 trans_fc = self.ET_par['trans_fc'][i]
00685 if (self.sm[i,self.t] < trans_wp):
00686 RZSMI[i] = 0
00687 elif self.sm[i,self.t] > trans_fc:
00688 RZSMI[i] = 1
00689 else:
00690
00691 RZSMI[i] = (self.sm[i,self.t]-trans_wp)/(trans_fc - trans_wp)
00692
00693
00694 self.SSMI = SSMI
00695 self.RZSMI = RZSMI
00696
00697
00698 def _transpiration_fun(self):
00699 """
00700 this function computes the actual transpiration for all the soil layers
00701 """
00702
00703 Lrd = self.Lrd
00704 Rd = self.Rd[self.t]
00705 r_density = np.empty(self.no_layer)
00706 for i in range(self.no_layer):
00707 if i==0:
00708 z1 = 0
00709 else:
00710 z1 = np.sum(self.z[:i])
00711 z2 = np.sum(self.z[:i+1])
00712 if z2>Rd:
00713 z2 = Rd
00714 if z1>z2:
00715 z1 = z2
00716
00717 r_density[i] = np.exp(-z1/Lrd) - np.exp(-z2/Lrd)
00718 r_density = r_density/( 1 - np.exp(-Rd/Lrd) )
00719
00720 self.r_density = r_density
00721 self.AT = self.RZSMI*r_density*self.trans
00722
00723
00724 def _shp(self, theta,i):
00725 """
00726 soil hydraulic properties module
00727 i is the layer
00728 """
00729
00730 qr = self.soil_par['qr']*np.exp(-self.mid_z[i]/self.soil_par['fl'])
00731 f = self.soil_par['f']*np.exp(-self.mid_z[i]/self.soil_par['fl'])
00732 a = self.soil_par['a']
00733 n = self.soil_par['n']
00734 Ks = self.soil_par['Ks']
00735 l = self.soil_par['l']
00736
00737 m = 1-1/n
00738 Se = (theta-qr)/(f - qr)
00739 if Se>=0.99:
00740 Se = 0.99
00741 elif Se<=0.01:
00742 Se = 0.01
00743 K = Ks*Se**l*(1-(1-Se**(1/m))**m)**2
00744 D = K/(a*(f-qr)*m*n*(Se**(1/m+1))*(Se**(-1/m)-1)**m)
00745 return K, D
00746
00747 def _surface_storage_fun(self):
00748 """
00749 this module stores the surface water
00750 and give as recharge to the groundwater model
00751 """
00752
00753 surface_storage = self.surface_storage[self.t] \
00754 + self.runoff_cur \
00755 + self.horton_runoff[self.t]
00756 a = self.surface_storage_par['a']
00757 b = self.surface_storage_par['b']
00758 Rep = a*surface_storage**b
00759
00760 self.surface_storage[self.t+1] = surface_storage - Rep
00761 self.Rep = Rep
00762
00763
00764 def _gw_fun(self):
00765 """
00766 Groundwater module
00767 """
00768 F = self.gw_par['F']
00769 G = self.gw_par['G']
00770 hmin = self.gw_par['hmin']
00771
00772 self.sy = F/G
00773 self.lam = (1-F)*self.sy
00774
00775
00776 u = self.recharge[self.t]-self.pumping_cur + self.Rep
00777 self.gw_level[self.t+1] = F*(self.gw_level[self.t]-hmin) + G*u + hmin
00778
00779 dzn = self.gw_level[self.t+1] - self.gw_level[self.t]
00780 self.discharge = u - self.sy*(dzn)
00781 self.z[-1] = self.z[-1] - dzn
00782
00783
00784
00785
00786
00787
00788 def _write_output(self):
00789 """
00790 This will write the data in the xls format
00791
00792 """
00793
00794 wbk = xlwt.Workbook()
00795 sheet = wbk.add_sheet('variables')
00796
00797 sheet.write(0,0,'year')
00798 sheet.write(0,1,'doy')
00799 sheet.write(0,2,'gw level')
00800 for i in range(self.no_layer):
00801 sheet.write(0,3+i,'SM - %i'%(i+1))
00802
00803 for i in range(self.max_t):
00804 sheet.write(i+1,0,self.year[i])
00805 sheet.write(i+1,1,self.doy[i])
00806 sheet.write(i+1,2,self.gw_level[i])
00807 for j in range(self.no_layer):
00808 sheet.write(i+1,3+j,self.sm[j,i])
00809
00810 sheet = wbk.add_sheet('flux')
00811
00812 sheet.write(0,0,'year')
00813 sheet.write(0,1,'doy')
00814 sheet.write(0,2,'rain')
00815 sheet.write(0,3,'PET')
00816 sheet.write(0,4,'lai')
00817 sheet.write(0,5,'pumping')
00818 sheet.write(0,6,'actual evap')
00819 sheet.write(0,7,'actual trans')
00820 sheet.write(0,8,'E_In')
00821 sheet.write(0,9,'AET')
00822 sheet.write(0,10,'recharge')
00823 sheet.write(0,11,'runoff')
00824
00825
00826 for i in range(self.max_t):
00827 sheet.write(i+1,0,self.year[i])
00828 sheet.write(i+1,1,self.doy[i])
00829 sheet.write(i+1,2,self.rain[i])
00830 sheet.write(i+1,3,self.pet[i])
00831 sheet.write(i+1,4,self.lai[i])
00832 sheet.write(i+1,5,self.pumping[i])
00833 sheet.write(i+1,6,self.actual_evap[i])
00834 sheet.write(i+1,7,self.actual_trans[i])
00835 sheet.write(i+1,8,self.E_In[i])
00836 sheet.write(i+1,9,self.actual_evap[i]+self.actual_trans[i]+self.E_In[i])
00837 sheet.write(i+1,10,self.recharge[i])
00838 sheet.write(i+1,11,self.runoff[i])
00839
00840 wbk.save(self.ofile_name)
00841
00842 output_message = 'Output data writting completed sucessfully'
00843 self._colored_output(output_message, 32)
00844
00845
00846 class CSGLM_ENKF(CSGLM):
00847 """
00848 This is the main class of the Ensemble Kalman Filter (EnKF)
00849 coupled with the CSGLM model. The model is given in the class CSGLM.
00850
00851 This will read the input data,
00852 do the processing
00853 and then write the output files
00854
00855 """
00856
00857 def __init__(self,input_file):
00858 """
00859 Input:
00860 input_file: the file which contains all the information
00861 including forcing and parameters.
00862 """
00863 self.input_file = input_file
00864 self.n_ens = 10
00865
00866 self._read_input()
00867
00868
00869 self.initialize()
00870
00871
00872 for t in range(self.max_t):
00873 self.t = t
00874
00875
00876 self._get_forcing()
00877
00878
00879 self._perturb_soil_par_ens()
00880
00881
00882 for ens in range(self.n_ens):
00883 self.ens = ens
00884
00885
00886 self._interception_ens_fun()
00887
00888
00889 self._runoff_ens_fun()
00890
00891
00892 self._soil_ens_fun()
00893
00894
00895 self._surface_storage_ens_fun()
00896
00897
00898 self._gw_ens_fun()
00899
00900
00901
00902 self._enkf_par()
00903
00904 self._enkf_ET()
00905
00906 self._write_output()
00907
00908
00909 self.nc_file.close()
00910
00911
00912 def initialize(self):
00913 """
00914 this initializes all the required variables
00915 and open the netcdf file for writting
00916 also generates the initial ensemble of the soil hydraulic parameters
00917 """
00918 max_t = int(self.final_time/self.dt)
00919 self.max_t = max_t
00920
00921
00922 self.surface_storage_ens = np.zeros((self.n_ens, self.max_t+1))
00923 self.gw_level_ens = np.zeros((self.n_ens, self.max_t+1))
00924
00925 self.gw_level_ens[:,0] = self.initial_gwl
00926 self.sm_ens = self.initial_sm + 0.02*np.random.normal(size=(self.n_ens,self.no_layer))
00927
00928
00929 file = nc.NetCDFFile(self.ofile_name, 'w')
00930 setattr(file, 'title', 'output of the model ambhas.csglm_enkf')
00931 now = datetime.datetime.now()
00932 setattr(file, 'description', 'The model was run at %s'%(now.ctime()))
00933 file.createDimension('depth', self.no_layer)
00934 file.createDimension('time', self.max_t+1)
00935 file.createDimension('ensemble', self.n_ens)
00936
00937
00938 varDims = 'depth',
00939 depth = file.createVariable('depth', 'd', varDims)
00940 depth.units = 'm'
00941 depth[:] = self.z
00942
00943
00944 varDims = 'time',
00945 self.nc_year = file.createVariable('year', 'd', varDims)
00946 self.nc_doy = file.createVariable('doy', 'd', varDims)
00947
00948
00949 varDims = 'ensemble', 'depth', 'time'
00950 self.nc_sm = file.createVariable('sm','d', varDims)
00951 self.nc_sm.units = 'v/v'
00952 self.nc_sm[:,:,0] = self.sm_ens
00953
00954
00955 varDims = 'time',
00956 self.nc_aet = file.createVariable('aet','d',varDims)
00957 self.nc_aet.units = 'mm'
00958 self.nc_recharge = file.createVariable('recharge','d',varDims)
00959 self.nc_recharge.units = 'mm'
00960
00961
00962 varDims = 'ensemble', 'time'
00963 self.nc_gw_level_ens = file.createVariable('gw_level_ens','d',varDims)
00964 self.nc_gw_level_ens.units = 'm'
00965 self.nc_gw_level_ens[:,0] = self.gw_level_ens[:,0]
00966
00967
00968 varDims = 'ensemble','time'
00969 self.nc_qr = file.createVariable('qr','d',varDims)
00970 self.nc_qr.units = 'v/v'
00971 self.nc_f = file.createVariable('f','d',varDims)
00972 self.nc_f.units = 'v/v'
00973 self.nc_a = file.createVariable('a','d',varDims)
00974 self.nc_a.units = '1/m'
00975 self.nc_n = file.createVariable('n','d',varDims)
00976 self.nc_n.units = '-'
00977 self.nc_Ks = file.createVariable('Ks','d',varDims)
00978 self.nc_Ks.units = 'm/s'
00979 self.nc_l = file.createVariable('l','d',varDims)
00980 self.nc_l.units = '-'
00981
00982 self.nc_file = file
00983
00984
00985 self._generate_soil_par_ens()
00986
00987
00988
00989 def _generate_soil_par_ens(self):
00990 """
00991 this uses the LHS to generate the ensemble of the parameters
00992
00993 this also computes the perturbation needed to perturb the parameters
00994 which is done in another function
00995 """
00996
00997
00998 v = np.random.normal(size=(self.n_ens,6))
00999 v = v-np.tile(v.mean(axis=0),(self.n_ens,1))
01000 qr = self.shp_ens['qr'] + v[:,0]*self.shp_ens['qr']*0.1
01001 f = self.shp_ens['f'] + v[:,1]*self.shp_ens['f']*0.1
01002 a = self.shp_ens['a'] + v[:,2]*self.shp_ens['a']*0.1
01003 n = self.shp_ens['n'] + v[:,3]*self.shp_ens['n']*0.1
01004 Ks = self.shp_ens['Ks'] + v[:,4]*self.shp_ens['Ks']*0.1
01005 l = self.shp_ens['l'] + v[:,5]*self.shp_ens['l']*0.1
01006
01007
01008 qr[qr>self.qr_max] = self.qr_max
01009 f[f>self.f_max] = self.f_max
01010 a[a>self.a_max] = self.a_max
01011 n[n>self.n_max] = self.n_max
01012 Ks[Ks>self.Ks_max] = self.Ks_max
01013 l[l>self.l_max] = self.l_max
01014
01015 qr[qr<self.qr_min] = self.qr_min
01016 f[f<self.f_min] = self.f_min
01017 a[a<self.a_min] = self.a_min
01018 n[n<self.n_min] = self.n_min
01019 Ks[Ks<self.Ks_min] = self.Ks_min
01020 l[l<self.l_min] = self.l_min
01021
01022 soil_par_ens = {}
01023 soil_par_ens['qr'] = qr
01024 soil_par_ens['f'] = f
01025 soil_par_ens['a'] = a
01026 soil_par_ens['n'] = n
01027 soil_par_ens['Ks'] = Ks
01028 soil_par_ens['l'] = l
01029
01030 self.soil_par_ens = soil_par_ens
01031
01032
01033 soil_pert = {}
01034 soil_pert['qr'] = (self.qr_max - self.qr_min)*0.1/100.0
01035 soil_pert['f'] = (self.f_max - self.f_min)*0.1/100.0
01036 soil_pert['a'] = (self.a_max - self.a_min)*0.1/100.0
01037 soil_pert['n'] = (self.n_max - self.n_min)*0.1/100.0
01038 soil_pert['Ks'] = (self.Ks_max - self.Ks_min)*0.1/100.0
01039 soil_pert['l'] = (self.l_max - self.l_min)*0.1/100.0
01040
01041 self.soil_pert = soil_pert
01042
01043 def _read_input(self):
01044 """
01045 This checks if all the required input sheets are present in the xls file,
01046 read the data from input file, which can be used later in other functions
01047 """
01048
01049
01050 input_sheets = ['ind', 'forcing', 'initial_condition', 'gw_par',
01051 'runoff_par', 'units', 'root_info', 'temporal_info',
01052 'spatial_info', 'ET_par', 'soil_hyd_par', 'output_par']
01053
01054
01055 self._check_sheets(input_sheets, self.input_file)
01056
01057
01058 self._read_ind()
01059
01060
01061 self._read_spatial()
01062
01063
01064 self._read_temporal()
01065
01066
01067 self._read_root_distribution()
01068
01069
01070 self._read_units()
01071
01072
01073 self._read_initial_condition()
01074
01075
01076 self._read_shp_ens()
01077
01078
01079 self._read_runoff_par()
01080
01081
01082 self._surface_storage_par()
01083
01084
01085 self._read_gw_par()
01086
01087
01088 self._read_ET_par()
01089
01090
01091 self._read_forcing()
01092
01093
01094 self._read_ofile_name()
01095
01096
01097 output_message = 'Input data reading completed sucessfully'
01098 self._colored_output(output_message, 32)
01099
01100 def _read_initial_condition(self):
01101 """
01102 read initial condition
01103 """
01104
01105 j = self.ind['initial_condition']
01106
01107 book = xlrd.open_workbook(self.input_file)
01108 sheet = book.sheet_by_name('initial_condition')
01109 theta_0 = sheet.row_values(j,2)
01110 self.initial_gwl = sheet.cell_value(j,1)
01111 self.initial_sm = np.array(theta_0)
01112
01113
01114
01115 def _read_shp_ens(self):
01116 """
01117 read the soil hydraulic parameters
01118 """
01119
01120 j = self.ind['soil_hyd_par']
01121
01122 book = xlrd.open_workbook(self.input_file)
01123 sheet = book.sheet_by_name('soil_hyd_par')
01124 shp_ens = {}
01125 shp_ens['qr'] = sheet.cell_value(j,1)
01126 shp_ens['f'] = sheet.cell_value(j,2)
01127 shp_ens['a'] = sheet.cell_value(j,3)
01128 shp_ens['n'] = sheet.cell_value(j,4)
01129 shp_ens['Ks'] = sheet.cell_value(j,5)
01130 shp_ens['l'] = sheet.cell_value(j,6)
01131
01132
01133 shp_ens['zl'] = sheet.cell_value(j,9)
01134 shp_ens['fl'] = sheet.cell_value(j,10)
01135
01136 m = 1-1/shp_ens['n']
01137
01138 shp_ens['evap_fc'] = self.psi2theta(-0.33, shp_ens['qr'], shp_ens['f'],
01139 shp_ens['a'], m, shp_ens['n'])
01140
01141 shp_ens['evap_wp'] = self.psi2theta(-15, shp_ens['qr'], shp_ens['f'],
01142 shp_ens['a'], m, shp_ens['n'])
01143
01144 self.shp_ens = shp_ens
01145
01146
01147 self.qr_min, self.qr_max = 0.02, 0.12
01148 self.f_min, self.f_max = 0.25, 0.4
01149 self.a_min, self.a_max = 2, 5
01150 self.n_min, self.n_max = 1.4, 2.4
01151 self.Ks_min, self.Ks_max = 1e-6, 1e-5
01152 self.l_min, self.l_max = 0.4, 0.6
01153
01154
01155 def _perturb_soil_par_ens(self):
01156 """
01157 this functions perturb the soil hydraulic parameters
01158 using the gaussian random variables
01159 """
01160 v = np.random.normal(size=(self.n_ens,6))
01161 v = v-np.tile(v.mean(axis=0),(self.n_ens,1))
01162 qr = self.soil_par_ens['qr']+self.soil_pert['qr']*v[:,0]
01163 f = self.soil_par_ens['f']+self.soil_pert['f']*v[:,1]
01164 a = self.soil_par_ens['a']+self.soil_pert['a']*v[:,2]
01165 n = self.soil_par_ens['n']+self.soil_pert['n']*v[:,3]
01166 Ks = self.soil_par_ens['Ks']+self.soil_pert['Ks']*(v[:,4]-v[:,4].mean())
01167 l = self.soil_par_ens['l']+self.soil_pert['l']*v[:,5]
01168
01169
01170
01171 qr[qr>self.qr_max] = self.qr_max
01172 f[f>self.f_max] = self.f_max
01173 a[a>self.a_max] = self.a_max
01174 n[n>self.n_max] = self.n_max
01175 Ks[Ks>self.Ks_max] = self.Ks_max
01176 l[l>self.l_max] = self.l_max
01177
01178 qr[qr<self.qr_min] = self.qr_min
01179 f[f<self.f_min] = self.f_min
01180 a[a<self.a_min] = self.a_min
01181 n[n<self.n_min] = self.n_min
01182 Ks[Ks<self.Ks_min] = self.Ks_min
01183 l[l<self.l_min] = self.l_min
01184
01185 soil_par_ens = {}
01186 soil_par_ens['qr'] = qr
01187 soil_par_ens['f'] = f
01188 soil_par_ens['a'] = a
01189 soil_par_ens['n'] = n
01190 soil_par_ens['Ks'] = Ks
01191 soil_par_ens['l'] = l
01192 m = 1-1/n
01193 soil_par_ens['evap_fc'] = self.psi2theta(-0.33, qr, f, a, m, n)
01194
01195 soil_par_ens['evap_wp'] = self.psi2theta(-15, qr, f, a, m, n)
01196 self.soil_par_ens = soil_par_ens
01197
01198 fl = self.shp_ens['fl']
01199 mid_z = self.mid_z
01200 ET_par = {}
01201 ET_par['trans_fc'] = self.psi2theta(-0.33, qr, f, a, m, n)
01202 ET_par['trans_wp'] = self.psi2theta(-15, qr, f, a, m, n)
01203
01204 self.ET_par = ET_par
01205
01206 def _runoff_ens_fun(self):
01207 """
01208 this module will calculate the runoff based on the initial soil moisture
01209 and net precipitation
01210
01211 Input:
01212 C: Average soil moisture
01213 Pn: Precipitation after interception loss
01214 runoff_par: runoff parameters ['Cm','B']
01215
01216 Output:
01217 runoff_cur: Runoff at current time step
01218 """
01219
01220
01221
01222
01223
01224 ens = self.ens
01225
01226 Kdt_ref = 3.0
01227 Kref = 2e-6
01228 theta_s = self.soil_par_ens['f'][ens]*np.exp(-self.mid_z/self.shp_ens['fl'])
01229
01230 Dx = theta_s - self.sm_ens[ens]
01231 Dx = Dx*self.mid_z
01232 Dx = Dx[:3].sum()
01233 Kdt = Kdt_ref*self.soil_par_ens['Ks'][ens]/Kref
01234
01235 Pn = self.net_rain_cur
01236 Imax = Pn*(Dx*(1-np.exp(-Kdt)))/(Pn+Dx*(1-np.exp(-Kdt)))
01237
01238 self.runoff_cur = Pn - Imax
01239 self.runoff = 1.0*self.runoff_cur
01240
01241 def _interception_ens_fun(self):
01242 """
01243 Input:
01244 lai_cur: LAI at the current time step
01245 pet_cur: PET at the current time step
01246 rain_cur: Rainfall at the current time step
01247
01248 Output:
01249 E_In: Evaporation from Interception
01250 T: Transpiration
01251 E: Evaporation
01252 net_rain_cur: Net rainfall (precipitation-interception loss) at
01253 current time step
01254 """
01255 In = self.lai[self.t]*0.2/1000.0
01256 soil_cover = np.exp(-0.5*self.lai_cur)
01257 veg_cover = 1 - soil_cover
01258
01259 E_In = np.min([veg_cover*self.rain_cur, veg_cover*self.pet_cur, veg_cover*In])
01260 T = np.min([veg_cover*self.pet_cur - 0.2*E_In, 1.2*self.pet_cur - E_In])
01261 E = np.min([soil_cover*self.pet_cur, 1.2*self.pet_cur-T-E_In])
01262 net_rain_cur = self.rain_cur - E_In
01263
01264
01265 self.E_In = E_In
01266 self.trans = T
01267 self.evap = E
01268 self.net_rain_cur = net_rain_cur
01269
01270 def _soil_ens_fun(self):
01271 """
01272 Input:
01273 soil_par : soil hydraulic parameters
01274 z : thicknes of layers
01275 R : runoff
01276 no_layer : no. of layers
01277 theta_0 : initial soil moisture
01278 root_frac : root fraction in each layer
01279 T : transpiration
01280 Pn : net precipitation (precipitation - interception)
01281 E : soil evaporation
01282 Pu : pumping
01283 dt : time step
01284 Output:
01285 theta_1: soil moisture for next time step
01286 Re: recharge (L)
01287 """
01288
01289 self.runoff_cur /= self.dt
01290 self.evap /= self.dt
01291 self.trans /= self.dt
01292 self.net_rain_cur /= self.dt
01293 self.pumping_cur /= self.dt
01294
01295
01296 theta_1_mat = np.zeros(self.no_layer)
01297
01298
01299 ens = self.ens
01300 sm = self.sm_ens[ens]
01301
01302
01303 K = np.zeros((self.no_layer,1))
01304 D = np.zeros((self.no_layer,1))
01305 for i in range(self.no_layer):
01306 if i<self.no_layer-1:
01307
01308
01309
01310 K[i], D[i] = self._shp(0.5*(sm[i]+sm[i+1]),i)
01311 else:
01312 K[i], D[i] = self._shp(sm[i],i)
01313 K = K.flatten()*np.exp(-self.mid_z/self.shp_ens['zl'])
01314 D = D.flatten()*np.exp(-self.mid_z/self.shp_ens['zl'])
01315
01316
01317
01318 self._smi_fun()
01319 AE = self.evap*self.SSMI
01320 self._transpiration_fun()
01321 AT = self.AT
01322
01323
01324 A = np.zeros((self.no_layer, self.no_layer))
01325 U = np.zeros((self.no_layer,1))
01326 z = self.z
01327 for i in range(self.no_layer):
01328 if i == 0:
01329 A[0,0] = -D[1]/(0.5*z[1]*(z[1]+z[2]))
01330 A[0,1] = D[1]/(0.5*z[1]*(z[1]+z[2]))
01331 U[0] = (-AT[0] - K[0] + self.net_rain_cur - AE - self.runoff_cur \
01332 + self.pumping_cur)/z[0]
01333 elif i == self.no_layer-1:
01334 A[i,i] = -D[i-1]/(0.5*z[i]*(z[i-1]+z[i]))
01335 A[i,i-1] = D[i-1]/(0.5*z[i]*(z[i-1]+z[i]))
01336 U[i] = (-AT[i] + K[i-1] - K[i])/z[i]
01337 else:
01338 A[i,i-1] = D[i-1]/(0.5*z[i]*(z[i-1]+z[i]))
01339 A[i,i+1] = D[i]/(0.5*z[i]*(z[i]+z[i+1]))
01340 A[i,i] = -A[i,i-1] -A[i,i+1]
01341 U[i] = (-AT[i] +K[i-1] - K[i])/z[i]
01342
01343
01344 F = np.eye(self.no_layer) + A*self.dt
01345
01346
01347 G = np.dot(F,U)*self.dt
01348
01349
01350 theta_1 = np.dot(F, sm) + G.flatten()
01351
01352
01353 Re = K[-1]*self.dt
01354
01355
01356
01357 if theta_1[0] >= self.soil_par_ens['f'][ens]:
01358 HR = (theta_1[0]-self.soil_par_ens['f'][ens])*z[0]
01359 theta_1[0] = self.soil_par_ens['f'][ens]
01360 else:
01361 HR = 0
01362
01363
01364 theta_s = self.soil_par_ens['f'][ens]*np.exp(-self.mid_z/self.shp_ens['fl'])
01365 wp = self.ET_par['trans_wp'][ens]*np.exp(-self.mid_z/self.shp_ens['fl'])
01366 for j in range(self.no_layer):
01367 if theta_1[j]>theta_s[j]:
01368 theta_1[j] = theta_s[j]
01369 if theta_1[j]<wp[j]:
01370 theta_1[j] = wp[j]
01371
01372
01373 self.sm_ens[ens] = theta_1.flatten()
01374 self.G = G
01375 self.F = F
01376 self.theta_1 = theta_1
01377 self.recharge = Re
01378 self.actual_evap = AE*self.dt
01379 self.actual_trans = AT.sum()*self.dt
01380 self.horton_runoff = HR
01381
01382 def _smi_fun(self):
01383 """
01384 this module computes the surface soil moisture stress index, and root zone soil moisture
01385 stress index
01386 """
01387 ens = self.ens
01388 sm = self.sm_ens[ens]
01389
01390 SSMI = (sm[0] - self.soil_par_ens['evap_wp'][ens])/(
01391 self.soil_par_ens['evap_fc'][ens] - self.soil_par_ens['evap_wp'][ens])
01392
01393 if SSMI > 1:
01394 SSMI = 1
01395 elif SSMI<0:
01396 SSMI = 0
01397
01398
01399 RZSMI = np.zeros((self.no_layer,))
01400 mid_z = self.mid_z
01401 fl = self.shp_ens['fl']
01402
01403 for i in range(self.no_layer):
01404 trans_wp = self.ET_par['trans_wp'][ens]*np.exp(-mid_z/fl)[i]
01405 trans_fc = self.ET_par['trans_fc'][ens]*np.exp(-mid_z/fl)[i]
01406 if (sm[i] < trans_wp):
01407 RZSMI[i] = 0
01408 elif sm[i] > trans_fc:
01409 RZSMI[i] = 1
01410 else:
01411
01412 RZSMI[i] = (sm[i]-trans_wp)/(trans_fc - trans_wp)
01413
01414
01415 self.SSMI = SSMI
01416 self.RZSMI = RZSMI
01417
01418 def _surface_storage_ens_fun(self):
01419 """
01420 this module stores the surface water
01421 and give as recharge to the groundwater model
01422 """
01423 ens = self.ens
01424
01425 surface_storage = self.surface_storage_ens[ens,self.t] \
01426 + self.runoff_cur \
01427 + self.horton_runoff
01428 a = 1.0*self.surface_storage_par['a']
01429 b = 1.0*self.surface_storage_par['b']
01430 Rep = a*surface_storage**b
01431
01432
01433 self.surface_storage_ens[ens,self.t+1] = surface_storage - Rep
01434 self.Rep = Rep
01435
01436 def _gw_ens_fun(self):
01437 """
01438 Groundwater module
01439 """
01440 ens = self.ens
01441 F = self.gw_par['F']
01442 G = self.gw_par['G']
01443 hmin = self.gw_par['hmin']
01444
01445 self.sy = F/G
01446 self.lam = (1-F)*self.sy
01447
01448
01449 u = self.recharge-self.pumping_cur + self.Rep
01450 self.gw_level_ens[ens,self.t+1] = F*(self.gw_level_ens[ens,self.t]-hmin) + G*u + hmin
01451
01452 dzn = self.gw_level_ens[ens,self.t+1] - self.gw_level_ens[ens,self.t]
01453 self.discharge = u - self.sy*(dzn)
01454 self.z_ens[ens,-1] = self.z_ens[ens,-1] - dzn
01455
01456 def _read_spatial(self):
01457 """
01458 Read the spatial info
01459 """
01460 book = xlrd.open_workbook(self.input_file)
01461 sheet = book.sheet_by_name('spatial_info')
01462
01463 j = self.ind['spatial_info']
01464 no_layer = int(sheet.cell_value(j,1))
01465 z = sheet.row_values(j,2)
01466 if no_layer != len(z):
01467 raise ValueError('The length of the thickness_layers\
01468 should be equal to the No_layer')
01469
01470 self.no_layer = no_layer
01471 self.z = z
01472 self.z_ens = np.tile(z,(self.n_ens,1))
01473
01474
01475 depth = np.zeros(no_layer+1)
01476 depth[1:] = np.cumsum(z)
01477 self.mid_z = 0.5*(depth[1:]+depth[:-1])
01478
01479 def _enkf_par(self):
01480 """
01481 ensemble kalman filter
01482 """
01483
01484
01485 x = self.sm_ens + 0.005*np.random.normal(size=self.no_layer)
01486 qr = self.soil_par_ens['qr']
01487 f = self.soil_par_ens['f']
01488 a = self.soil_par_ens['a']
01489 n = self.soil_par_ens['n']
01490 Ks = self.soil_par_ens['Ks']
01491 l = self.soil_par_ens['l']
01492 soil_par = (np.vstack([qr, f, a, n, Ks, l])).T
01493 X = np.hstack([x, soil_par])
01494
01495
01496 X_bar = np.tile(X.mean(axis=0),(10,1))
01497 X_X_bar = X-X_bar
01498 cov_XX = np.dot(X_X_bar.T,X_X_bar) + 1e-6*np.eye(self.no_layer+6)
01499 cov_XX = 0.5*(cov_XX + cov_XX.T)
01500
01501
01502
01503 e = np.zeros((self.n_ens, self.no_layer+6))
01504 if np.isnan(self.meas_sm_mean[self.t-1]):
01505 e[:,0] = 0
01506 v = 0.01*np.random.normal(size=(self.n_ens,self.no_layer+6))
01507 else:
01508 e[:,0] = self.meas_sm_mean[self.t-1] - self.sm_ens[:,0].mean()
01509 v = self.meas_sm_std[self.t-1]*np.random.normal(size=(self.n_ens,self.no_layer+6))
01510
01511 v = v-np.tile(v.mean(axis=0),(self.n_ens,1))
01512 ev = e + v
01513 cov_ee = np.dot(ev.T, ev) + 1e-6*np.eye(self.no_layer+6)
01514 cov_ee = 0.5*(cov_ee + cov_ee.T)
01515
01516
01517 K = np.dot(cov_XX, np.linalg.pinv(cov_XX+cov_ee))
01518
01519
01520 v = np.random.normal(size=(self.n_ens,))
01521 v = v-v.mean()
01522 e[:,0] = e[:,0]+0.005*v
01523 K = 0.5*(K + K.T)
01524 usm_par = X + np.dot(K,e.T).T
01525 temp = np.dot(K,e.T).T
01526
01527 self.usm_par = usm_par
01528
01529
01530
01531
01532 sm_ens = usm_par[:,:self.no_layer]
01533 sm_ens[sm_ens<0] = 0
01534 sm_ens[sm_ens>1] = 1
01535 v = 0.001*np.random.normal(size=(sm_ens.shape))
01536 v = v-np.tile(v.mean(axis=0),(self.n_ens,1))
01537 self.sm_ens = sm_ens + v
01538
01539 qr = usm_par[:,self.no_layer+0]
01540 f = usm_par[:,self.no_layer+1]
01541 a = usm_par[:,self.no_layer+2]
01542 n = usm_par[:,self.no_layer+3]
01543 Ks = usm_par[:,self.no_layer+4]
01544 l = usm_par[:,self.no_layer+5]
01545
01546
01547 qr[qr>self.qr_max] = self.qr_max
01548 f[f>self.f_max] = self.f_max
01549 a[a>self.a_max] = self.a_max
01550 n[n>self.n_max] = self.n_max
01551 Ks[Ks>self.Ks_max] = self.Ks_max
01552 l[l>self.l_max] = self.l_max
01553
01554 qr[qr<self.qr_min] = self.qr_min
01555 f[f<self.f_min] = self.f_min
01556 a[a<self.a_min] = self.a_min
01557 n[n<self.n_min] = self.n_min
01558 Ks[Ks<self.Ks_min] = self.Ks_min
01559 l[l<self.l_min] = self.l_min
01560
01561
01562 self.soil_par_ens['qr'] = qr
01563 self.soil_par_ens['f'] = f
01564 self.soil_par_ens['a'] = a
01565 self.soil_par_ens['n'] = n
01566 self.soil_par_ens['Ks'] = Ks
01567 self.soil_par_ens['l'] = l
01568
01569 self.K = K
01570 self.cov_ee = cov_ee
01571 self.cov_XX = cov_XX
01572
01573 def _enkf_ET(self):
01574 """
01575 ensemble kalman filter
01576 """
01577
01578
01579 x = self.sm_ens + 0.005*np.random.normal(size=self.no_layer)
01580 qr = self.soil_par_ens['qr']
01581 f = self.soil_par_ens['f']
01582 a = self.soil_par_ens['a']
01583 n = self.soil_par_ens['n']
01584 Ks = self.soil_par_ens['Ks']
01585 l = self.soil_par_ens['l']
01586 soil_par = (np.vstack([qr, f, a, n, Ks, l])).T
01587 X = np.hstack([x, soil_par])
01588
01589
01590 X_bar = np.tile(X.mean(axis=0),(10,1))
01591 X_X_bar = X-X_bar
01592 cov_XX = np.dot(X_X_bar.T,X_X_bar) + 1e-6*np.eye(self.no_layer+6)
01593 cov_XX = 0.5*(cov_XX + cov_XX.T)
01594
01595
01596
01597 err_aet = np.zeros(self.n_ens)
01598 for ens in range(self.n_ens):
01599 self.ens = ens
01600 self._smi_fun()
01601 AE = self.evap*self.SSMI
01602 self._transpiration_fun()
01603 AT = self.AT
01604
01605 err_aet[ens] = self.meas_aet[self.t] - AE - AT.sum() - self.E_In
01606 err_ae = err_aet.mean()*AE/(AE+AT.sum())
01607 err_at = err_aet.mean()*AT.sum()/(AE+AT.sum())
01608
01609 e = np.zeros((self.n_ens, self.no_layer+6))
01610 e[:,0] = (err_ae + err_at*self.r_density[0])/self.z[0]
01611 e[:,1] = err_at*self.r_density[1]/self.z[1]
01612 e[:,2] = err_at*self.r_density[2]/self.z[2]
01613 e[:,3] = err_at*self.r_density[3]/self.z[3]
01614 e[:,4] = err_at*self.r_density[4]/self.z[4]
01615 v = 0.03*np.random.normal(size=(self.n_ens,self.no_layer+6))
01616
01617 v = v-np.tile(v.mean(axis=0),(self.n_ens,1))
01618 ev = e + v
01619 cov_ee = np.dot(ev.T, ev) + 1e-6*np.eye(self.no_layer+6)
01620 cov_ee = 0.5*(cov_ee + cov_ee.T)
01621
01622
01623 print self.t, e.max(), self.z[4]
01624
01625 K = np.dot(cov_XX, np.linalg.pinv(cov_XX+cov_ee))
01626
01627
01628 v = np.random.normal(size=(self.n_ens,))
01629 v = v-v.mean()
01630 e[:,0] = e[:,0]+0.005*v
01631 K = 0.5*(K + K.T)
01632 usm_par = X + np.dot(K,e.T).T
01633 temp = np.dot(K,e.T).T
01634
01635 self.usm_par = usm_par
01636
01637
01638
01639
01640 sm_ens = usm_par[:,:self.no_layer]
01641 sm_ens[sm_ens<0] = 0
01642 sm_ens[sm_ens>1] = 1
01643 v = 0.001*np.random.normal(size=(sm_ens.shape))
01644 v = v-np.tile(v.mean(axis=0),(self.n_ens,1))
01645 self.sm_ens = sm_ens + v
01646
01647 qr = usm_par[:,self.no_layer+0]
01648 f = usm_par[:,self.no_layer+1]
01649 a = usm_par[:,self.no_layer+2]
01650 n = usm_par[:,self.no_layer+3]
01651 Ks = usm_par[:,self.no_layer+4]
01652 l = usm_par[:,self.no_layer+5]
01653
01654
01655 qr[qr>self.qr_max] = self.qr_max
01656 f[f>self.f_max] = self.f_max
01657 a[a>self.a_max] = self.a_max
01658 n[n>self.n_max] = self.n_max
01659 Ks[Ks>self.Ks_max] = self.Ks_max
01660 l[l>self.l_max] = self.l_max
01661
01662 qr[qr<self.qr_min] = self.qr_min
01663 f[f<self.f_min] = self.f_min
01664 a[a<self.a_min] = self.a_min
01665 n[n<self.n_min] = self.n_min
01666 Ks[Ks<self.Ks_min] = self.Ks_min
01667 l[l<self.l_min] = self.l_min
01668
01669
01670 self.soil_par_ens['qr'] = qr
01671 self.soil_par_ens['f'] = f
01672 self.soil_par_ens['a'] = a
01673 self.soil_par_ens['n'] = n
01674 self.soil_par_ens['Ks'] = Ks
01675 self.soil_par_ens['l'] = l
01676
01677 self.K = K
01678 self.cov_ee = cov_ee
01679 self.cov_XX = cov_XX
01680
01681 def _read_forcing(self):
01682 """
01683 read the forcing data from xls file
01684 """
01685 book = xlrd.open_workbook(self.input_file)
01686 sheet = book.sheet_by_name('forcing')
01687
01688 data_len = sheet.nrows-1
01689 year = np.zeros(data_len)
01690 doy = np.zeros(data_len)
01691 rain = np.zeros(data_len)
01692 pet = np.zeros(data_len)
01693 ndvi = np.zeros(data_len)
01694 pumping = np.zeros(data_len)
01695 meas_sm_mean = np.zeros(data_len)
01696 meas_sm_std = np.zeros(data_len)
01697 meas_aet = np.zeros(data_len)
01698
01699 for i in xrange(data_len):
01700 year[i] = sheet.cell_value(i+1,0)
01701 doy[i] = sheet.cell_value(i+1,1)
01702 rain[i] = sheet.cell_value(i+1,2)
01703 pet[i] = sheet.cell_value(i+1,3)
01704 ndvi[i] = sheet.cell_value(i+1,4)
01705 pumping[i] = sheet.cell_value(i+1,5)
01706 meas_sm_mean[i] = sheet.cell_value(i+1,6)
01707 meas_sm_std[i] = sheet.cell_value(i+1,7)
01708 meas_aet[i] = sheet.cell_value(i+1,8)/1000.0
01709
01710 self.year = year
01711 self.doy = doy
01712 self.meas_sm_mean = meas_sm_mean
01713 self.meas_sm_std = meas_sm_std
01714 self.meas_aet = meas_aet
01715
01716
01717 if self.forcing_units['rain'] == 'mm':
01718 self.rain = rain/1000.0
01719 elif self.forcing_units['rain'] == 'm':
01720 self.rain = rain
01721 else:
01722 raise ValueError("The units of rain should be either 'mm' or 'm' ")
01723
01724 if self.forcing_units['pet'] == 'mm':
01725 self.pet = pet/1000.0
01726 elif self.forcing_units['pet'] == 'm':
01727 self.pet = pet
01728 else:
01729 raise ValueError("The units of PET should be either 'mm' or 'm' ")
01730
01731 if self.forcing_units['pumping'] == 'mm':
01732 self.pumping = pumping/1000.0
01733 elif self.forcing_units['pumping'] == 'm':
01734 self.pumping = pumping
01735 else:
01736 raise ValueError("The units of pumping should be either 'mm' or 'm' ")
01737
01738
01739 ndvi_max = self.ndvi_max
01740 ndvi_min = self.ndvi_min
01741 ndvi[ndvi>ndvi_max] = ndvi_max
01742 ndvi[ndvi<ndvi_min] = ndvi_min
01743
01744 fapar = 1.60*ndvi-0.02
01745 fapar_max = self.fapar_max
01746
01747 lai_max = self.lai_max
01748 lai = lai_max*np.log(1-fapar)/np.log(1-fapar_max)
01749
01750 Rd_max = self.Rd_max
01751 Rd = Rd_max*lai/lai_max
01752 fc = ((ndvi-ndvi_max)/(ndvi_max-ndvi_min))**2
01753
01754 self.kc = 0.8+0.4*(1-np.exp(-0.7*lai))
01755 self.ndvi = ndvi
01756 self.lai = lai
01757 self.Rd = Rd
01758 self.fc = fc
01759
01760 def _write_output(self):
01761 """
01762 this functions writes the output at each time step
01763 """
01764
01765 self.nc_year[self.t] = (self.cur_year)
01766 self.nc_doy[self.t] = (self.cur_doy)
01767 self.nc_sm[:,:,self.t+1] = self.sm_ens
01768 self.nc_gw_level_ens[:,self.t+1] = self.gw_level_ens[:,self.t+1]
01769
01770
01771 self.nc_qr[:,self.t] = self.soil_par_ens['qr']
01772 self.nc_f[:,self.t] = self.soil_par_ens['f']
01773 self.nc_a[:,self.t] = self.soil_par_ens['a']
01774 self.nc_n[:,self.t] = self.soil_par_ens['n']
01775 self.nc_Ks[:,self.t] = self.soil_par_ens['Ks']
01776 self.nc_l[:,self.t] = self.soil_par_ens['l']
01777
01778
01779 def _shp(self, theta,i):
01780 """
01781 soil hydraulic properties module
01782 i is the layer
01783 """
01784 ens = self.ens
01785 qr = self.soil_par_ens['qr'][ens]*np.exp(-self.mid_z[i]/self.shp_ens['fl'])
01786 f = self.soil_par_ens['f'][ens]*np.exp(-self.mid_z[i]/self.shp_ens['fl'])
01787 a = self.soil_par_ens['a'][ens]
01788 n = self.soil_par_ens['n'][ens]
01789 Ks = self.soil_par_ens['Ks'][ens]
01790 l = self.soil_par_ens['l'][ens]
01791
01792 m = 1-1/n
01793 Se = (theta-qr)/(f - qr)
01794 if Se>=0.99:
01795 Se = 0.99
01796 elif Se<=0.01:
01797 Se = 0.01
01798 K = Ks*Se**l*(1-(1-Se**(1/m))**m)**2
01799 D = K/(a*(f-qr)*m*n*(Se**(1/m+1))*(Se**(-1/m)-1)**m)
01800 return K, D
01801
01802 def _read_ET_par(self):
01803 """
01804 read the parameters related to evaporation
01805 """
01806
01807
01808
01809
01810
01811 ET_par = {}
01812
01813
01814 qr = self.shp_ens['qr']
01815 f = self.shp_ens['f']
01816 a = self.shp_ens['a']
01817 n = self.shp_ens['n']
01818 m = 1-1/n
01819 fl = self.shp_ens['fl']
01820 mid_z = self.mid_z
01821 ET_par['trans_fc'] = self.psi2theta(-0.33, qr, f, a, m, n)*np.exp(-mid_z/fl)
01822 ET_par['trans_wp'] = self.psi2theta(-15, qr, f, a, m, n)*np.exp(-mid_z/fl)
01823
01824 self.ET_par = ET_par
01825
01826 if __name__=='__main__':
01827
01828
01829 berambadi_enkf = CSGLM_ENKF('/home/tomer/csglm/input/berambadi_enkf.xls')
01830
01831
01832
01833
01834
01835
01836
01837
01838
01839
01840