def Dist_HBV2(ConceptualModel, lakecell, q_lake, DEM, flow_acc, flow_acc_plan, sp_prec, sp_et, sp_temp, sp_pars, p2, init_st=None, ll_temp=None, q_0=None): """ original function """ n_steps = sp_prec.shape[ 2] + 1 # no of time steps =length of time series +1 # intiialise vector of nans to fill states dummy_states = np.empty([n_steps, 5]) # [sp,sm,uz,lz,wc] dummy_states[:] = np.nan # Get the mask mask, no_val = raster.get_mask(DEM) # shape of the fpl raster (rows, columns)-------------- rows are x and columns are y x_ext, y_ext = mask.shape # y_ext, x_ext = mask.shape # shape of the fpl raster (rows, columns)------------ should change rows are y and columns are x # Get deltas of pixel geo_trans = DEM.GetGeoTransform( ) # get the coordinates of the top left corner and cell size [x,dx,y,dy] dx = np.abs(geo_trans[1]) / 1000.0 # dx in Km dy = np.abs(geo_trans[-1]) / 1000.0 # dy in Km px_area = dx * dy # area of the cell # Enumerate the total number of pixels in the catchment tot_elem = np.sum( np.sum([ [1 for elem in mask_i if elem != no_val] for mask_i in mask ])) # get row by row and search [mask_i for mask_i in mask] # total pixel area px_tot_area = tot_elem * px_area # total area of pixels # Get number of non-value data st = [] # Spatially distributed states qlz = [] quz = [] #------------------------------------------------------------------------------ for x in range(x_ext): # no of rows st_i = [] q_lzi = [] q_uzi = [] # q_out_i = [] # run all cells in one row ---------------------------------------------------- for y in range(y_ext): # no of columns if mask[x, y] != no_val: # only for cells in the domain # Calculate the states per cell # TODO optimise for multiprocessing these loops # _, _st, _uzg, _lzg = ConceptualModel.simulate_new_model(avg_prec = sp_prec[x, y,:], _, _st, _uzg, _lzg = ConceptualModel.Simulate( prec=sp_prec[x, y, :], temp=sp_temp[x, y, :], et=sp_et[x, y, :], par=sp_pars[x, y, :], p2=p2, init_st=init_st, ll_temp=None, q_0=q_0, snow=0) #extra_out = True # append column after column in the same row ----------------- st_i.append(np.array(_st)) #calculate upper zone Q = K1*(LZ_int_1) q_lz_temp = np.array(sp_pars[x, y, 6]) * _lzg q_lzi.append(q_lz_temp) # calculate lower zone Q = k*(UZ_int_3)**(1+alpha) q_uz_temp = np.array(sp_pars[x, y, 5]) * (np.power( _uzg, (1.0 + sp_pars[x, y, 7]))) q_uzi.append(q_uz_temp) #print("total = "+str(fff)+"/"+str(tot_elem)+" cell, row= "+str(x+1)+" column= "+str(y+1) ) else: # if the cell is novalue------------------------------------- # Fill the empty cells with a nan vector st_i.append( dummy_states ) # fill all states(5 states) for all time steps = nan q_lzi.append( dummy_states[:, 0] ) # q lower zone =nan for all time steps = nan q_uzi.append( dummy_states[:, 0] ) # q upper zone =nan for all time steps = nan # store row by row-------- ---------------------------------------------------- #st.append(st_i) # state variables st.append(st_i) # state variables qlz.append(np.array(q_lzi)) # lower zone discharge mm/timestep quz.append( np.array(q_uzi)) # upper zone routed discharge mm/timestep #------------------------------------------------------------------------------ # convert to arrays st = np.array(st) qlz = np.array(qlz) quz = np.array(quz) #%% convert quz from mm/time step to m3/sec area_coef = p2[1] / px_tot_area quz = quz * px_area * area_coef / (p2[0] * 3.6) no_cells = list( set([ flow_acc_plan[i, j] for i in range(x_ext) for j in range(y_ext) if not np.isnan(flow_acc_plan[i, j]) ])) # no_cells=list(set([int(flow_acc_plan[i,j]) for i in range(x_ext) for j in range(y_ext) if flow_acc_plan[i,j] != no_val])) no_cells.sort() #%% routing lake discharge with DS cell k & x and adding to cell Q q_lake = routing.Muskingum_V(q_lake, q_lake[0], sp_pars[lakecell[0], lakecell[1], 10], sp_pars[lakecell[0], lakecell[1], 11], p2[0]) q_lake = np.append(q_lake, q_lake[-1]) # both lake & Quz are in m3/s #new quz[lakecell[0], lakecell[1], :] = quz[lakecell[0], lakecell[1], :] + q_lake #%% cells at the divider quz_routed = np.zeros_like(quz) * np.nan # for all cell with 0 flow acc put the quz for x in range(x_ext): # no of rows for y in range(y_ext): # no of columns if mask[x, y] != no_val and flow_acc_plan[x, y] == 0: quz_routed[x, y, :] = quz[x, y, :] #%% new for j in range(1, len(no_cells)): #2):# for x in range(x_ext): # no of rows for y in range(y_ext): # no of columns # check from total flow accumulation if mask[x, y] != no_val and flow_acc_plan[x, y] == no_cells[j]: # print(no_cells[j]) q_r = np.zeros(n_steps) for i in range(len(flow_acc[str(x) + "," + str(y)])): # no_cells[j] # bring the indexes of the us cell x_ind = flow_acc[str(x) + "," + str(y)][i][0] y_ind = flow_acc[str(x) + "," + str(y)][i][1] # sum the Q of the US cells (already routed for its cell) # route first with there own k & xthen sum q_r = q_r + routing.Muskingum_V( quz_routed[x_ind, y_ind, :], quz_routed[x_ind, y_ind, 0], sp_pars[x_ind, y_ind, 10], sp_pars[x_ind, y_ind, 11], p2[0]) # q=q_r # add the routed upstream flows to the current Quz in the cell quz_routed[x, y, :] = quz[x, y, :] + q_r #%% check if the max flow _acc is at the outlet # if tot_elem != np.nanmax(flow_acc_plan): # raise ("flow accumulation plan is not correct") # outlet is the cell that has the max flow_acc outlet = np.where(flow_acc_plan == np.nanmax( flow_acc_plan)) #np.nanmax(flow_acc_plan) outletx = outlet[0][0] outlety = outlet[1][0] #%% qlz = np.array([np.nanmean(qlz[:, :, i]) for i in range(n_steps) ]) # average of all cells (not routed mm/timestep) # convert Qlz to m3/sec qlz = qlz * p2[1] / (p2[0] * 3.6) # generation qout = qlz + quz_routed[outletx, outlety, :] return qout, st, quz_routed, qlz, quz
def HapiWithlake(Model, Lake, ll_temp=None, q_0=None): """ ============================================================ HapiWithlake(Model, Lake,ll_temp=None, q_0=None) ============================================================ HapiWithlake connects three modules the lake, the distributed ranfall-runoff module and spatial routing module Parameters ---------- Model : [Catchment object] DESCRIPTION. Lake : TYPE DESCRIPTION. ll_temp : TYPE, optional DESCRIPTION. The default is None. q_0 : TYPE, optional DESCRIPTION. The default is None. Returns ------- None. """ plake = Lake.MeteoData[:, 0] et = Lake.MeteoData[:, 1] t = Lake.MeteoData[:, 2] tm = Lake.MeteoData[:, 3] # lake simulation Lake.Qlake, _ = hbv_lake.simulate( plake, t, et, Lake.Parameters, [Model.Timef, Lake.CatArea, Lake.LakeArea], Lake.StageDischargeCurve, 0, init_st=Lake.InitialCond, ll_temp=tm, lake_sim=True) # qlake is in m3/sec # lake routing Lake.QlakeR = routing.Muskingum_V(Lake.Qlake, Lake.Qlake[0], Lake.Parameters[11], Lake.Parameters[12], Model.Timef) # subcatchment distrrm.RunLumpedRRM(Model) # routing lake discharge with DS cell k & x and adding to cell Q qlake = routing.Muskingum_V( Lake.QlakeR, Lake.QlakeR[0], Model.Parameters[Lake.OutflowCell[0], Lake.OutflowCell[1], 10], Model.Parameters[Lake.OutflowCell[0], Lake.OutflowCell[1], 11], Model.Timef) qlake = np.append(qlake, qlake[-1]) # both lake & Quz are in m3/s Model.quz[Lake.OutflowCell[0], Lake.OutflowCell[1], :] = Model.quz[ Lake.OutflowCell[0], Lake.OutflowCell[1], :] + qlake # run the GIS part to rout from cell to another distrrm.SpatialRouting(Model) Model.qout = Model.qout[:-1]
def SpatialRouting(Model): """ ==================================================================== SpatialRouting(qlz,quz,flow_acc,flow_direct,sp_pars,p2) ==================================================================== SpatialRouting method routes the discharge from cell to another following the flow direction input raster Inputs: ---------- 1-qlz: [numpy ndarray] 3D array of the lower zone discharge 2-quz: [numpy ndarray] 3D array of the upper zone discharge 3-flow_acc: [gdal.dataset] flow accumulation raster file of the catchment (clipped to the catchment only) 4-flow_direct: [gdal.dataset] flow Direction raster file of the catchment (clipped to the catchment only) 5-sp_pars: [numpy ndarray] 3D array of the parameters 6-p2: [List] list of unoptimized parameters p2[0] = tfac, 1 for hourly, 0.25 for 15 min time step and 24 for daily time step p2[1] = catchment area in km2 Outputs: ---------- 1-qout: [numpy array] 1D timeseries of discharge at the outlet of the catchment of unit m3/sec 2-quz_routed: [numpy ndarray] 3D array of the upper zone discharge accumulated and routed at each time step 3-qlz_translated: [numpy ndarray] 3D array of the lower zone discharge translated at each time step """ # # routing lake discharge with DS cell k & x and adding to cell Q # q_lake=Routing.Muskingum_V(q_lake,q_lake[0],sp_pars[lakecell[0],lakecell[1],10],sp_pars[lakecell[0],lakecell[1],11],p2[0]) # q_lake=np.append(q_lake,q_lake[-1]) # # both lake & Quz are in m3/s # #new # quz[lakecell[0],lakecell[1],:]=quz[lakecell[0],lakecell[1],:]+q_lake ### cells at the divider Model.quz_routed = np.zeros_like(Model.quz) """ lower zone discharge is going to be just translated without any attenuation in order to be able to calculate total discharge (uz+lz) at internal points in the catchment """ Model.qlz_translated = np.zeros_like(Model.quz) #*np.nan Model.Qtot = np.zeros_like(Model.quz) # for all cell with 0 flow acc put the quz for x in range(Model.rows): # no of rows for y in range(Model.cols): # no of columns if Model.FlowAccArr[ x, y] != Model.NoDataValue and Model.FlowAccArr[x, y] == 0: Model.quz_routed[x, y, :] = Model.quz[x, y, :] Model.qlz_translated[x, y, :] = Model.qlz[x, y, :] ### remaining cells for j in range(1, len(Model.acc_val)): #TODO parallelize # all cells with the same acc_val can run at the same time for x in range(Model.rows): # no of rows for y in range(Model.cols): # no of columns # check from total flow accumulation if Model.FlowAccArr[ x, y] != Model.NoDataValue and Model.FlowAccArr[ x, y] == Model.acc_val[j]: # for UZ q_uzi = np.zeros(Model.TS) # for lz qlzi = np.zeros(Model.TS) # iterate to route uz and translate lz for i in range(len( Model.FDT[str(x) + "," + str(y)])): # Model.acc_val[j] # bring the indexes of the us cell x_ind = Model.FDT[str(x) + "," + str(y)][i][0] y_ind = Model.FDT[str(x) + "," + str(y)][i][1] # sum the Q of the US cells (already routed for its cell) # route first with there own k & xthen sum q_uzi = q_uzi + routing.Muskingum_V( Model.quz_routed[x_ind, y_ind, :], Model.quz_routed[x_ind, y_ind, 0], Model.Parameters[x_ind, y_ind, 10], Model.Parameters[x_ind, y_ind, 11], Model.Timef) qlzi = qlzi + Model.qlz_translated[x_ind, y_ind, :] # add the routed upstream flows to the current Quz in the cell Model.quz_routed[x, y, :] = Model.quz[x, y, :] + q_uzi Model.qlz_translated[x, y, :] = Model.qlz[x, y, :] + qlzi outletx = Model.Outlet[0][0] outlety = Model.Outlet[1][0] Model.qout = Model.qlz_translated[ outletx, outlety, :] + Model.quz_routed[outletx, outlety, :] Model.Qtot = Model.qlz_translated + Model.quz_routed