def checkerboard(mapin, fcc): """ checkerboard create a checkerboard map with unique id's in a fcc*fcc cells area. The resulting map can be used to derive statistics for (later) upscaling of maps (using the fcc factor) .. warning: use with unitcell to get most reliable results! Input: - map (used to determine coordinates) - fcc (size of the areas in cells) Output: - checkerboard type map """ msker = pcr.defined(mapin) ymin = pcr.mapminimum(pcr.ycoordinate(msker)) yc = (pcr.ycoordinate((msker)) - ymin) / pcr.celllength() yc = pcr.rounddown(yc / fcc) # yc = yc/fcc xmin = pcr.mapminimum(pcr.xcoordinate((msker))) xc = (pcr.xcoordinate((msker)) - xmin) / pcr.celllength() xc = pcr.rounddown(xc / fcc) # xc = xc/fcc yc = yc * (pcr.mapmaximum(xc) + 1.0) xy = pcr.ordinal(xc + yc) return xy
def getICs(self, initial_condition): avgInflow = initial_condition['avgLakeReservoirInflowShort'] avgOutflow = initial_condition['avgLakeReservoirOutflowLong'] if initial_condition['waterBodyStorage'] is not None: # read directly waterBodyStorage = initial_condition['waterBodyStorage'] else: # calculate waterBodyStorage at cells where lakes and/or reservoirs are defined # storageAtLakeAndReservoirs = pcr.cover(\ pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0., initial_condition['channelStorage']), 0.0) # # - move only non negative values and use rounddown values storageAtLakeAndReservoirs = pcr.max( 0.00, pcr.rounddown(storageAtLakeAndReservoirs)) # # lake and reservoir storages = waterBodyStorage (m3) ; values are given for the entire lake / reservoir cells waterBodyStorage = pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0., \ pcr.areatotal(storageAtLakeAndReservoirs,\ self.waterBodyIds)) self.avgInflow = pcr.cover(avgInflow, 0.0) # unit: m3/s self.avgOutflow = pcr.cover(avgOutflow, 0.0) # unit: m3/s self.waterBodyStorage = pcr.cover(waterBodyStorage, 0.0) # unit: m3 self.avgInflow = pcr.ifthen(self.landmask, self.avgInflow) self.avgOutflow = pcr.ifthen(self.landmask, self.avgOutflow) self.waterBodyStorage = pcr.ifthen(self.landmask, self.waterBodyStorage)
def getWaterBodyOutflow( self, maxTimestepsToAvgDischargeLong, avgChannelDischarge, length_of_time_step=vos.secondsPerDay(), downstreamDemand=None, ): # outflow in volume from water bodies with lake type (m3): lakeOutflow = self.getLakeOutflow(avgChannelDischarge, length_of_time_step) # outflow in volume from water bodies with reservoir type (m3): if isinstance(downstreamDemand, types.NoneType): downstreamDemand = pcr.scalar(0.0) reservoirOutflow = self.getReservoirOutflow(avgChannelDischarge, length_of_time_step, downstreamDemand) # outgoing/release volume from lakes and/or reservoirs self.waterBodyOutflow = pcr.cover(reservoirOutflow, lakeOutflow) # make sure that all water bodies have outflow: self.waterBodyOutflow = pcr.max(0., pcr.cover(self.waterBodyOutflow, 0.0)) # limit outflow to available storage factor = 0.25 # to avoid flip flop self.waterBodyOutflow = pcr.min(self.waterBodyStorage * factor, self.waterBodyOutflow) # unit: m3 # use round values self.waterBodyOutflow = (pcr.rounddown(self.waterBodyOutflow / 1.) * 1. ) # unit: m3 # outflow rate in m3 per sec waterBodyOutflowInM3PerSec = (self.waterBodyOutflow / length_of_time_step) # unit: m3/s # updating (long term) average outflow (m3/s) ; # - needed to constrain/maintain reservoir outflow: # temp = pcr.max( 1.0, pcr.min( maxTimestepsToAvgDischargeLong, self.timestepsToAvgDischarge - 1.0 + length_of_time_step / vos.secondsPerDay(), ), ) deltaOutflow = waterBodyOutflowInM3PerSec - self.avgOutflow R = deltaOutflow * (length_of_time_step / vos.secondsPerDay()) / temp self.avgOutflow = self.avgOutflow + R self.avgOutflow = pcr.max(0.0, self.avgOutflow) # # for the reference, see the "weighted incremental algorithm" in http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance # update waterBodyStorage (after outflow): self.waterBodyStorage = self.waterBodyStorage - self.waterBodyOutflow self.waterBodyStorage = pcr.max(0.0, self.waterBodyStorage)
def dynamic(self): logger.info("Step 4: Monte Carlo simulation") # draw a random value (uniform for the entire map) z = pcr.mapnormal() #~ self.report(z,"z") # constraints, in order to make sure that random values are in the table of "lookup_table_average_thickness" z = pcr.max(-5.0, z) z = pcr.min(5.0, z) # assign average thickness (also uniform for the entire map) based on z self.Davg = pcr.lookupscalar(self.lookup_table_average_thickness, z) # self.report(self.Davg, "davg") self.lnDavg = pcr.ln(self.Davg) # sedimentary basin thickness (varying over cells and samples) lnD = self.F * (self.lnCV * self.lnDavg) + self.lnDavg # set the minimum depth (must be bigger than zero) minimum_depth = 0.005 lnD = pcr.max(pcr.ln(minimum_depth), lnD) # extrapolation lnD = pcr.cover(lnD, \ pcr.windowaverage(lnD, 1.50*vos.getMapAttributes(self.clone_map_file,"cellsize"))) lnD = pcr.cover(lnD, \ pcr.windowaverage(pcr.cover(lnD, pcr.ln(minimum_depth)), 3.00*vos.getMapAttributes(self.clone_map_file,"cellsize"))) lnD = pcr.cover(lnD, \ pcr.windowaverage(pcr.cover(lnD, pcr.ln(minimum_depth)), 0.50)) # smoothing per quarter arc degree lnD = pcr.windowaverage(lnD, 0.25) # thickness in meter self.D = pcr.exp(lnD) #~ # smoothing bottom elevation #~ dem_bottom = pcr.windowaverage(self.dem_average - self.D, 0.50) #~ # thickness in meter #~ self.D = pcr.max(0.0, self.dem_average - dem_bottom) #~ # smoothing #~ self.D = pcr.windowaverage(self.D, 1.50*vos.getMapAttributes(self.clone_map_file,"cellsize")) # accuracy until cm only self.D = pcr.rounddown(self.D * 100.) / 100. self.report(self.D, "damc")
def getICs(self, initial_condition): avgInflow = initial_condition["avgLakeReservoirInflowShort"] avgOutflow = initial_condition["avgLakeReservoirOutflowLong"] # if not isinstance(initial_condition["waterBodyStorage"], types.NoneType): # read directly waterBodyStorage = initial_condition["waterBodyStorage"] else: # calculate waterBodyStorage at cells where lakes and/or reservoirs are defined # storageAtLakeAndReservoirs = pcr.cover( pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0., initial_condition["channelStorage"], ), 0.0, ) # # - move only non negative values and use rounddown values storageAtLakeAndReservoirs = pcr.max( 0.00, pcr.rounddown(storageAtLakeAndReservoirs)) # # lake and reservoir storages = waterBodyStorage (m3) ; values are given for the entire lake / reservoir cells waterBodyStorage = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0., pcr.areatotal(storageAtLakeAndReservoirs, self.waterBodyIds), ) self.avgInflow = pcr.cover(avgInflow, 0.0) # unit: m3/s self.avgOutflow = pcr.cover(avgOutflow, 0.0) # unit: m3/s self.waterBodyStorage = pcr.cover(waterBodyStorage, 0.0) # unit: m3 self.avgInflow = pcr.ifthen(self.landmask, self.avgInflow) self.avgOutflow = pcr.ifthen(self.landmask, self.avgOutflow) self.waterBodyStorage = pcr.ifthen(self.landmask, self.waterBodyStorage)
def area_percentile(inmap, area, n, order, percentile): """ calculates percentile of inmap per area n is the number of points in each area, order, the sorter order of inmap per area (output of areaorder(inmap,area)) n is the output of pcr.areatotal(pcr.spatial(pcr.scalar(1.0)),area) Input: - inmap - area map - n - order (riverorder) - percentile Output: - percentile map """ i = pcr.rounddown((n * percentile) / 100.0 + 0.5) # index in order map perc = pcr.ifthen(i == order, inmap) return pcr.areaaverage(perc, area)
def getICs(self, initial_condition): avgInflow = initial_condition["avgLakeReservoirInflowShort"] avgOutflow = initial_condition["avgLakeReservoirOutflowLong"] # if not isinstance(initial_condition["waterBodyStorage"], type(None)): # read directly waterBodyStorage = initial_condition["waterBodyStorage"] else: # calculate waterBodyStorage at cells where lakes and/or reservoirs are defined # storageAtLakeAndReservoirs = pcr.cover( pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, initial_condition["channelStorage"], ), 0.0, ) # # - move only non negative values and use rounddown values storageAtLakeAndReservoirs = pcr.max( 0.00, pcr.rounddown(storageAtLakeAndReservoirs) ) # # lake and reservoir storages = waterBodyStorage (m3) ; values are given for the entire lake / reservoir cells waterBodyStorage = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, pcr.areatotal(storageAtLakeAndReservoirs, self.waterBodyIds), ) self.avgInflow = pcr.cover(avgInflow, 0.0) # unit: m3/s self.avgOutflow = pcr.cover(avgOutflow, 0.0) # unit: m3/s self.waterBodyStorage = pcr.cover(waterBodyStorage, 0.0) # unit: m3 self.avgInflow = pcr.ifthen(self.landmask, self.avgInflow) self.avgOutflow = pcr.ifthen(self.landmask, self.avgOutflow) self.waterBodyStorage = pcr.ifthen(self.landmask, self.waterBodyStorage)
def read_forcings(self, currTimeStep): # reading precipitation: self.precipitation = vos.netcdf2PCRobjClone(\ self.preFileNC,'precipitation',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) precipitationCorrectionFactor = pcr.scalar( 1.0 ) # Since 19 Feb 2014, Edwin removed the support for correcting precipitation. self.precipitation = pcr.max(0.,self.precipitation*\ precipitationCorrectionFactor) self.precipitation = pcr.cover(self.precipitation, 0.0) # ignore very small values of precipitation (less than 0.00001 m/day or less than 0.01 kg.m-2.day-1 ) if self.usingDailyTimeStepForcingData: self.precipitation = pcr.rounddown( self.precipitation * 100000.) / 100000. # reading temperature self.temperature = vos.netcdf2PCRobjClone(\ self.tmpFileNC,'temperature',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) # Downscaling precipitation and temperature if self.downscalePrecipitationOption: self.downscalePrecipitation(currTimeStep) if self.downscaleTemperatureOption: self.downscaleTemperature(currTimeStep) # calculate or obtain referencePotET if self.refETPotMethod == 'Hamon': self.referencePotET = \ refPotET.HamonPotET(self.temperature,\ currTimeStep.doy,\ self.latitudes) if self.refETPotMethod == 'Input': self.referencePotET = vos.netcdf2PCRobjClone(\ self.etpFileNC,'evapotranspiration',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) # Downscaling referenceETPot (based on temperature) if self.downscaleReferenceETPotOption: self.downscaleReferenceETPot() # smoothing: if self.forcingSmoothing == True: logger.info("Forcing data are smoothed.") self.precipitation = pcr.windowaverage(self.precipitation, self.smoothingWindowsLength) self.temperature = pcr.windowaverage(self.temperature, self.smoothingWindowsLength) self.referencePotET = pcr.windowaverage( self.referencePotET, self.smoothingWindowsLength) # define precipitation, temperature and referencePotET ONLY at landmask area (for reporting): self.precipitation = pcr.ifthen(self.landmask, self.precipitation) self.temperature = pcr.ifthen(self.landmask, self.temperature) self.referencePotET = pcr.ifthen(self.landmask, self.referencePotET) if self.report == True: timeStamp = datetime.datetime(currTimeStep.year,\ currTimeStep.month,\ currTimeStep.day,\ 0) # writing daily output to netcdf files timestepPCR = currTimeStep.timeStepPCR if self.outDailyTotNC[0] != "None": for var in self.outDailyTotNC: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_dailyTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,timestepPCR-1) # writing monthly output to netcdf files # -cummulative if self.outMonthTotNC[0] != "None": for var in self.outMonthTotNC: # introduce variables at the beginning of simulation or # reset variables at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.day == 1: \ vars(self)[var+'MonthTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'MonthTot'] += vars(self)[var] # reporting at the end of the month: if currTimeStep.endMonth == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'MonthTot'),\ vos.MV),timeStamp,currTimeStep.monthIdx-1) # -average if self.outMonthAvgNC[0] != "None": for var in self.outMonthAvgNC: # only if a accumulator variable has not been defined: if var not in self.outMonthTotNC: # introduce accumulator at the beginning of simulation or # reset accumulator at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.day == 1: \ vars(self)[var+'MonthTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'MonthTot'] += vars(self)[var] # calculating average & reporting at the end of the month: if currTimeStep.endMonth == True: vars(self)[var+'MonthAvg'] = vars(self)[var+'MonthTot']/\ currTimeStep.day self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthAvg.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'MonthAvg'),\ vos.MV),timeStamp,currTimeStep.monthIdx-1) # # -last day of the month if self.outMonthEndNC[0] != "None": for var in self.outMonthEndNC: # reporting at the end of the month: if currTimeStep.endMonth == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthEnd.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,currTimeStep.monthIdx-1) # writing yearly output to netcdf files # -cummulative if self.outAnnuaTotNC[0] != "None": for var in self.outAnnuaTotNC: # introduce variables at the beginning of simulation or # reset variables at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.doy == 1: \ vars(self)[var+'AnnuaTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'AnnuaTot'] += vars(self)[var] # reporting at the end of the year: if currTimeStep.endYear == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'AnnuaTot'),\ vos.MV),timeStamp,currTimeStep.annuaIdx-1) # -average if self.outAnnuaAvgNC[0] != "None": for var in self.outAnnuaAvgNC: # only if a accumulator variable has not been defined: if var not in self.outAnnuaTotNC: # introduce accumulator at the beginning of simulation or # reset accumulator at the beginning of the year if currTimeStep.timeStepPCR == 1 or \ currTimeStep.doy == 1: \ vars(self)[var+'AnnuaTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'AnnuaTot'] += vars(self)[var] # # calculating average & reporting at the end of the year: if currTimeStep.endYear == True: vars(self)[var+'AnnuaAvg'] = vars(self)[var+'AnnuaTot']/\ currTimeStep.doy self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaAvg.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'AnnuaAvg'),\ vos.MV),timeStamp,currTimeStep.annuaIdx-1) # # -last day of the year if self.outAnnuaEndNC[0] != "None": for var in self.outAnnuaEndNC: # reporting at the end of the year: if currTimeStep.endYear == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaEnd.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,currTimeStep.annuaIdx-1)
def read_forcings(self, currTimeStep): #----------------------------------------------------------------------- # NOTE: RvB 13/07/2016 hard-coded reference to the variable names # preciptiation, temperature and evapotranspiration have been replaced # by the variable names used in the netCDF and passed from the ini file #----------------------------------------------------------------------- # method for finding time indexes in the precipitation netdf file: # - the default one method_for_time_index = None # - based on the ini/configuration file (if given) if 'time_index_method_for_precipitation_netcdf' in list(self.iniItems.meteoOptions.keys()) and\ self.iniItems.meteoOptions['time_index_method_for_precipitation_netcdf'] != "None": method_for_time_index = self.iniItems.meteoOptions[ 'time_index_method_for_precipitation_netcdf'] # reading precipitation: if self.precipitation_set_per_year: #~ print currTimeStep.year nc_file_per_year = self.preFileNC % (float( currTimeStep.year), float(currTimeStep.year)) self.precipitation = vos.netcdf2PCRobjClone(\ nc_file_per_year, self.preVarName,\ str(currTimeStep.fulldate), useDoy = method_for_time_index, cloneMapFileName = self.cloneMap,\ LatitudeLongitude = True) else: self.precipitation = vos.netcdf2PCRobjClone(\ self.preFileNC, self.preVarName,\ str(currTimeStep.fulldate), useDoy = method_for_time_index, cloneMapFileName = self.cloneMap,\ LatitudeLongitude = True) #----------------------------------------------------------------------- # NOTE: RvB 13/07/2016 added to automatically update precipitation self.precipitation = self.preConst + self.preFactor * pcr.ifthen( self.landmask, self.precipitation) #----------------------------------------------------------------------- # make sure that precipitation is always positive self.precipitation = pcr.max(0., self.precipitation) self.precipitation = pcr.cover(self.precipitation, 0.0) # ignore very small values of precipitation (less than 0.00001 m/day or less than 0.01 kg.m-2.day-1 ) if self.usingDailyTimeStepForcingData: self.precipitation = pcr.rounddown( self.precipitation * 100000.) / 100000. # method for finding time index in the temperature netdf file: # - the default one method_for_time_index = None # - based on the ini/configuration file (if given) if 'time_index_method_for_temperature_netcdf' in list(self.iniItems.meteoOptions.keys()) and\ self.iniItems.meteoOptions['time_index_method_for_temperature_netcdf'] != "None": method_for_time_index = self.iniItems.meteoOptions[ 'time_index_method_for_temperature_netcdf'] # reading temperature if self.temperature_set_per_year: nc_file_per_year = self.tmpFileNC % (int( currTimeStep.year), int(currTimeStep.year)) self.temperature = vos.netcdf2PCRobjClone(\ nc_file_per_year, self.tmpVarName,\ str(currTimeStep.fulldate), useDoy = method_for_time_index, cloneMapFileName = self.cloneMap,\ LatitudeLongitude = True) else: self.temperature = vos.netcdf2PCRobjClone(\ self.tmpFileNC,self.tmpVarName,\ str(currTimeStep.fulldate), useDoy = method_for_time_index, cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) #----------------------------------------------------------------------- # NOTE: RvB 13/07/2016 added to automatically update temperature self.temperature = self.tmpConst + self.tmpFactor * pcr.ifthen( self.landmask, self.temperature) #----------------------------------------------------------------------- # Downscaling precipitation and temperature if self.downscalePrecipitationOption: self.downscalePrecipitation(currTimeStep) if self.downscaleTemperatureOption: self.downscaleTemperature(currTimeStep) # calculate or obtain referencePotET if self.refETPotMethod == 'Hamon': self.referencePotET = \ refPotET.HamonPotET(self.temperature,\ currTimeStep.doy,\ self.latitudes) if self.refETPotMethod == 'Input': # method for finding time indexes in the precipitation netdf file: # - the default one method_for_time_index = None # - based on the ini/configuration file (if given) if 'time_index_method_for_ref_pot_et_netcdf' in list(self.iniItems.meteoOptions.keys()) and\ self.iniItems.meteoOptions['time_index_method_for_ref_pot_et_netcdf'] != "None": method_for_time_index = self.iniItems.meteoOptions[ 'time_index_method_for_ref_pot_et_netcdf'] if self.refETPotFileNC_set_per_year: nc_file_per_year = self.etpFileNC % (int( currTimeStep.year), int(currTimeStep.year)) self.referencePotET = vos.netcdf2PCRobjClone(\ nc_file_per_year, self.refETPotVarName,\ str(currTimeStep.fulldate), useDoy = method_for_time_index, cloneMapFileName = self.cloneMap,\ LatitudeLongitude = True) else: self.referencePotET = vos.netcdf2PCRobjClone(\ self.etpFileNC,self.refETPotVarName,\ str(currTimeStep.fulldate), useDoy = method_for_time_index, cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) #----------------------------------------------------------------------- # NOTE: RvB 13/07/2016 added to automatically update reference potential evapotranspiration self.referencePotET = self.refETPotConst + self.refETPotFactor * pcr.ifthen( self.landmask, self.referencePotET) #----------------------------------------------------------------------- # Downscaling referenceETPot (based on temperature) if self.downscaleReferenceETPotOption: self.downscaleReferenceETPot() # smoothing: if self.forcingSmoothing == True: logger.debug("Forcing data are smoothed.") self.precipitation = pcr.windowaverage(self.precipitation, self.smoothingWindowsLength) self.temperature = pcr.windowaverage(self.temperature, self.smoothingWindowsLength) self.referencePotET = pcr.windowaverage( self.referencePotET, self.smoothingWindowsLength) # rounding temperature values to minimize numerical errors (note only to minimize, not remove) self.temperature = pcr.roundoff(self.temperature * 1000.) / 1000. # ignore snow by setting temperature to 25 deg C if self.ignore_snow: self.temperature = pcr.spatial(pcr.scalar(25.)) # define precipitation, temperature and referencePotET ONLY at landmask area (for reporting): self.precipitation = pcr.ifthen(self.landmask, self.precipitation) self.temperature = pcr.ifthen(self.landmask, self.temperature) self.referencePotET = pcr.ifthen(self.landmask, self.referencePotET) # make sure precipitation and referencePotET are always positive: self.precipitation = pcr.max(0.0, self.precipitation) self.referencePotET = pcr.max(0.0, self.referencePotET) if self.report == True: timeStamp = datetime.datetime(currTimeStep.year,\ currTimeStep.month,\ currTimeStep.day,\ 0) # writing daily output to netcdf files timestepPCR = currTimeStep.timeStepPCR if self.outDailyTotNC[0] != "None": for var in self.outDailyTotNC: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_dailyTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,timestepPCR-1) # writing monthly output to netcdf files # -cummulative if self.outMonthTotNC[0] != "None": for var in self.outMonthTotNC: # introduce variables at the beginning of simulation or # reset variables at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.day == 1: \ vars(self)[var+'MonthTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'MonthTot'] += vars(self)[var] # reporting at the end of the month: if currTimeStep.endMonth == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'MonthTot'),\ vos.MV),timeStamp,currTimeStep.monthIdx-1) # -average if self.outMonthAvgNC[0] != "None": for var in self.outMonthAvgNC: # only if a accumulator variable has not been defined: if var not in self.outMonthTotNC: # introduce accumulator at the beginning of simulation or # reset accumulator at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.day == 1: \ vars(self)[var+'MonthTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'MonthTot'] += vars(self)[var] # calculating average & reporting at the end of the month: if currTimeStep.endMonth == True: vars(self)[var+'MonthAvg'] = vars(self)[var+'MonthTot']/\ currTimeStep.day self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthAvg.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'MonthAvg'),\ vos.MV),timeStamp,currTimeStep.monthIdx-1) # # -last day of the month if self.outMonthEndNC[0] != "None": for var in self.outMonthEndNC: # reporting at the end of the month: if currTimeStep.endMonth == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthEnd.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,currTimeStep.monthIdx-1) # writing yearly output to netcdf files # -cummulative if self.outAnnuaTotNC[0] != "None": for var in self.outAnnuaTotNC: # introduce variables at the beginning of simulation or # reset variables at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.doy == 1: \ vars(self)[var+'AnnuaTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'AnnuaTot'] += vars(self)[var] # reporting at the end of the year: if currTimeStep.endYear == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'AnnuaTot'),\ vos.MV),timeStamp,currTimeStep.annuaIdx-1) # -average if self.outAnnuaAvgNC[0] != "None": for var in self.outAnnuaAvgNC: # only if a accumulator variable has not been defined: if var not in self.outAnnuaTotNC: # introduce accumulator at the beginning of simulation or # reset accumulator at the beginning of the year if currTimeStep.timeStepPCR == 1 or \ currTimeStep.doy == 1: \ vars(self)[var+'AnnuaTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'AnnuaTot'] += vars(self)[var] # # calculating average & reporting at the end of the year: if currTimeStep.endYear == True: vars(self)[var+'AnnuaAvg'] = vars(self)[var+'AnnuaTot']/\ currTimeStep.doy self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaAvg.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'AnnuaAvg'),\ vos.MV),timeStamp,currTimeStep.annuaIdx-1) # # -last day of the year if self.outAnnuaEndNC[0] != "None": for var in self.outAnnuaEndNC: # reporting at the end of the year: if currTimeStep.endYear == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaEnd.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,currTimeStep.annuaIdx-1)
def set_river_package(self, discharge, currTimeStep): logger.info("Set the river package.") # - surface water river bed/bottom elevation and conductance need_to_define_surface_water_bed = False if currTimeStep == None: # this is for a steady state simulation (no currTimeStep define) need_to_define_surface_water_bed = True else: # only at the first month of the model simulation or the first month of the year if self.firstMonthOfSimulation or currTimeStep.month == 1: need_to_define_surface_water_bed = True self.firstMonthOfSimulation = False # This part becomes False as we don't need it anymore. if need_to_define_surface_water_bed: logger.info("Estimating the surface water bed elevation and surface water bed conductance.") #~ # - for lakes and resevoirs, alternative 1: make the bottom elevation deep --- Shall we do this? #~ additional_depth = 500. #~ surface_water_bed_elevation = pcr.ifthen(pcr.scalar(self.WaterBodies.waterBodyIds) > 0.0, \ #~ self.dem_riverbed - additional_depth) # # - for lakes and resevoirs, estimate bed elevation from dem and bankfull depth surface_water_bed_elevation = pcr.ifthen(pcr.scalar(self.WaterBodies.waterBodyIds) > 0.0, self.dem_average) surface_water_bed_elevation = pcr.areaaverage(surface_water_bed_elevation, self.WaterBodies.waterBodyIds) surface_water_bed_elevation -= pcr.areamaximum(self.bankfull_depth, self.WaterBodies.waterBodyIds) # surface_water_bed_elevation = pcr.cover(surface_water_bed_elevation, self.dem_riverbed) #~ surface_water_bed_elevation = self.dem_riverbed # This is an alternative, if we do not want to introduce very deep bottom elevations of lakes and/or reservoirs. # # rounding values for surface_water_bed_elevation self.surface_water_bed_elevation = pcr.roundup(surface_water_bed_elevation * 1000.)/1000. # # - river bed condutance (unit: m2/day) bed_surface_area = pcr.ifthen(pcr.scalar(self.WaterBodies.waterBodyIds) > 0.0, \ self.WaterBodies.fracWat * self.cellAreaMap) # TODO: Incorporate the concept of dynamicFracWat # I have problem with the convergence if I use this one. bed_surface_area = pcr.min(bed_surface_area,\ pcr.ifthen(pcr.scalar(self.WaterBodies.waterBodyIds) > 0.0, \ pcr.areaaverage(self.bankfull_width * self.channelLength, self.WaterBodies.waterBodyIds))) bed_surface_area = pcr.cover(bed_surface_area, \ self.bankfull_width * self.channelLength) #~ bed_surface_area = self.bankfull_width * self.channelLength bed_conductance = (1.0/self.bed_resistance) * bed_surface_area bed_conductance = pcr.ifthenelse(bed_conductance < 1e-20, 0.0, \ bed_conductance) self.bed_conductance = pcr.cover(bed_conductance, 0.0) logger.info("Estimating outlet widths of lakes and/or reservoirs.") # - 'channel width' for lakes and reservoirs channel_width = pcr.areamaximum(self.bankfull_width, self.WaterBodies.waterBodyIds) self.channel_width = pcr.cover(channel_width, self.bankfull_width) logger.info("Estimating surface water elevation.") # - convert discharge value to surface water elevation (m) river_water_height = (self.channel_width**(-3/5)) * (discharge**(3/5)) * ((self.gradient)**(-3/10)) *(self.manningsN**(3/5)) surface_water_elevation = self.dem_riverbed + \ river_water_height # # - calculating water level (unit: m) above the flood plain # TODO: Improve this concept (using Rens's latest innundation scheme) #---------------------------------------------------------- water_above_fpl = pcr.max(0.0, surface_water_elevation - self.dem_floodplain) # unit: m, water level above the floodplain (not distributed) water_above_fpl *= self.bankfull_depth * self.bankfull_width / self.cellAreaMap # unit: m, water level above the floodplain (distributed within the cell) # TODO: Improve this concept using Rens's latest scheme # # - corrected surface water elevation surface_water_elevation = pcr.ifthenelse(surface_water_elevation > self.dem_floodplain, \ self.dem_floodplain + water_above_fpl, \ surface_water_elevation) # - surface water elevation for lakes and reservoirs: lake_reservoir_water_elevation = pcr.ifthen(self.WaterBodies.waterBodyOut, surface_water_elevation) lake_reservoir_water_elevation = pcr.areamaximum(lake_reservoir_water_elevation, self.WaterBodies.waterBodyIds) lake_reservoir_water_elevation = pcr.cover(lake_reservoir_water_elevation, \ pcr.areaaverage(surface_water_elevation, self.WaterBodies.waterBodyIds)) # - maximum and minimum values for lake_reservoir_water_elevation lake_reservoir_water_elevation = pcr.min(self.dem_floodplain, lake_reservoir_water_elevation) lake_reservoir_water_elevation = pcr.max(self.surface_water_bed_elevation, lake_reservoir_water_elevation) # - smoothing lake_reservoir_water_elevation = pcr.areaaverage(surface_water_elevation, self.WaterBodies.waterBodyIds) # # - merge lake and reservoir water elevation surface_water_elevation = pcr.cover(lake_reservoir_water_elevation, surface_water_elevation) # # - covering the missing values and rounding surface_water_elevation = pcr.cover(surface_water_elevation, self.surface_water_bed_elevation) surface_water_elevation = pcr.rounddown(surface_water_elevation * 1000.)/1000. # # - make sure that HRIV >= RBOT ; no infiltration if HRIV = RBOT (and h < RBOT) self.surface_water_elevation = pcr.max(surface_water_elevation, self.surface_water_bed_elevation) # # - pass the values to the RIV package self.pcr_modflow.setRiver(self.surface_water_elevation, self.surface_water_bed_elevation, self.bed_conductance, 1)
def waterAbstractionAndAllocation(water_demand_volume,available_water_volume,allocation_zones,\ zone_area = None, high_volume_treshold = 1000000., debug_water_balance = True,\ extra_info_for_water_balance_reporting = "", ignore_small_values = True): logger.debug("Allocation of abstraction.") # demand volume in each cell (unit: m3) if ignore_small_values: # ignore small values to avoid runding error cellVolDemand = pcr.rounddown(pcr.max(0.0, water_demand_volume)) else: cellVolDemand = pcr.max(0.0, water_demand_volume) # total demand volume in each zone/segment (unit: m3) zoneVolDemand = pcr.areatotal(cellVolDemand, allocation_zones) # total available water volume in each cell if ignore_small_values: # ignore small values to avoid runding error cellAvlWater = pcr.rounddown(pcr.max(0.00, available_water_volume)) else: cellAvlWater = pcr.max(0.00, available_water_volume) # total available water volume in each zone/segment (unit: m3) # - to minimize numerical errors, separating cellAvlWater if not isinstance(high_volume_treshold, types.NoneType): # mask: 0 for small volumes ; 1 for large volumes (e.g. in lakes and reservoirs) mask = pcr.cover(\ pcr.ifthen(cellAvlWater > high_volume_treshold, pcr.boolean(1)), pcr.boolean(0)) zoneAvlWater = pcr.areatotal(pcr.ifthenelse(mask, 0.0, cellAvlWater), allocation_zones) zoneAvlWater += pcr.areatotal(pcr.ifthenelse(mask, cellAvlWater, 0.0), allocation_zones) else: zoneAvlWater = pcr.areatotal(cellAvlWater, allocation_zones) # total actual water abstraction volume in each zone/segment (unit: m3) # - limited to available water zoneAbstraction = pcr.min(zoneAvlWater, zoneVolDemand) # actual water abstraction volume in each cell (unit: m3) cellAbstraction = getValDivZero(\ cellAvlWater, zoneAvlWater, smallNumber)*zoneAbstraction cellAbstraction = pcr.min(cellAbstraction, cellAvlWater) if ignore_small_values: # ignore small values to avoid runding error cellAbstraction = pcr.rounddown(pcr.max(0.00, cellAbstraction)) # to minimize numerical errors, separating cellAbstraction if not isinstance(high_volume_treshold, types.NoneType): # mask: 0 for small volumes ; 1 for large volumes (e.g. in lakes and reservoirs) mask = pcr.cover(\ pcr.ifthen(cellAbstraction > high_volume_treshold, pcr.boolean(1)), pcr.boolean(0)) zoneAbstraction = pcr.areatotal( pcr.ifthenelse(mask, 0.0, cellAbstraction), allocation_zones) zoneAbstraction += pcr.areatotal( pcr.ifthenelse(mask, cellAbstraction, 0.0), allocation_zones) else: zoneAbstraction = pcr.areatotal(cellAbstraction, allocation_zones) # allocation water to meet water demand (unit: m3) cellAllocation = getValDivZero(\ cellVolDemand, zoneVolDemand, smallNumber)*zoneAbstraction #~ # extraAbstraction to minimize numerical errors: #~ zoneDeficitAbstraction = pcr.max(0.0,\ #~ pcr.areatotal(cellAllocation , allocation_zones) -\ #~ pcr.areatotal(cellAbstraction, allocation_zones)) #~ remainingCellAvlWater = pcr.max(0.0, cellAvlWater - cellAbstraction) #~ cellAbstraction += zoneDeficitAbstraction * getValDivZero(\ #~ remainingCellAvlWater, #~ pcr.areatotal(remainingCellAvlWater, allocation_zones), #~ smallNumber) #~ # #~ # extraAllocation to minimize numerical errors: #~ zoneDeficitAllocation = pcr.max(0.0,\ #~ pcr.areatotal(cellAbstraction, allocation_zones) -\ #~ pcr.areatotal(cellAllocation , allocation_zones)) #~ remainingCellDemand = pcr.max(0.0, cellVolDemand - cellAllocation) #~ cellAllocation += zoneDeficitAllocation * getValDivZero(\ #~ remainingCellDemand, #~ pcr.areatotal(remainingCellDemand, allocation_zones), #~ smallNumber) if debug_water_balance and not isinstance(zone_area, types.NoneType): zoneAbstraction = pcr.cover( pcr.areatotal(cellAbstraction, allocation_zones) / zone_area, 0.0) zoneAllocation = pcr.cover( pcr.areatotal(cellAllocation, allocation_zones) / zone_area, 0.0) waterBalanceCheck([zoneAbstraction],\ [zoneAllocation],\ [pcr.scalar(0.0)],\ [pcr.scalar(0.0)],\ 'abstraction - allocation per zone/segment (PS: Error here may be caused by rounding error.)' ,\ True,\ extra_info_for_water_balance_reporting,threshold=1e-4) return cellAbstraction, cellAllocation
def getWaterBodyOutflow( self, maxTimestepsToAvgDischargeLong, avgChannelDischarge, length_of_time_step=vos.secondsPerDay(), downstreamDemand=None, ): # outflow in volume from water bodies with lake type (m3): lakeOutflow = self.getLakeOutflow(avgChannelDischarge, length_of_time_step) # outflow in volume from water bodies with reservoir type (m3): if isinstance(downstreamDemand, type(None)): downstreamDemand = pcr.scalar(0.0) reservoirOutflow = self.getReservoirOutflow( avgChannelDischarge, length_of_time_step, downstreamDemand ) # outgoing/release volume from lakes and/or reservoirs self.waterBodyOutflow = pcr.cover(reservoirOutflow, lakeOutflow) # make sure that all water bodies have outflow: self.waterBodyOutflow = pcr.max(0.0, pcr.cover(self.waterBodyOutflow, 0.0)) # limit outflow to available storage factor = 0.25 # to avoid flip flop self.waterBodyOutflow = pcr.min( self.waterBodyStorage * factor, self.waterBodyOutflow ) # unit: m3 # use round values self.waterBodyOutflow = ( pcr.rounddown(self.waterBodyOutflow / 1.0) * 1.0 ) # unit: m3 # outflow rate in m3 per sec waterBodyOutflowInM3PerSec = ( self.waterBodyOutflow / length_of_time_step ) # unit: m3/s # updating (long term) average outflow (m3/s) ; # - needed to constrain/maintain reservoir outflow: # temp = pcr.max( 1.0, pcr.min( maxTimestepsToAvgDischargeLong, self.timestepsToAvgDischarge - 1.0 + length_of_time_step / vos.secondsPerDay(), ), ) deltaOutflow = waterBodyOutflowInM3PerSec - self.avgOutflow R = deltaOutflow * (length_of_time_step / vos.secondsPerDay()) / temp self.avgOutflow = self.avgOutflow + R self.avgOutflow = pcr.max(0.0, self.avgOutflow) # # for the reference, see the "weighted incremental algorithm" in http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance # update waterBodyStorage (after outflow): self.waterBodyStorage = self.waterBodyStorage - self.waterBodyOutflow self.waterBodyStorage = pcr.max(0.0, self.waterBodyStorage)
def update(self,landSurface,routing,currTimeStep): if self.debugWaterBalance: preStorGroundwater = self.storGroundwater preStorGroundwaterFossil = self.storGroundwaterFossil # get riverbed infiltration from the previous time step (from routing) self.surfaceWaterInf = routing.riverbedExchange/routing.cellArea # m self.storGroundwater += self.surfaceWaterInf # get net recharge (percolation-capRise) and update storage: self.storGroundwater = pcr.max(0.,\ self.storGroundwater + landSurface.gwRecharge) # potential groundwater abstraction (unit: m) potGroundwaterAbstract = landSurface.totalPotentialGrossDemand -\ landSurface.allocSurfaceWaterAbstract if self.usingAllocSegments == False or self.limitAbstraction: # Note: For simplicity, no network for a run with limitAbstraction. logger.info("Groundwater abstraction is only to satisfy local demand. No network for distributing groundwater.") # nonFossil groundwater abstraction (unit: m) to fulfill water demand # - assumption: Groundwater is only abstracted to satisfy local demand. self.nonFossilGroundwaterAbs = \ pcr.max(0.0, pcr.min(self.storGroundwater,\ potGroundwaterAbstract)) # self.allocNonFossilGroundwater = self.nonFossilGroundwaterAbs if self.usingAllocSegments and self.limitAbstraction == False: # Note: Incorporating distribution network of groundwater source is possible only if limitAbstraction = False. logger.info("Using groundwater source allocation.") # gross/potential demand volume in each cell (unit: m3) cellVolGrossDemand = potGroundwaterAbstract*routing.cellArea # total gross demand volume in each segment/zone (unit: m3) segTtlGrossDemand = pcr.areatotal(cellVolGrossDemand, self.allocSegments) # total available groundwater water volume in each cell - ignore small values (less than 1 m3) cellAvlGroundwater = pcr.max(0.00, self.storGroundwater* routing.cellArea) cellAvlGroundwater = pcr.rounddown( cellAvlGroundwater/1.)*1. # total available surface water volume in each segment/zone (unit: m3) segAvlGroundwater = pcr.areatotal(cellAvlGroundwater, self.allocSegments) segAvlGroundwater = pcr.max(0.00, segAvlGroundwater) # total actual surface water abstraction volume in each segment/zone (unit: m3) # # - not limited to available water - ignore small values (less than 1 m3) segActGroundwaterAbs = pcr.max(0.0,\ pcr.rounddown(segTtlGrossDemand)) # # - limited to available water segActGroundwaterAbs = pcr.min(segAvlGroundwater, segActGroundwaterAbs) # actual surface water abstraction volume in each cell (unit: m3) volActGroundwaterAbstract = vos.getValDivZero(\ cellAvlGroundwater, segAvlGroundwater, vos.smallNumber) * \ segActGroundwaterAbs volActGroundwaterAbstract = pcr.min(cellAvlGroundwater , volActGroundwaterAbstract) # unit: m3 # actual non fossil groundwater abstraction volume in meter (unit: m) self.nonFossilGroundwaterAbs = pcr.ifthen(self.landmask, volActGroundwaterAbstract) /\ routing.cellArea # unit: m # allocation non fossil groundwater abstraction volume to each cell (unit: m3) self.volAllocGroundwaterAbstract = vos.getValDivZero(\ cellVolGrossDemand, segTtlGrossDemand, vos.smallNumber) *\ segActGroundwaterAbs # unit: m3 # allocation surface water abstraction in meter (unit: m) self.allocNonFossilGroundwater = pcr.ifthen(self.landmask, self.volAllocGroundwaterAbstract)/\ routing.cellArea # unit: m if self.debugWaterBalance == str('True'): abstraction = pcr.cover(pcr.areatotal(self.nonFossilGroundwaterAbs *routing.cellArea, self.allocSegments)/self.segmentArea, 0.0) allocation = pcr.cover(pcr.areatotal(self.allocNonFossilGroundwater*routing.cellArea, self.allocSegments)/self.segmentArea, 0.0) vos.waterBalanceCheck([abstraction],\ [allocation],\ [pcr.scalar(0.0)],\ [pcr.scalar(0.0)],\ 'non fossil groundwater abstraction - allocation per zone/segment (PS: Error here may be caused by rounding error.)' ,\ True,\ "",threshold=5e-4) # update storGoundwater after self.nonFossilGroundwaterAbs self.storGroundwater = pcr.max(0.,self.storGroundwater - self.nonFossilGroundwaterAbs) # unmetDemand (m), satisfied by fossil gwAbstractions # TODO: Include desalinization self.unmetDemand = pcr.max(0.0, potGroundwaterAbstract - \ self.allocNonFossilGroundwater) # m (equal to zero if limitAbstraction = True) if self.limitAbstraction: logger.info("No fossil groundwater abstraction is allowed") # TODO: check that self.unmetDemand = 0.0 # correcting unmetDemand with available fossil groundwater # Note: For simplicity, limitFossilGroundwaterAbstraction can only be combined with local source assumption if self.usingAllocSegments == False and self.limitFossilGroundwaterAbstraction: self.unmetDemand = pcr.min(pcr.max(0.0, self.storGroundwaterFossil), self.unmetDemand) # calculate the average groundwater abstraction (m/day) from the last 365 days: totalAbstraction = self.unmetDemand + self.nonFossilGroundwaterAbs deltaAbstraction = totalAbstraction - self.avgAbstraction self.avgAbstraction = self.avgAbstraction +\ deltaAbstraction/\ pcr.min(365., pcr.max(1.0, routing.timestepsToAvgDischarge)) self.avgAbstraction = pcr.max(0.0, self.avgAbstraction) # update storGroundwaterFossil after unmetDemand self.storGroundwaterFossil -= self.unmetDemand # calculate baseflow and update storage: self.baseflow = pcr.max(0.,\ pcr.min(self.storGroundwater,\ self.recessionCoeff* \ self.storGroundwater)) self.storGroundwater = pcr.max(0.,\ self.storGroundwater - self.baseflow) # PS: baseflow must be calculated at the end (to ensure the availability of storGroundwater to support nonFossilGroundwaterAbs) if self.debugWaterBalance: vos.waterBalanceCheck([self.surfaceWaterInf,\ landSurface.gwRecharge],\ [self.baseflow,\ self.nonFossilGroundwaterAbs],\ [ preStorGroundwater],\ [self.storGroundwater],\ 'storGroundwater',\ True,\ currTimeStep.fulldate,threshold=1e-4) if self.debugWaterBalance: vos.waterBalanceCheck([pcr.scalar(0.0)],\ [self.unmetDemand],\ [ preStorGroundwaterFossil],\ [self.storGroundwaterFossil],\ 'storGroundwaterFossil',\ True,\ currTimeStep.fulldate,threshold=1e-3) if self.debugWaterBalance and self.limitFossilGroundwaterAbstraction: vos.waterBalanceCheck([pcr.scalar(0.0)],\ [self.unmetDemand],\ [pcr.max(0.0, preStorGroundwaterFossil)],\ [pcr.max(0.0,self.storGroundwaterFossil)],\ 'storGroundwaterFossil (with limitFossilGroundwaterAbstraction)',\ True,\ currTimeStep.fulldate,threshold=1e-3) if self.debugWaterBalance and landSurface.limitAbstraction: vos.waterBalanceCheck([potGroundwaterAbstract],\ [self.nonFossilGroundwaterAbs],\ [pcr.scalar(0.)],\ [pcr.scalar(0.)],\ 'non fossil groundwater abstraction',\ True,\ currTimeStep.fulldate,threshold=1e-4) if self.debugWaterBalance: vos.waterBalanceCheck([self.unmetDemand, self.allocNonFossilGroundwater, landSurface.allocSurfaceWaterAbstract],\ [landSurface.totalPotentialGrossDemand],\ [pcr.scalar(0.)],\ [pcr.scalar(0.)],\ 'water demand allocation (from surface water, groundwater and unmetDemand)',\ True,\ currTimeStep.fulldate,threshold=1e-4) if self.report == True: timeStamp = datetime.datetime(currTimeStep.year,\ currTimeStep.month,\ currTimeStep.day,\ 0) # writing daily output to netcdf files timestepPCR = currTimeStep.timeStepPCR if self.outDailyTotNC[0] != "None": for var in self.outDailyTotNC: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_dailyTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,timestepPCR-1) # writing monthly output to netcdf files # -cummulative if self.outMonthTotNC[0] != "None": for var in self.outMonthTotNC: # introduce variables at the beginning of simulation or # reset variables at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.day == 1:\ vars(self)[var+'MonthTot'] = pcr.scalar(0.0) # accumulating vars(self)[var+'MonthTot'] += vars(self)[var] # reporting at the end of the month: if currTimeStep.endMonth == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'MonthTot'),\ vos.MV),timeStamp,currTimeStep.monthIdx-1) # -average if self.outMonthAvgNC[0] != "None": for var in self.outMonthAvgNC: # only if a accumulator variable has not been defined: if var not in self.outMonthTotNC: # introduce accumulator at the beginning of simulation or # reset accumulator at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.day == 1:\ vars(self)[var+'MonthTot'] = pcr.scalar(0.0) # accumulating vars(self)[var+'MonthTot'] += vars(self)[var] # calculating average & reporting at the end of the month: if currTimeStep.endMonth == True: vars(self)[var+'MonthAvg'] = vars(self)[var+'MonthTot']/\ currTimeStep.day self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthAvg.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'MonthAvg'),\ vos.MV),timeStamp,currTimeStep.monthIdx-1) # # -last day of the month if self.outMonthEndNC[0] != "None": for var in self.outMonthEndNC: # reporting at the end of the month: if currTimeStep.endMonth == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthEnd.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,currTimeStep.monthIdx-1) # writing yearly output to netcdf files # -cummulative if self.outAnnuaTotNC[0] != "None": for var in self.outAnnuaTotNC: # introduce variables at the beginning of simulation or # reset variables at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.doy == 1:\ vars(self)[var+'AnnuaTot'] = pcr.scalar(0.0) # accumulating vars(self)[var+'AnnuaTot'] += vars(self)[var] # reporting at the end of the year: if currTimeStep.endYear == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'AnnuaTot'),\ vos.MV),timeStamp,currTimeStep.annuaIdx-1) # -average if self.outAnnuaAvgNC[0] != "None": for var in self.outAnnuaAvgNC: # only if a accumulator variable has not been defined: if var not in self.outAnnuaTotNC: # introduce accumulator at the beginning of simulation or # reset accumulator at the beginning of the year if currTimeStep.timeStepPCR == 1 or \ currTimeStep.doy == 1:\ vars(self)[var+'AnnuaTot'] = pcr.scalar(0.0) # accumulating vars(self)[var+'AnnuaTot'] += vars(self)[var] # # calculating average & reporting at the end of the year: if currTimeStep.endYear == True: vars(self)[var+'AnnuaAvg'] = vars(self)[var+'AnnuaTot']/\ currTimeStep.doy self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaAvg.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'AnnuaAvg'),\ vos.MV),timeStamp,currTimeStep.annuaIdx-1) # # -last day of the year if self.outAnnuaEndNC[0] != "None": for var in self.outAnnuaEndNC: # reporting at the end of the year: if currTimeStep.endYear == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaEnd.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,currTimeStep.annuaIdx-1)
def returnPrecision(pcrMap,precisionValue= -6.): #-returns the precision of a PCRaster map relative to the log10 base # specified by the precisionValue return 10.**(precisionValue+pcr.rounddown(pcr.log10(pcrMap)))
def read_forcings(self, currTimeStep): # reading precipitation: if self.precipitation_set_per_year: #~ print currTimeStep.year nc_file_per_year = self.preFileNC % (float( currTimeStep.year), float(currTimeStep.year)) self.precipitation = vos.netcdf2PCRobjClone(\ nc_file_per_year, 'precipitation',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName = self.cloneMap,\ LatitudeLongitude = True) else: #self.precipitation = vos.netcdf2PCRobjClone(\ # self.preFileNC, 'precipitation',\ # str(currTimeStep.fulldate), # useDoy = None, # cloneMapFileName = self.cloneMap,\ # LatitudeLongitude = True) # reading precip when ISI-MIP is used precipitation = vos.netcdf2PCRobjClone(\ self.preFileNC, 'pr',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName = self.cloneMap,\ LatitudeLongitude = True) # unit original km m-2 s-1 to m d-1 # soortelijk gewicht water = 998 km/m3 self.precipitation = (precipitation / 998.0) * (60. * 60. * 24) precipitationCorrectionFactor = pcr.scalar( 1.0 ) # Since 19 Feb 2014, Edwin removed the support for correcting precipitation. self.precipitation = pcr.max(0.,self.precipitation*\ precipitationCorrectionFactor) self.precipitation = pcr.cover(self.precipitation, 0.0) # ignore very small values of precipitation (less than 0.00001 m/day or less than 0.01 kg.m-2.day-1 ) if self.usingDailyTimeStepForcingData: self.precipitation = pcr.rounddown( self.precipitation * 100000.) / 100000. # reading temperature if self.temperature_set_per_year: nc_file_per_year = self.tmpFileNC % (int( currTimeStep.year), int(currTimeStep.year)) self.temperature = vos.netcdf2PCRobjClone(\ nc_file_per_year, 'temperature',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName = self.cloneMap,\ LatitudeLongitude = True) else: # updated to temp when using ISI-MIP projection temperature = vos.netcdf2PCRobjClone(\ self.tmpFileNC,'tas',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) # unit K to C self.temperature = temperature - 273.15 # Downscaling precipitation and temperature if self.downscalePrecipitationOption: self.downscalePrecipitation(currTimeStep) if self.downscaleTemperatureOption: self.downscaleTemperature(currTimeStep) # calculate or obtain referencePotET if self.refETPotMethod == 'Hamon': self.referencePotET = \ refPotET.HamonPotET(self.temperature,\ currTimeStep.doy,\ self.latitudes) if self.refETPotMethod == 'Input': if self.refETPotFileNC_set_per_year: nc_file_per_year = self.etpFileNC % (int( currTimeStep.year), int(currTimeStep.year)) self.referencePotET = vos.netcdf2PCRobjClone(\ nc_file_per_year, 'evapotranspiration',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName = self.cloneMap,\ LatitudeLongitude = True) else: self.referencePotET = vos.netcdf2PCRobjClone(\ self.etpFileNC,'evapotranspiration',\ str(currTimeStep.fulldate), useDoy = None, cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) # Downscaling referenceETPot (based on temperature) if self.downscaleReferenceETPotOption: self.downscaleReferenceETPot() # smoothing: if self.forcingSmoothing == True: logger.debug("Forcing data are smoothed.") self.precipitation = pcr.windowaverage(self.precipitation, self.smoothingWindowsLength) self.temperature = pcr.windowaverage(self.temperature, self.smoothingWindowsLength) self.referencePotET = pcr.windowaverage( self.referencePotET, self.smoothingWindowsLength) # make sure precipitation is always positive: self.precipitation = pcr.max(0.0, self.precipitation) # rounding temperature values to minimize numerical errors (note only to minimize, not remove) self.temperature = pcr.roundoff(self.temperature * 1000.) / 1000. # ignore snow by setting temperature to 25 deg C if self.ignore_snow: self.temperature = pcr.spatial(pcr.scalar(25.)) # define precipitation, temperature and referencePotET ONLY at landmask area (for reporting): self.precipitation = pcr.ifthen(self.landmask, self.precipitation) self.temperature = pcr.ifthen(self.landmask, self.temperature) self.referencePotET = pcr.ifthen(self.landmask, self.referencePotET) if self.report == True: timeStamp = datetime.datetime(currTimeStep.year,\ currTimeStep.month,\ currTimeStep.day,\ 0) # writing daily output to netcdf files timestepPCR = currTimeStep.timeStepPCR if self.outDailyTotNC[0] != "None": for var in self.outDailyTotNC: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_dailyTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,timestepPCR-1) # writing monthly output to netcdf files # -cummulative if self.outMonthTotNC[0] != "None": for var in self.outMonthTotNC: # introduce variables at the beginning of simulation or # reset variables at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.day == 1: \ vars(self)[var+'MonthTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'MonthTot'] += vars(self)[var] # reporting at the end of the month: if currTimeStep.endMonth == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'MonthTot'),\ vos.MV),timeStamp,currTimeStep.monthIdx-1) # -average if self.outMonthAvgNC[0] != "None": for var in self.outMonthAvgNC: # only if a accumulator variable has not been defined: if var not in self.outMonthTotNC: # introduce accumulator at the beginning of simulation or # reset accumulator at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.day == 1: \ vars(self)[var+'MonthTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'MonthTot'] += vars(self)[var] # calculating average & reporting at the end of the month: if currTimeStep.endMonth == True: vars(self)[var+'MonthAvg'] = vars(self)[var+'MonthTot']/\ currTimeStep.day self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthAvg.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'MonthAvg'),\ vos.MV),timeStamp,currTimeStep.monthIdx-1) # # -last day of the month if self.outMonthEndNC[0] != "None": for var in self.outMonthEndNC: # reporting at the end of the month: if currTimeStep.endMonth == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthEnd.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,currTimeStep.monthIdx-1) # writing yearly output to netcdf files # -cummulative if self.outAnnuaTotNC[0] != "None": for var in self.outAnnuaTotNC: # introduce variables at the beginning of simulation or # reset variables at the beginning of the month if currTimeStep.timeStepPCR == 1 or \ currTimeStep.doy == 1: \ vars(self)[var+'AnnuaTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'AnnuaTot'] += vars(self)[var] # reporting at the end of the year: if currTimeStep.endYear == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaTot.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'AnnuaTot'),\ vos.MV),timeStamp,currTimeStep.annuaIdx-1) # -average if self.outAnnuaAvgNC[0] != "None": for var in self.outAnnuaAvgNC: # only if a accumulator variable has not been defined: if var not in self.outAnnuaTotNC: # introduce accumulator at the beginning of simulation or # reset accumulator at the beginning of the year if currTimeStep.timeStepPCR == 1 or \ currTimeStep.doy == 1: \ vars(self)[var+'AnnuaTot'] = pcr.scalar(0.0) # accumulating vars(self)[var + 'AnnuaTot'] += vars(self)[var] # # calculating average & reporting at the end of the year: if currTimeStep.endYear == True: vars(self)[var+'AnnuaAvg'] = vars(self)[var+'AnnuaTot']/\ currTimeStep.doy self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaAvg.nc",\ var,\ pcr2numpy(self.__getattribute__(var+'AnnuaAvg'),\ vos.MV),timeStamp,currTimeStep.annuaIdx-1) # # -last day of the year if self.outAnnuaEndNC[0] != "None": for var in self.outAnnuaEndNC: # reporting at the end of the year: if currTimeStep.endYear == True: self.netcdfObj.data2NetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaEnd.nc",\ var,\ pcr2numpy(self.__getattribute__(var),vos.MV),\ timeStamp,currTimeStep.annuaIdx-1)
def set_river_package(self, discharge): logger.info("Set the river package.") # specify the river package # # - surface water river bed/bottom elevation # # - for lakes and resevoirs, make the bottom elevation deep --- Shall we do this? #~ additional_depth = 500. #~ surface_water_bed_elevation = pcr.ifthen(pcr.scalar(self.WaterBodies.waterBodyIds) > 0.0, \ #~ self.dem_riverbed - additional_depth) #~ surface_water_bed_elevation = pcr.cover(surface_water_bed_elevation, self.dem_riverbed) # surface_water_bed_elevation = self.dem_riverbed # This is an alternative, if we do not want to introduce very deep bottom elevations of lakes and/or reservoirs. # # rounding values for surface_water_bed_elevation self.surface_water_bed_elevation = pcr.roundup( surface_water_bed_elevation * 1000.) / 1000. # # - river bed condutance (unit: m2/day) bed_surface_area = pcr.ifthen(pcr.scalar(self.WaterBodies.waterBodyIds) > 0.0, \ self.WaterBodies.fracWat * self.cellAreaMap) # TODO: Incorporate the concept of dynamicFracWat bed_surface_area = pcr.cover(bed_surface_area, \ self.bankfull_width * self.channelLength) bed_surface_area = self.bankfull_width * self.channelLength bed_conductance = (1.0 / self.bed_resistance) * bed_surface_area bed_conductance = pcr.ifthenelse(bed_conductance < 1e-20, 0.0, \ bed_conductance) self.bed_conductance = pcr.cover(bed_conductance, 0.0) # # - 'channel width' for lakes and reservoirs channel_width = pcr.areamaximum(self.bankfull_width, self.WaterBodies.waterBodyIds) channel_width = pcr.cover(channel_width, self.bankfull_width) # # - convert discharge value to surface water elevation (m) river_water_height = (channel_width**(-3 / 5)) * (discharge**( 3 / 5)) * ((self.gradient)**(-3 / 10)) * (self.manningsN**(3 / 5)) surface_water_elevation = self.dem_riverbed + \ river_water_height # # - calculating water level (unit: m) above the flood plain # TODO: Improve this concept (using Rens's latest innundation scheme) #---------------------------------------------------------- water_above_fpl = pcr.max( 0.0, surface_water_elevation - self.dem_floodplain ) # unit: m, water level above the floodplain (not distributed) water_above_fpl *= self.bankfull_depth * self.bankfull_width / self.cellAreaMap # unit: m, water level above the floodplain (distributed within the cell) # TODO: Improve this concept using Rens's latest scheme # # - corrected surface water elevation surface_water_elevation = pcr.ifthenelse(surface_water_elevation > self.dem_floodplain, \ self.dem_floodplain + water_above_fpl, \ surface_water_elevation) # - surface water elevation for lakes and reservoirs: lake_reservoir_water_elevation = pcr.ifthen( self.WaterBodies.waterBodyOut, surface_water_elevation) lake_reservoir_water_elevation = pcr.areamaximum( lake_reservoir_water_elevation, self.WaterBodies.waterBodyIds) lake_reservoir_water_elevation = pcr.cover(lake_reservoir_water_elevation, \ pcr.areaaverage(surface_water_elevation, self.WaterBodies.waterBodyIds)) # - maximum and minimum values for lake_reservoir_water_elevation lake_reservoir_water_elevation = pcr.min( self.dem_floodplain, lake_reservoir_water_elevation) lake_reservoir_water_elevation = pcr.max( surface_water_bed_elevation, lake_reservoir_water_elevation) # - smoothing lake_reservoir_water_elevation = pcr.areaaverage( surface_water_elevation, self.WaterBodies.waterBodyIds) # # - merge lake and reservoir water elevation surface_water_elevation = pcr.cover(lake_reservoir_water_elevation, surface_water_elevation) # # - pass values to the river package surface_water_elevation = pcr.cover(surface_water_elevation, self.surface_water_bed_elevation) surface_water_elevation = pcr.rounddown( surface_water_elevation * 1000.) / 1000. # # - make sure that HRIV >= RBOT ; no infiltration if HRIV = RBOT (and h < RBOT) self.surface_water_elevation = pcr.max( surface_water_elevation, self.surface_water_bed_elevation) # # - pass the values to the RIV package self.pcr_modflow.setRiver(self.surface_water_elevation, \ self.surface_water_bed_elevation, self.bed_conductance, 2)
def waterAbstractionAndAllocation(water_demand_volume,available_water_volume,allocation_zones,\ zone_area = None, high_volume_treshold = 1000000., debug_water_balance = True,\ extra_info_for_water_balance_reporting = "", ignore_small_values = True): logger.debug("Allocation of abstraction.") # demand volume in each cell (unit: m3) if ignore_small_values: # ignore small values to avoid runding error cellVolDemand = pcr.rounddown(pcr.max(0.0, water_demand_volume)) else: cellVolDemand = pcr.max(0.0, water_demand_volume) # total demand volume in each zone/segment (unit: m3) zoneVolDemand = pcr.areatotal(cellVolDemand, allocation_zones) # total available water volume in each cell if ignore_small_values: # ignore small values to avoid runding error cellAvlWater = pcr.rounddown(pcr.max(0.00, available_water_volume)) else: cellAvlWater = pcr.max(0.00, available_water_volume) # total available water volume in each zone/segment (unit: m3) # - to minimize numerical errors, separating cellAvlWater if not isinstance(high_volume_treshold,types.NoneType): # mask: 0 for small volumes ; 1 for large volumes (e.g. in lakes and reservoirs) mask = pcr.cover(\ pcr.ifthen(cellAvlWater > high_volume_treshold, pcr.boolean(1)), pcr.boolean(0)) zoneAvlWater = pcr.areatotal( pcr.ifthenelse(mask, 0.0, cellAvlWater), allocation_zones) zoneAvlWater += pcr.areatotal( pcr.ifthenelse(mask, cellAvlWater, 0.0), allocation_zones) else: zoneAvlWater = pcr.areatotal(cellAvlWater, allocation_zones) # total actual water abstraction volume in each zone/segment (unit: m3) # - limited to available water zoneAbstraction = pcr.min(zoneAvlWater, zoneVolDemand) # actual water abstraction volume in each cell (unit: m3) cellAbstraction = getValDivZero(\ cellAvlWater, zoneAvlWater, smallNumber)*zoneAbstraction cellAbstraction = pcr.min(cellAbstraction, cellAvlWater) if ignore_small_values: # ignore small values to avoid runding error cellAbstraction = pcr.rounddown(pcr.max(0.00, cellAbstraction)) # to minimize numerical errors, separating cellAbstraction if not isinstance(high_volume_treshold,types.NoneType): # mask: 0 for small volumes ; 1 for large volumes (e.g. in lakes and reservoirs) mask = pcr.cover(\ pcr.ifthen(cellAbstraction > high_volume_treshold, pcr.boolean(1)), pcr.boolean(0)) zoneAbstraction = pcr.areatotal( pcr.ifthenelse(mask, 0.0, cellAbstraction), allocation_zones) zoneAbstraction += pcr.areatotal( pcr.ifthenelse(mask, cellAbstraction, 0.0), allocation_zones) else: zoneAbstraction = pcr.areatotal(cellAbstraction, allocation_zones) # allocation water to meet water demand (unit: m3) cellAllocation = getValDivZero(\ cellVolDemand, zoneVolDemand, smallNumber)*zoneAbstraction #~ # extraAbstraction to minimize numerical errors: #~ zoneDeficitAbstraction = pcr.max(0.0,\ #~ pcr.areatotal(cellAllocation , allocation_zones) -\ #~ pcr.areatotal(cellAbstraction, allocation_zones)) #~ remainingCellAvlWater = pcr.max(0.0, cellAvlWater - cellAbstraction) #~ cellAbstraction += zoneDeficitAbstraction * getValDivZero(\ #~ remainingCellAvlWater, #~ pcr.areatotal(remainingCellAvlWater, allocation_zones), #~ smallNumber) #~ # #~ # extraAllocation to minimize numerical errors: #~ zoneDeficitAllocation = pcr.max(0.0,\ #~ pcr.areatotal(cellAbstraction, allocation_zones) -\ #~ pcr.areatotal(cellAllocation , allocation_zones)) #~ remainingCellDemand = pcr.max(0.0, cellVolDemand - cellAllocation) #~ cellAllocation += zoneDeficitAllocation * getValDivZero(\ #~ remainingCellDemand, #~ pcr.areatotal(remainingCellDemand, allocation_zones), #~ smallNumber) if debug_water_balance and not isinstance(zone_area,types.NoneType): zoneAbstraction = pcr.cover(pcr.areatotal(cellAbstraction, allocation_zones)/zone_area, 0.0) zoneAllocation = pcr.cover(pcr.areatotal(cellAllocation , allocation_zones)/zone_area, 0.0) waterBalanceCheck([zoneAbstraction],\ [zoneAllocation],\ [pcr.scalar(0.0)],\ [pcr.scalar(0.0)],\ 'abstraction - allocation per zone/segment (PS: Error here may be caused by rounding error.)' ,\ True,\ extra_info_for_water_balance_reporting,threshold=1e-4) return cellAbstraction, cellAllocation