def returnFloodedFraction(self,volume): #-returns the flooded fraction given the flood volume and the associated water height # using a logistic smoother near intersections (K&K, 2007) #-find the match on the basis of the shortest distance to the available intersections or steps deltaXMin= self.floodVolume[self.nrEntries-1] y_i= pcr.scalar(1.) k= [pcr.scalar(0.)]*2 mInt= pcr.scalar(0.) for iCnt in range(self.nrEntries-1,0,-1): #-find x_i for current volume and update match if applicable # also update slope and intercept deltaX= volume-self.floodVolume[iCnt] mask= pcr.abs(deltaX) < pcr.abs(deltaXMin) deltaXMin= pcr.ifthenelse(mask,deltaX,deltaXMin) y_i= pcr.ifthenelse(mask,self.areaFractions[iCnt],y_i) k[0]= pcr.ifthenelse(mask,self.kSlope[iCnt-1],k[0]) k[1]= pcr.ifthenelse(mask,self.kSlope[iCnt],k[1]) mInt= pcr.ifthenelse(mask,self.mInterval[iCnt],mInt) #-all values returned, process data: calculate scaled deltaX and smoothed function # on the basis of the integrated logistic functions PHI(x) and 1-PHI(x) deltaX= deltaXMin deltaXScaled= pcr.ifthenelse(deltaX < 0.,pcr.scalar(-1.),1.)*\ pcr.min(criterionKK,pcr.abs(deltaX/pcr.max(1.,mInt))) logInt= self.integralLogisticFunction(deltaXScaled) #-compute fractional flooded area and flooded depth floodedFraction= pcr.ifthenelse(volume > 0.,\ pcr.ifthenelse(pcr.abs(deltaXScaled) < criterionKK,\ y_i-k[0]*mInt*logInt[0]+k[1]*mInt*logInt[1],\ y_i+pcr.ifthenelse(deltaX < 0.,k[0],k[1])*deltaX),0.) floodedFraction= pcr.max(0.,pcr.min(1.,floodedFraction)) floodDepth= pcr.ifthenelse(floodedFraction > 0.,volume/(floodedFraction*self.cellArea),0.) return floodedFraction, floodDepth
def agriZone_Ep_Sa_cropG(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on LP - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa - Fa is based on storage in Sa - Code for ini-file: 4 """ JarvisCoefficients.calcEp(self, k) self.PotEvaporation = pcr.cover(pcr.ifthenelse(self.EpHour >= 0, self.EpHour, 0), 0) self.samax2 = self.samax[k] * self.cropG self.Qaadd = pcr.max(self.Sa_t[k] - self.samax2, 0) self.Qa = pcr.max(self.Pe - (self.samax2 - self.Sa_t[k]), 0) + self.Qaadd self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) self.SaN = pcr.min(self.Sa[k] / self.samax2, 1) self.SuN = self.Su[k] / self.sumax[k] self.Ea1 = pcr.max((self.PotEvaporation - self.Ei), 0) * pcr.min( self.Sa[k] / (self.samax2 * self.LP[k]), 1 ) self.Fa1 = pcr.ifthenelse( self.SaN > 0, self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * e ** (-self.decF[k] * (1 - self.SaN)), 0, ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Fa = ( self.Fa1 + (self.Fa1 / pcr.ifthenelse(self.Fa1 + self.Ea1 > 0, self.Fa1 + self.Ea1, 1)) * self.Sa_diff ) self.Ea = ( self.Ea1 + (self.Ea1 / pcr.ifthenelse(self.Fa1 + self.Ea1 > 0, self.Fa1 + self.Ea1, 1)) * self.Sa_diff ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) - self.Ea - self.Fa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[k] = self.Pe - self.Ea - self.Qa - self.Fa - self.Sa[k] + self.Sa_t[k] self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa self.Fa_[k] = self.Fa
def glacierHBV(GlacierFrac, GlacierStore, Snow, Temperature, TT, Cfmax, G_SIfrac, timestepsecs, basetimestep): """ Run Glacier module and add the snowpack on-top of it. First, a fraction of the snowpack is converted into ice using the HBV-light model (fraction between 0.001-0.005 per day). Glacier melting is modelled using a Temperature degree factor and only occurs if the snow cover < 10 mm. :ivar GlacierFrac: Fraction of wflow cell covered by glaciers :ivar GlacierStore: Volume of the galcier in the cell in mm w.e. :ivar Snow: Snow pack on top of Glacier :ivar Temperature: Air temperature :ivar TT: Temperature threshold for ice melting :ivar Cfmax: Ice degree-day factor in mm/(°C/day) :ivar G_SIfrac: Fraction of the snow part turned into ice each timestep :ivar timestepsecs: Model timestep in seconds :ivar basetimestep: Model base timestep (86 400 seconds) :returns: Snow,Snow2Glacier,GlacierStore,GlacierMelt, """ #Fraction of the snow transformed into ice (HBV-light model) Snow2Glacier = G_SIfrac * Snow Snow2Glacier = pcr.ifthenelse( GlacierFrac > 0.0, Snow2Glacier, pcr.scalar(0.0) ) # Max conversion to 8mm/day Snow2Glacier = ( pcr.min(Snow2Glacier, 8.0) * timestepsecs / basetimestep ) Snow = Snow - (Snow2Glacier * GlacierFrac) GlacierStore = GlacierStore + Snow2Glacier PotMelt = pcr.ifthenelse( Temperature > TT, Cfmax * (Temperature - TT), pcr.scalar(0.0) ) # Potential snow melt, based on temperature GlacierMelt = pcr.ifthenelse( Snow < 10.0, pcr.min(PotMelt, GlacierStore), pcr.cover(0.0) ) # actual Glacier melt GlacierStore = GlacierStore - GlacierMelt # dry snow content return Snow, Snow2Glacier, GlacierStore, GlacierMelt
def rainfall_interception_hbv(Rainfall, PotEvaporation, Cmax, InterceptionStorage): """ Returns: TF, Interception, IntEvap,InterceptionStorage """ Interception = pcr.min( Rainfall, Cmax - InterceptionStorage ) #: Interception in mm/timestep InterceptionStorage = ( InterceptionStorage + Interception ) #: Current interception storage TF = Rainfall - Interception IntEvap = pcr.min( InterceptionStorage, PotEvaporation ) #: Evaporation from interception storage InterceptionStorage = InterceptionStorage - IntEvap return TF, Interception, IntEvap, InterceptionStorage
def rainfall_interception_modrut( Precipitation, PotEvap, CanopyStorage, CanopyGapFraction, Cmax ): """ Interception according to a modified Rutter model. The model is solved explicitly and there is no drainage below Cmax. Returns: - NetInterception: P - TF - SF (may be different from the actual wet canopy evaporation) - ThroughFall: - StemFlow: - LeftOver: Amount of potential eveporation not used - Interception: Actual wet canopy evaporation in this thimestep - CanopyStorage: Canopy storage at the end of the timestep """ ########################################################################## # Interception according to a modified Rutter model with hourly timesteps# ########################################################################## p = CanopyGapFraction pt = 0.1 * p # Amount of P that falls on the canopy Pfrac = pcr.max((1 - p - pt), 0) * Precipitation # S cannot be larger than Cmax, no gravity drainage below that DD = pcr.ifthenelse(CanopyStorage > Cmax, CanopyStorage - Cmax, 0.0) CanopyStorage = CanopyStorage - DD # Add the precipitation that falls on the canopy to the store CanopyStorage = CanopyStorage + Pfrac # Now do the Evap, make sure the store does not get negative dC = -1 * pcr.min(CanopyStorage, PotEvap) CanopyStorage = CanopyStorage + dC LeftOver = PotEvap + dC # Amount of evap not used # Now drain the canopy storage again if needed... D = pcr.ifthenelse(CanopyStorage > Cmax, CanopyStorage - Cmax, 0.0) CanopyStorage = CanopyStorage - D # Calculate throughfall ThroughFall = DD + D + p * Precipitation StemFlow = Precipitation * pt # Calculate interception, this is NET Interception NetInterception = Precipitation - ThroughFall - StemFlow Interception = -dC return NetInterception, ThroughFall, StemFlow, LeftOver, Interception, CanopyStorage
def rainfall_interception_gash( Cmax, EoverR, CanopyGapFraction, Precipitation, CanopyStorage, maxevap=9999 ): """ Interception according to the Gash model (For daily timesteps). """ # TODO: add other rainfall interception method (lui) # TODO: Include subdaily Gash model # TODO: add LAI variation in year # Hack for stemflow pt = 0.1 * CanopyGapFraction P_sat = pcr.max( pcr.scalar(0.0), pcr.cover( (-Cmax / EoverR) * pcr.ln(1.0 - (EoverR / (1.0 - CanopyGapFraction - pt))), pcr.scalar(0.0), ), ) # large storms P > P_sat largestorms = Precipitation > P_sat Iwet = pcr.ifthenelse( largestorms, ((1 - CanopyGapFraction - pt) * P_sat) - Cmax, Precipitation * (1 - CanopyGapFraction - pt), ) Isat = pcr.ifthenelse(largestorms, (EoverR) * (Precipitation - P_sat), 0.0) Idry = pcr.ifthenelse(largestorms, Cmax, 0.0) Itrunc = 0 StemFlow = pt * Precipitation ThroughFall = Precipitation - Iwet - Idry - Isat - Itrunc - StemFlow Interception = Iwet + Idry + Isat + Itrunc # Non corect for area without any Interception (say open water Cmax -- zero) CmaxZero = Cmax <= 0.0 ThroughFall = pcr.ifthenelse(CmaxZero, Precipitation, ThroughFall) Interception = pcr.ifthenelse(CmaxZero, pcr.scalar(0.0), Interception) StemFlow = pcr.ifthenelse(CmaxZero, pcr.scalar(0.0), StemFlow) # Now corect for maximum potential evap OverEstimate = pcr.ifthenelse( Interception > maxevap, Interception - maxevap, pcr.scalar(0.0) ) Interception = pcr.min(Interception, maxevap) # Add surpluss to the thoughdfall ThroughFall = ThroughFall + OverEstimate return ThroughFall, Interception, StemFlow, CanopyStorage
def getLakeOutflow( self, avgChannelDischarge, length_of_time_step=vos.secondsPerDay() ): # waterHeight (m): temporary variable, a function of storage: minWaterHeight = ( 0.001 ) # (m) Rens used 0.001 m as the limit # this is to make sure there is always lake outflow, # but it will be still limited by available self.waterBodyStorage waterHeight = pcr.cover( pcr.max( minWaterHeight, (self.waterBodyStorage - pcr.cover(self.waterBodyCap, 0.0)) / self.waterBodyArea, ), 0.0, ) # weirWidth (m) : # - estimated from avgOutflow (m3/s) using the bankfull discharge formula # avgOutflow = self.avgOutflow avgOutflow = pcr.ifthenelse( avgOutflow > 0.0, avgOutflow, pcr.max(avgChannelDischarge, self.avgInflow, 0.001), ) # This is needed when new lakes/reservoirs introduced (its avgOutflow is still zero). avgOutflow = pcr.areamaximum(avgOutflow, self.waterBodyIds) # bankfullWidth = pcr.cover(pcr.scalar(4.8) * ((avgOutflow) ** (0.5)), 0.0) weirWidthUsed = bankfullWidth weirWidthUsed = pcr.max( weirWidthUsed, self.minWeirWidth ) # TODO: minWeirWidth based on the GRanD database weirWidthUsed = pcr.cover( pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0.0, weirWidthUsed), 0.0 ) # avgInflow <= lakeOutflow (weirFormula) <= waterBodyStorage lakeOutflowInM3PerSec = pcr.max( self.weirFormula(waterHeight, weirWidthUsed), self.avgInflow ) # unit: m3/s # estimate volume of water relased by lakes lakeOutflow = lakeOutflowInM3PerSec * length_of_time_step # unit: m3 lakeOutflow = pcr.min(self.waterBodyStorage, lakeOutflow) # lakeOutflow = pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0.0, lakeOutflow) lakeOutflow = pcr.ifthen(pcr.scalar(self.waterBodyTyp) == 1, lakeOutflow) # TODO: Consider endorheic lake/basin. No outflow for endorheic lake/basin! return lakeOutflow
def getICs(self, iniItems, iniConditions=None): # print iniItems.groundwaterOptions['storGroundwaterFossilIni'] # initial condition for storGroundwater (unit: m) if iniConditions == None: # when the model just start self.storGroundwater = vos.readPCRmapClone( iniItems.groundwaterOptions['storGroundwaterIni'], self.cloneMap, self.tmpDir, self.inputDir) self.avgAbstraction = vos.readPCRmapClone( iniItems.groundwaterOptions['avgTotalGroundwaterAbstractionIni'], self.cloneMap, self.tmpDir, self.inputDir) else: # during/after spinUp self.storGroundwater = iniConditions['groundwater']['storGroundwater'] self.avgAbstraction = iniConditions['groundwater']['avgTotalGroundwaterAbstractionIni'] # initial condition for storGroundwaterFossil (unit: m) # # Note that storGroundwaterFossil should not be depleted during the spin-up. # if iniItems.groundwaterOptions['storGroundwaterFossilIni'] != "Maximum": #logger.info("Using a pre-defined initial condition for fossil groundwater storage.") self.storGroundwaterFossil = vos.readPCRmapClone( iniItems.groundwaterOptions['storGroundwaterFossilIni'], self.cloneMap, self.tmpDir, self.inputDir) # if self.limitFossilGroundwaterAbstraction and iniItems.groundwaterOptions['storGroundwaterFossilIni'] != "Maximum": #logger.info("The pre-defined initial condition for fossil groundwater is limited by fossilWaterCap (full capacity).") self.storGroundwaterFossil = pcr.min( self.storGroundwaterFossil, self.fossilWaterCap) # if self.limitFossilGroundwaterAbstraction and iniItems.groundwaterOptions['storGroundwaterFossilIni'] == "Maximum": #logger.info("Assuming 'full' fossilWaterCap as the initial condition for fossil groundwater storage.") self.storGroundwaterFossil = self.fossilWaterCap # make sure that active storGroundwater and avgAbstraction cannot be negative # self.storGroundwater = pcr.cover(self.storGroundwater, 0.0) self.storGroundwater = pcr.max(0., self.storGroundwater) self.storGroundwater = pcr.ifthen(self.landmask, self.storGroundwater) # self.avgAbstraction = pcr.cover(self.avgAbstraction, 0.0) self.avgAbstraction = pcr.max(0., self.avgAbstraction) self.avgAbstraction = pcr.ifthen(self.landmask, self.avgAbstraction) # storGroundwaterFossil can be negative (particularly if limitFossilGroundwaterAbstraction == False) self.storGroundwaterFossil = pcr.ifthen(self.landmask, self.storGroundwaterFossil)
def getLakeOutflow(self, avgChannelDischarge, length_of_time_step=vos.secondsPerDay()): # waterHeight (m): temporary variable, a function of storage: # (m) Rens used 0.001 m as the limit # this is to make sure there is always lake outflow, minWaterHeight = 0.001 # but it will be still limited by available self.waterBodyStorage waterHeight = pcr.cover( pcr.max( minWaterHeight, (self.waterBodyStorage - pcr.cover(self.waterBodyCap, 0.0)) / self.waterBodyArea), 0.) # weirWidth (m) : # - estimated from avgOutflow (m3/s) using the bankfull discharge formula # avgOutflow = self.avgOutflow avgOutflow = pcr.ifthenelse( avgOutflow > 0., avgOutflow, pcr.max(avgChannelDischarge, self.avgInflow, 0.001) ) # This is needed when new lakes/reservoirs introduced (its avgOutflow is still zero). avgOutflow = pcr.areamaximum(avgOutflow, self.waterBodyIds) # bankfullWidth = pcr.cover(pcr.scalar(4.8) * ((avgOutflow)**(0.5)), 0.) weirWidthUsed = bankfullWidth # TODO: minWeirWidth based on the GRanD database weirWidthUsed = pcr.max(weirWidthUsed, self.minWeirWidth) weirWidthUsed = pcr.cover( pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0., weirWidthUsed), 0.0) # avgInflow <= lakeOutflow (weirFormula) <= waterBodyStorage lakeOutflowInM3PerSec = pcr.max( self.weirFormula(waterHeight, weirWidthUsed), self.avgInflow) # unit: m3/s # estimate volume of water relased by lakes lakeOutflow = lakeOutflowInM3PerSec * \ length_of_time_step # unit: m3 lakeOutflow = pcr.min(self.waterBodyStorage, lakeOutflow) # lakeOutflow = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0., lakeOutflow) lakeOutflow = pcr.ifthen( pcr.scalar(self.waterBodyTyp) == 1, lakeOutflow) # TODO: Consider endorheic lake/basin. No outflow for endorheic lake/basin! return (lakeOutflow)
def lateralFlow(self): self.calculateLateralFlow() self.soilMoistureThick = self.soilMoistureThick - self.lateralFlowFluxAmount + pcr.upstream( self.ldd, self.lateralFlowFluxAmount) self.upwardSeepageAmount = pcr.max( pcr.scalar(0.0), self.soilMoistureThick - self.soilPorosityThick) self.soilMoistureThick = pcr.min(self.soilMoistureThick, self.soilPorosityThick) self.upwardSeepageFlux = self.amountToFlux(self.upwardSeepageAmount) self.totalUpwardSeepageInUpstreamArea() self.totalSoilMoistureThickInUpstreamArea() self.totalSoilMoistureFractionInUpstreamArea() self.totalSaturatedThickInUpstreamArea() return self.upwardSeepageFlux
def abstractWater(self, potentialAbstractionFlux): # DJ add check on potentialAbstractionFlux >= 0 self.calculateMaximumAbstractionThick() potentialAbstractionFluxAmount = self.fluxToAmount( potentialAbstractionFlux) self.actualAbstractionFluxAmount = pcr.min( self.maximumAbstractionThick, potentialAbstractionFluxAmount) self.soilMoistureThick = self.soilMoistureThick - self.actualAbstractionFluxAmount # conversions self.actualAbstractionFlux = self.amountToFlux( self.actualAbstractionFluxAmount) # for a report self.totalActualAbstractionInUpstreamArea() return self.actualAbstractionFlux
def agriZone_Jarvis(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on Jarvis stress functions - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa - Code for ini-file: 1 """ self.Qa = pcr.max(self.Pe - (self.samax[k] - self.Sa_t[k]), 0) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) self.SaN = pcr.min(self.Sa[k] / self.samax2, 1) self.SuN = self.Su[k] / self.sumax[k] JarvisCoefficients.calcEu( self, k, 1 ) # calculation of Ea based on Jarvis stress functions self.Ea1 = self.Eu self.Fa1 = self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * e ** ( -self.decF[k] * self.SuN ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Fa = ( self.Fa1 + (self.Fa1 / pcr.ifthenelse(self.Fa1 + self.Ea1 > 0, self.Fa1 + self.Ea1, 1)) * self.Sa_diff ) self.Ea = ( self.Ea1 + (self.Ea1 / pcr.ifthenelse(self.Fa1 + self.Ea1 > 0, self.Fa1 + self.Ea1, 1)) * self.Sa_diff ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) - self.Ea - self.Fa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[k] = self.Pe - self.Ea - self.Qa - self.Fa - self.Sa[k] + self.Sa_t[k] self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa self.Fa_[k] = self.Fa
def mapFilling(self, map_with_MV, map_without_MV, method = "window_average"): # ----- method 1: inverse distance method (but too slow) if method == "inverse_distance": logger.info('Extrapolation using "inverse distance" in progress!') # # - interpolation mask for cells without values interpolatedMask = pcr.ifthenelse(\ pcr.defined(map_with_MV),\ pcr.boolean(0),\ pcr.boolean(1),) map_with_MV_intrpl = pcr.inversedistance(interpolatedMask, \ map_with_MV, 2, 1.50, 25) # else: # method 2: using window average logger.info('Extrapolation using "modified window average" in progress!') # map_with_MV_intrpl = 0.70 * pcr.windowaverage(map_with_MV, 1.50) + \ 0.25 * pcr.windowaverage(map_with_MV, 2.00) + \ 0.05 * pcr.windowaverage(map_with_MV, 2.50) + \ pcr.scalar(0.0) # # - interpolated values are only introduced in cells with MV map_with_MV_intrpl = pcr.cover(map_with_MV, map_with_MV_intrpl) # # - calculating weight factor: weight_factor = pcr.scalar(pcr.defined(map_with_MV)) weight_factor = pcr.windowaverage(0.70*weight_factor, 1.50) +\ pcr.windowaverage(0.25*weight_factor, 2.00) +\ pcr.windowaverage(0.05*weight_factor, 2.50) weight_factor = pcr.min(1.0, weight_factor) weight_factor = pcr.max(0.0, weight_factor) weight_factor = pcr.cover(weight_factor, 0.0) # # merge with weight factor merged_map = weight_factor * map_with_MV_intrpl + \ (1.0 - weight_factor) * map_without_MV # # retain the original values and make sure that all values are covered filled_map = pcr.cover(map_with_MV, merged_map) filled_map = pcr.cover(filled_map, map_without_MV) logger.info('Extrapolation is done!') return filled_map
def kinAlphaDynamic(self,watStor): #-given the total water storage in the cell, returns the Q-A relationship # for the kinematic wave and required parameters floodVol= pcr.max(0,watStor-self.channelStorageCapacity) floodFrac, floodZ= self.returnFloodedFraction(floodVol) #-wetted perimeter, cross-sectional area and # corresponding mannings' n wetA= watStor/self.channelLength #-wetted perimeter, alpha and composite manning's n wetPFld= pcr.max(0.,floodFrac*self.cellArea/self.channelLength-\ self.channelWidth)+2.*floodZ wetPCh= self.channelWidth+\ 2.*pcr.min(self.channelDepth,watStor/self.channelArea) wetP= wetPFld+wetPCh manQ= (wetPCh/wetP*self.channelManN**1.5+\ wetPFld/wetP*self.floodplainManN**1.5)**(2./3.) alphaQ= (manQ*wetP**(2./3.)*self.channelGradient**-0.5)**self.betaQ #-returning variables of interest: flooded fraction, cross-sectional area # and alphaQ return floodFrac,floodZ,wetA,alphaQ
def kinAlphaDynamic(self, watStor): #-given the total water storage in the cell, returns the Q-A relationship # for the kinematic wave and required parameters floodVol = pcr.max(0, watStor - self.channelStorageCapacity) floodFrac, floodZ = self.returnFloodedFraction(floodVol) #-wetted perimeter, cross-sectional area and # corresponding mannings' n wetA = watStor / self.channelLength #-wetted perimeter, alpha and composite manning's n wetPFld= pcr.max(0.,floodFrac*self.cellArea/self.channelLength-\ self.channelWidth)+2.*floodZ wetPCh= self.channelWidth+\ 2.*pcr.min(self.channelDepth,watStor/self.channelArea) wetP = wetPFld + wetPCh manQ= (wetPCh/wetP*self.channelManN**1.5+\ wetPFld/wetP*self.floodplainManN**1.5)**(2./3.) alphaQ = (manQ * wetP**(2. / 3.) * self.channelGradient**-0.5)**self.betaQ #-returning variables of interest: flooded fraction, cross-sectional area # and alphaQ return floodFrac, floodZ, wetA, alphaQ
def moveFromChannelToWaterBody( self, newStorageAtLakeAndReservoirs, timestepsToAvgDischarge, maxTimestepsToAvgDischargeShort, length_of_time_step=vos.secondsPerDay(), ): # new lake and/or reservoir storages (m3) newStorageAtLakeAndReservoirs = pcr.cover( pcr.areatotal(newStorageAtLakeAndReservoirs, self.waterBodyIds), 0.0 ) # incoming volume (m3) self.inflow = newStorageAtLakeAndReservoirs - self.waterBodyStorage # inflowInM3PerSec (m3/s) inflowInM3PerSec = self.inflow / length_of_time_step # updating (short term) average inflow (m3/s) ; # - needed to constrain lake outflow: # temp = pcr.max( 1.0, pcr.min( maxTimestepsToAvgDischargeShort, self.timestepsToAvgDischarge - 1.0 + length_of_time_step / vos.secondsPerDay(), ), ) deltaInflow = inflowInM3PerSec - self.avgInflow R = deltaInflow * (length_of_time_step / vos.secondsPerDay()) / temp self.avgInflow = self.avgInflow + R self.avgInflow = pcr.max(0.0, self.avgInflow) # # for the reference, see the "weighted incremental algorithm" in http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance # updating waterBodyStorage (m3) self.waterBodyStorage = newStorageAtLakeAndReservoirs
def moveFromChannelToWaterBody( self, newStorageAtLakeAndReservoirs, timestepsToAvgDischarge, maxTimestepsToAvgDischargeShort, length_of_time_step=vos.secondsPerDay(), ): # new lake and/or reservoir storages (m3) newStorageAtLakeAndReservoirs = pcr.cover( pcr.areatotal(newStorageAtLakeAndReservoirs, self.waterBodyIds), 0.0) # incoming volume (m3) self.inflow = newStorageAtLakeAndReservoirs - self.waterBodyStorage # inflowInM3PerSec (m3/s) inflowInM3PerSec = self.inflow / length_of_time_step # updating (short term) average inflow (m3/s) ; # - needed to constrain lake outflow: # temp = pcr.max( 1.0, pcr.min( maxTimestepsToAvgDischargeShort, self.timestepsToAvgDischarge - 1.0 + length_of_time_step / vos.secondsPerDay(), ), ) deltaInflow = inflowInM3PerSec - self.avgInflow R = deltaInflow * (length_of_time_step / vos.secondsPerDay()) / temp self.avgInflow = self.avgInflow + R self.avgInflow = pcr.max(0.0, self.avgInflow) # # for the reference, see the "weighted incremental algorithm" in http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance # updating waterBodyStorage (m3) self.waterBodyStorage = newStorageAtLakeAndReservoirs
def agriZone_Jarvis(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on Jarvis stress functions - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa - Code for ini-file: 1 """ self.Qa = pcr.max(self.Pe - (self.samax[k] - self.Sa_t[k]), 0) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) self.SaN = pcr.min(self.Sa[k] / self.samax2, 1) self.SuN = self.Su[k] / self.sumax[k] JarvisCoefficients.calcEu( self, k, 1) # calculation of Ea based on Jarvis stress functions self.Ea1 = self.Eu self.Fa1 = self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * e**(-self.decF[k] * self.SuN) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Fa = (self.Fa1 + (self.Fa1 / pcr.ifthenelse( self.Fa1 + self.Ea1 > 0, self.Fa1 + self.Ea1, 1)) * self.Sa_diff) self.Ea = (self.Ea1 + (self.Ea1 / pcr.ifthenelse( self.Fa1 + self.Ea1 > 0, self.Fa1 + self.Ea1, 1)) * self.Sa_diff) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) - self.Ea - self.Fa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[ k] = self.Pe - self.Ea - self.Qa - self.Fa - self.Sa[k] + self.Sa_t[k] self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa self.Fa_[k] = self.Fa
def inverse_gumbel(p_zero, loc, scale, return_period): """ This function computes values for a given return period using the zero probability, location and shape parameters given. """ p = pcr.scalar(1. - 1./return_period) # p_residual is the probability density function of the population consisting of any values above zero p_residual = pcr.min(pcr.max((p - p_zero) / (1.0 - p_zero), 0.0), 1.0) #~ # - alternative equation found on: https://repos.deltares.nl/repos/Hydrology/trunk/GLOFRIS/src/rp_bias_corr.py (see the method inv_gumbel) #~ p_residual = np.minimum(np.maximum((p-p_zero)/(1-p_zero), 0), np.float64(1-1./1e9)) # I think this is the correct equation""" reduced_variate = -pcr.ln(-pcr.ln(p_residual)) flvol = reduced_variate * scale + loc # infinite numbers can occur. reduce these to zero! # if any values become negative due to the statistical extrapolation, fix them to zero (may occur if the sample size for fitting was small and a small return period is requested) flvol = pcr.max(0.0, pcr.cover(flvol, 0.0)) return flvol
def dem_lowering(self, msr, area_polluted): """Calculate the cost and standard deviation of lowering the DEM.""" if msr.settings.loc['msr_type', 1] in ['sidechannel', 'lowering']: # Determine the total volumetric difference over the measure area # Assume that polluted area is < 0.5 m deep, see doc and Middelkoop. dh_tot = self.ref_dem - msr.dem dh_polluted = pcr.ifthen(area_polluted, pcr.min(0.5, dh_tot)) # pcr.aguila(dh_polluted) dV_tot = dh_tot * pcr.cellarea() dV_polluted = dh_polluted * pcr.cellarea() volume_tot = area_total_value(dV_tot, msr.area) volume_polluted = area_total_value(dV_polluted, area_polluted) else: volume_tot = 0 volume_polluted = 0 # Return cost and standard deviation as a dataframe cost_ew = self.cost_input.iloc[0:3, 0:2] cost_init = volume_tot * cost_ew.drop('flpl_low_polluted') cost_polluted = volume_polluted * cost_ew.loc['flpl_low_polluted', :] cost_out = cost_init.copy() cost_out = cost_out.append(cost_polluted) return cost_out
def estimate_bottom_of_bank_storage(self): # influence zone depth (m) influence_zone_depth = 0.5 # bottom_elevation > flood_plain elevation - influence zone bottom_of_bank_storage = self.dem_floodplain - influence_zone_depth #~ # bottom_elevation > river bed #~ bottom_of_bank_storage = pcr.max(self.dem_riverbed, bottom_of_bank_storage) # bottom_elevation > its downstream value bottom_of_bank_storage = pcr.max(bottom_of_bank_storage, \ pcr.cover(pcr.downstream(self.lddMap, bottom_of_bank_storage), bottom_of_bank_storage)) # bottom_elevation >= 0.0 (must be higher than sea level) bottom_of_bank_storage = pcr.max(0.0, bottom_of_bank_storage) # reducing noise bottom_of_bank_storage = pcr.max(bottom_of_bank_storage,\ pcr.windowaverage(bottom_of_bank_storage, 3.0 * pcr.clone().cellSize())) # bottom_elevation < dem_average bottom_of_bank_storage = pcr.min(bottom_of_bank_storage, self.dem_average) bottom_of_bank_storage = pcr.cover(bottom_of_bank_storage, self.dem_average) # TODO: Check again this concept. # TODO: We may want to improve this concept - by incorporating the following # - smooth bottom_elevation # - upstream areas in the mountainous regions and above perrenial stream starting points may also be drained (otherwise water will accumulate) # - bottom_elevation > minimum elevation that is estimated from the maximum of S3 from the PCR-GLOBWB simulation return bottom_of_bank_storage
def __init__(self, iniItems, landmask): object.__init__(self) # cloneMap, temporary directory for the resample process, temporary directory for the modflow process, absolute path for input directory, landmask self.cloneMap = iniItems.cloneMap self.tmpDir = iniItems.tmpDir self.tmp_modflow_dir = iniItems.tmp_modflow_dir self.inputDir = iniItems.globalOptions['inputDir'] self.landmask = landmask # configuration from the ini file self.iniItems = iniItems # topography properties: read several variables from the netcdf file for var in ['dem_minimum','dem_maximum','dem_average','dem_standard_deviation',\ 'slopeLength','orographyBeta','tanslope',\ 'dzRel0000','dzRel0001','dzRel0005',\ 'dzRel0010','dzRel0020','dzRel0030','dzRel0040','dzRel0050',\ 'dzRel0060','dzRel0070','dzRel0080','dzRel0090','dzRel0100']: vars(self)[var] = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['topographyNC'], \ var, self.cloneMap) vars(self)[var] = pcr.cover(vars(self)[var], 0.0) # channel properties: read several variables from the netcdf file for var in ['lddMap','cellAreaMap','gradient','bankfull_width', 'bankfull_depth','dem_floodplain','dem_riverbed']: vars(self)[var] = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['channelNC'], \ var, self.cloneMap) vars(self)[var] = pcr.cover(vars(self)[var], 0.0) # minimum channel width minimum_channel_width = 0.5 # TODO: Define this one in the configuration file self.bankfull_width = pcr.max(minimum_channel_width, self.bankfull_width) #~ # cell fraction if channel water reaching the flood plan # NOT USED YET #~ self.flood_plain_fraction = self.return_innundation_fraction(pcr.max(0.0, self.dem_floodplain - self.dem_minimum)) # coefficient of Manning self.manningsN = vos.readPCRmapClone(self.iniItems.modflowParameterOptions['manningsN'],\ self.cloneMap,self.tmpDir,self.inputDir) # minimum channel gradient minGradient = 0.00005 # TODO: Define this one in the configuration file self.gradient = pcr.max(minGradient, pcr.cover(self.gradient, minGradient)) # correcting lddMap self.lddMap = pcr.ifthen(pcr.scalar(self.lddMap) > 0.0, self.lddMap) self.lddMap = pcr.lddrepair(pcr.ldd(self.lddMap)) # channelLength = approximation of channel length (unit: m) # This is approximated by cell diagonal. cellSizeInArcMin = np.round(pcr.clone().cellSize()*60.) # FIXME: This one will not work if you use the resolution: 0.5, 1.5, 2.5 arc-min verticalSizeInMeter = cellSizeInArcMin*1852. horizontalSizeInMeter = self.cellAreaMap/verticalSizeInMeter self.channelLength = ((horizontalSizeInMeter)**(2)+\ (verticalSizeInMeter)**(2))**(0.5) # option for lakes and reservoir self.onlyNaturalWaterBodies = False if self.iniItems.modflowParameterOptions['onlyNaturalWaterBodies'] == "True": self.onlyNaturalWaterBodies = True # groundwater linear recession coefficient (day-1) ; the linear reservoir concept is still being used to represent fast response flow # particularly from karstic aquifer in mountainous regions self.recessionCoeff = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['groundwaterPropertiesNC'],\ 'recessionCoeff', self.cloneMap) self.recessionCoeff = pcr.cover(self.recessionCoeff,0.00) self.recessionCoeff = pcr.min(1.0000,self.recessionCoeff) # if 'minRecessionCoeff' in iniItems.modflowParameterOptions.keys(): minRecessionCoeff = float(iniItems.modflowParameterOptions['minRecessionCoeff']) else: minRecessionCoeff = 1.0e-4 # This is the minimum value used in Van Beek et al. (2011). self.recessionCoeff = pcr.max(minRecessionCoeff,self.recessionCoeff) # aquifer saturated conductivity (m/day) self.kSatAquifer = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['groundwaterPropertiesNC'],\ 'kSatAquifer', self.cloneMap) self.kSatAquifer = pcr.cover(self.kSatAquifer,pcr.mapmaximum(self.kSatAquifer)) self.kSatAquifer = pcr.max(0.001,self.kSatAquifer) # TODO: Define the minimum value as part of the configuration file # aquifer specific yield (dimensionless) self.specificYield = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['groundwaterPropertiesNC'],\ 'specificYield', self.cloneMap) self.specificYield = pcr.cover(self.specificYield,pcr.mapmaximum(self.specificYield)) self.specificYield = pcr.max(0.010,self.specificYield) # TODO: TO BE CHECKED: The resample process of specificYield self.specificYield = pcr.min(1.000,self.specificYield) # TODO: Define the minimum value as part of the configuration file # estimate of thickness (unit: m) of accesible groundwater totalGroundwaterThickness = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['estimateOfTotalGroundwaterThicknessNC'],\ 'thickness', self.cloneMap) # extrapolation totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness,\ pcr.windowaverage(totalGroundwaterThickness, 1.0)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness,\ pcr.windowaverage(totalGroundwaterThickness, 1.5)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, 0.0) # # set minimum thickness minimumThickness = pcr.scalar(float(\ self.iniItems.modflowParameterOptions['minimumTotalGroundwaterThickness'])) totalGroundwaterThickness = pcr.max(minimumThickness, totalGroundwaterThickness) # # set maximum thickness: 250 m. # TODO: Define this one as part of the ini file maximumThickness = 250. self.totalGroundwaterThickness = pcr.min(maximumThickness, totalGroundwaterThickness) # TODO: Define the maximum value as part of the configuration file # surface water bed thickness (unit: m) bed_thickness = 0.1 # TODO: Define this as part of the configuration file # surface water bed resistance (unit: day) bed_resistance = bed_thickness / (self.kSatAquifer) minimum_bed_resistance = 1.0 # TODO: Define this as part of the configuration file self.bed_resistance = pcr.max(minimum_bed_resistance,\ bed_resistance,) # option to ignore capillary rise self.ignoreCapRise = True if self.iniItems.modflowParameterOptions['ignoreCapRise'] == "False": self.ignoreCapRise = False # a variable to indicate if the modflow has been called or not self.modflow_has_been_called = False # list of the convergence criteria for HCLOSE (unit: m) # - Deltares default's value is 0.001 m # check this value with Jarno self.criteria_HCLOSE = [0.001, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] self.criteria_HCLOSE = sorted(self.criteria_HCLOSE) # list of the convergence criteria for RCLOSE (unit: m3) # - Deltares default's value for their 25 and 250 m resolution models is 10 m3 # check this value with Jarno cell_area_assumption = verticalSizeInMeter * float(pcr.cellvalue(pcr.mapmaximum(horizontalSizeInMeter),1)[0]) self.criteria_RCLOSE = [10., 10.* cell_area_assumption/(250.*250.), 10.* cell_area_assumption/(25.*25.)] self.criteria_RCLOSE = sorted(self.criteria_RCLOSE) # initiate the index for HCLOSE and RCLOSE self.iteration_HCLOSE = 0 self.iteration_RCLOSE = 0 # initiate old style reporting # TODO: remove this! self.initiate_old_style_groundwater_reporting(iniItems)
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 initiate_modflow(self): logger.info("Initializing pcraster modflow.") # initialise self.pcr_modflow = None self.pcr_modflow = pcr.initialise(pcr.clone()) # grid specification - two layer model top_layer_2 = self.dem_average # - thickness of layer 1 is at least 10% of totalGroundwaterThickness # TODO: Change this using Inge's thickness of confining layer. bottom_layer_2 = self.dem_average - 0.10 * self.totalGroundwaterThickness # - thickness of layer 1 should be until 5 m below the river bed bottom_layer_2 = pcr.min(self.dem_riverbed - 5.0, bottom_layer_2) # - make sure that the minimum thickness of layer 2 is at least 5.0 m thickness_of_layer_2 = pcr.max(5.0, top_layer_2 - bottom_layer_2) bottom_layer_2 = top_layer_2 - thickness_of_layer_2 # - thickness of layer 1 is at least 5.0 m thickness_of_layer_1 = pcr.max( 5.0, self.totalGroundwaterThickness - thickness_of_layer_2) bottom_layer_1 = bottom_layer_2 - thickness_of_layer_1 self.pcr_modflow.createBottomLayer(bottom_layer_1, bottom_layer_2) self.pcr_modflow.addLayer(top_layer_2) # specification for the boundary condition (IBOUND, BAS package) # - active cells only in landmask # - constant head for outside the landmask ibound = pcr.ifthen(self.landmask, pcr.nominal(1)) ibound = pcr.cover(ibound, pcr.nominal(-1)) self.pcr_modflow.setBoundary(ibound, 1) # upper layer self.pcr_modflow.setBoundary(ibound, 2) # lower layer # specification for conductivities (BCF package) horizontal_conductivity = self.kSatAquifer # unit: m/day # set the minimum value for transmissivity; (Deltares's default value: 10 m2/day) minimimumTransmissivity = 10. # - layer 2 (upper layer) horizontal_conductivity_layer_2 = pcr.max(minimimumTransmissivity, \ horizontal_conductivity * thickness_of_layer_2) / thickness_of_layer_2 vertical_conductivity_layer_2 = pcr.min(self.kSatAquifer, 0.00000000000000005) * self.cellAreaMap/\ (pcr.clone().cellSize()*pcr.clone().cellSize()) self.pcr_modflow.setConductivity(00, horizontal_conductivity_layer_2, \ vertical_conductivity_layer_2, 2) # - layer 1 (lower layer) horizontal_conductivity_layer_1 = pcr.max(minimimumTransmissivity, \ horizontal_conductivity * thickness_of_layer_1) / thickness_of_layer_1 vertical_conductivity_layer_1 = vertical_conductivity_layer_2 # dummy values self.pcr_modflow.setConductivity(00, horizontal_conductivity_layer_1, \ vertical_conductivity_layer_1, 1) # specification for storage coefficient # - correction due to the usage of lat/lon coordinates primary = pcr.cover( self.specificYield * self.cellAreaMap / (pcr.clone().cellSize() * pcr.clone().cellSize()), 0.0) primary = pcr.max(1e-20, primary) secondary = primary # dummy values as we used layer type 00 self.pcr_modflow.setStorage(primary, secondary, 1) self.pcr_modflow.setStorage(primary, secondary, 2) # set drain package self.set_drain_package()
def dynamic(self): ##################### # * dynamic section # ##################### #-evaluation of the current date: return current month and the time step used #-reading in fluxes over land and water area for current time step [m/d] # and read in reservoir demand and surface water extraction [m3] try: self.landSurfaceQ= clippedRead.get(pcrm.generateNameT(landSurfaceQFileName,self.currentTimeStep())) except: pass try: self.potWaterSurfaceQ= clippedRead.get(pcrm.generateNameT(waterSurfaceQFileName,self.currentTimeStep())) except: pass #-surface water extraction and reservoir demand currently set to zero, should # be computed automatically and updated to reservoirs self.potSurfaceWaterExtraction= pcr.spatial(pcr.scalar(0.)) #self.waterBodies.demand= #self.reservoirDemandTSS.assignID(self.waterBodies.ID,self.currentTimeStep(),0.)*self.timeSec #-initialization of cumulative values of actual water extractions self.actWaterSurfaceQ= pcr.spatial(pcr.scalar(0.)) self.actSurfaceWaterExtraction= pcr.spatial(pcr.scalar(0.)) #-definition of sub-loop for routing scheme - explicit scheme has to satisfy Courant condition timeLimit= pcr.cellvalue(pcr.mapminimum((pcr.cover(pcr.ifthen(self.waterBodies.distribution == 0,\ self.channelLength/self.flowVelocity),\ self.timeSec/self.nrIterDefault)*self.timeSec/self.nrIterDefault)**0.5),1)[0] nrIter= int(self.timeSec/timeLimit) nrIter= min(nrIter,int(self.timeSec/300.)) while float(self.timeSec/nrIter) % 1 <> 0: nrIter+= 1 deltaTime= self.timeSec/nrIter #-sub-loop for current time step if self.currentDate.day == 1 or nrIter >= 24: print '\n*\tprocessing %s, currently using %d substeps of %d seconds\n' % \ (self.currentDate.date(),nrIter,deltaTime) #-update discharge and storage for nrICur in range(nrIter): #-initializing discharge for the current sub-timestep and fill in values # for channels and at outlets of waterbodies # * channels * estQ= pcr.ifthenelse((self.actualStorage > 0.) & (self.waterBodies.distribution == 0) ,\ (self.wettedArea/self.alphaQ)**(1./self.betaQ),0.) #estQ= pcr.ifthenelse((self.actualStorage > 0.) & (self.waterBodies.distribution == 0) ,\ #0.5*(self.Q+(self.wettedArea/self.alphaQ)**(1./self.betaQ)),0.) #estQ= pcr.min(estQ,self.actualStorage/deltaTime) self.report(estQ,'results/qest') self.Q= pcr.spatial(pcr.scalar(0.)) self.Q= pcr.ifthenelse(self.waterBodies.distribution == 0,\ pcr.kinematic(self.channelLDD,estQ,0.,self.alphaQ,\ self.betaQ,1,deltaTime,self.channelLength),self.Q) # * water bodies * self.waterBodies.dischargeUpdate() self.Q= self.waterBodies.returnMapValue(self.Q,self.waterBodies.actualQ) #-fluxes and resulting change in storage: first the local fluxes are evaluated # and aggregated over the water bodies where applicable; this includes the specific runoff [m/day/m2] # from input and the estimated extraction from surface water as volume per day [m3/day]; # specific runoff from the land surface is always positive whereas the fluxes over the water surface # are potential, including discharge, and are adjusted to match the availabe storage; to this end, # surface water storage and fluxes over water bodies are totalized and assigned to the outlet; # discharge is updated in a separate step, after vertical fluxes are compared to the actual storage deltaActualStorage= ((self.landFraction*self.landSurfaceQ+\ self.waterFraction*self.potWaterSurfaceQ)*self.cellArea-\ self.potSurfaceWaterExtraction)*float(self.duration)/nrIter deltaActualStorage= pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.ifthenelse(self.waterBodies.location != 0,\ pcr.areatotal(deltaActualStorage,self.waterBodies.distribution),0),\ deltaActualStorage) adjustmentRatio= pcr.ifthenelse(deltaActualStorage < 0.,\ pcr.min(1.,-self.actualStorage/deltaActualStorage),1.) self.actWaterSurfaceQ+= adjustmentRatio*self.potWaterSurfaceQ self.actSurfaceWaterExtraction+= adjustmentRatio*self.actSurfaceWaterExtraction deltaActualStorage*= adjustmentRatio #-local water balance check if testLocalWaterBalance: differenceActualStorage= self.actualStorage differenceActualStorage+= deltaActualStorage #-overall water balance check: net input self.cumulativeDeltaStorage+= pcr.catchmenttotal(deltaActualStorage,self.LDD) #-update storage first with local changes, then balance discharge with storage and update storage # with lateral flow and return value to water bodies self.actualStorage+= deltaActualStorage self.actualStorage= pcr.max(0.,self.actualStorage) self.Q= pcr.min(self.Q,self.actualStorage/deltaTime) deltaActualStorage= (-self.Q+pcr.upstream(self.LDD,self.Q))*deltaTime deltaActualStorage= pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.ifthenelse(self.waterBodies.location != 0,\ pcr.areatotal(deltaActualStorage,self.waterBodies.distribution),0),\ deltaActualStorage) self.actualStorage+= deltaActualStorage self.actualStorage= pcr.max(0.,self.actualStorage) self.waterBodies.actualStorage= self.waterBodies.retrieveMapValue(self.actualStorage) #-flooded fraction returned floodedFraction,floodedDepth,\ self.wettedArea,self.alphaQ= self.kinAlphaComposite(self.actualStorage,self.floodplainMask) self.wettedArea= self.waterBodies.returnMapValue(self.wettedArea,\ self.waterBodies.channelWidth+2.*self.waterBodies.updateWaterHeight()) self.waterFraction= pcr.ifthenelse(self.waterBodies.distribution == 0,\ pcr.max(self.waterFractionMask,floodedFraction),self.waterFractionMask) self.landFraction= pcr.max(0.,1.-self.waterFraction) self.flowVelocity= pcr.ifthenelse(self.wettedArea > 0,self.Q/self.wettedArea,0.) #-local water balance check if testLocalWaterBalance: differenceActualStorage+= deltaActualStorage differenceActualStorage-= self.actualStorage totalDifference= pcr.cellvalue(pcr.maptotal(differenceActualStorage),1)[0] minimumDifference= pcr.cellvalue(pcr.mapminimum(differenceActualStorage),1)[0] maximumDifference= pcr.cellvalue(pcr.mapmaximum(differenceActualStorage),1)[0] if abs(totalDifference) > 1.e-3: print 'water balance error: total %e; min %e; max %e' %\ (totalDifference,minimumDifference,maximumDifference) if reportLocalWaterBalance: pcr.report(differenceActualStorage,'mbe_%s.map' % self.currentDate.date()) #-overall water balance check: updating cumulative discharge and total storage [m3] self.totalDischarge+= self.Q*deltaTime self.totalStorage= pcr.catchmenttotal(self.actualStorage,self.LDD) #-check on occurrence of last day and report mass balance if self.currentDate == self.endDate: #-report initial maps pcr.report(self.Q,self.QIniMap) pcr.report(self.actualStorage,self.actualStorageIniMap) #-return relative and absolute water balance error per cell and # as total at basin outlets self.totalDischarge= pcr.ifthen((self.waterBodies.distribution == 0) | \ (self.waterBodies.location != 0),self.totalDischarge) self.cumulativeDeltaStorage= pcr.ifthen((self.waterBodies.distribution == 0) | \ (self.waterBodies.location != 0),self.cumulativeDeltaStorage) massBalanceError= self.totalStorage+self.totalDischarge-\ self.cumulativeDeltaStorage relMassBalanceError= 1.+pcr.ifthenelse(self.cumulativeDeltaStorage <> 0., massBalanceError/self.cumulativeDeltaStorage,0.) totalMassBalanceError= pcr.cellvalue(pcr.maptotal(pcr.ifthen(self.basinOutlet,\ massBalanceError)),1)[0] totalCumulativeDeltaStorage= pcr.cellvalue(pcr.maptotal(pcr.ifthen(self.basinOutlet,\ self.cumulativeDeltaStorage)),1)[0] if totalCumulativeDeltaStorage > 0: totalRelativeMassBalanceError= 1.+totalMassBalanceError/totalCumulativeDeltaStorage else: totalRelativeMassBalanceError= 1. #-report maps and echo value pcr.report(massBalanceError,mbeFileName) pcr.report(relMassBalanceError,mbrFileName) print '\n*\ttotal global mass balance error [m3]: %8.3g' % totalMassBalanceError print '\n*\trelative global mass balance error [-]: %5.3f' % totalRelativeMassBalanceError #-echo to screen: total mass balance error and completion of run print '\trun completed' #-end of day: return states and fluxes #-get surface water attributes? if getSurfaceWaterAttributes: #-compute the following secondary variables: # surface water area [m2]: area given dynamic surface water fraction # residence time [days]: volume over discharge, assigned -1 in case discharge is zero # surface water depth [m], weighed by channel and floodplain volume surfaceWaterArea= self.waterFraction*self.cellArea surfaceWaterArea= pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.ifthenelse(self.waterBodies.location != 0,\ pcr.areatotal(surfaceWaterArea,self.waterBodies.distribution),0),\ surfaceWaterArea) surfaceWaterResidenceTime= pcr.ifthenelse(self.Q > 0.,self.actualStorage/(self.Q*self.timeSec),-1) surfaceWaterDepth= pcr.ifthenelse(self.actualStorage > 0.,\ pcr.max(0.,self.actualStorage-self.channelStorageCapacity)**2/\ (self.actualStorage*surfaceWaterArea),0.) surfaceWaterDepth+= pcr.ifthenelse(self.actualStorage > 0.,\ pcr.min(self.channelStorageCapacity,self.actualStorage)**2/(self.waterFractionMask*\ self.cellArea*self.actualStorage),0.) #-reports: values at outlet of lakes or reservoirs are assigned to their full extent self.report(pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.areamaximum(surfaceWaterArea,self.waterBodies.distribution),surfaceWaterArea),\ surfaceWaterAreaFileName) self.report(pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.areamaximum(surfaceWaterResidenceTime,self.waterBodies.distribution),surfaceWaterResidenceTime),\ surfaceWaterResidenceTimeFileName) self.report(pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.areamaximum(surfaceWaterDepth,self.waterBodies.distribution),surfaceWaterDepth),\ surfaceWaterDepthFileName) #-reports on standard output: values at outlet of lakes or reservoirs are assigned to their full extent self.report(pcr.ifthenelse(self.waterBodies.distribution != 0, pcr.areamaximum(self.flowVelocity,self.waterBodies.distribution),self.flowVelocity),flowVelocityFileName) self.report(pcr.ifthenelse(self.waterBodies.distribution != 0, pcr.areamaximum(self.Q,self.waterBodies.distribution),self.Q),QFileName) self.report(pcr.ifthenelse(self.waterBodies.distribution == 0,\ floodedFraction,0.),floodedFractionFileName) self.report(pcr.ifthenelse(self.waterBodies.distribution == 0,\ floodedDepth,0.),floodedDepthFileName) self.report(self.actualStorage,actualStorageFileName) #-update date for time step and report relevant daily output self.currentDate= self.currentDate+datetime.timedelta(self.duration)
def complexreservoir( waterlevel, ReserVoirLocs, LinkedReserVoirLocs, ResArea, ResThreshold, ResStorFunc, ResOutflowFunc, sh, hq, res_b, res_e, inflow, precip, pet, ReservoirComplexAreas, JDOY, timestepsecs=86400, ): mv = -999.0 inflow = pcr.ifthen(pcr.boolean(ReserVoirLocs), inflow) prec_av = pcr.ifthen( pcr.boolean(ReserVoirLocs), pcr.areaaverage(precip, ReservoirComplexAreas) ) pet_av = pcr.ifthen( pcr.boolean(ReserVoirLocs), pcr.areaaverage(pet, ReservoirComplexAreas) ) np_reslocs = pcr.pcr2numpy(ReserVoirLocs, 0.0) np_linkedreslocs = pcr.pcr2numpy(LinkedReserVoirLocs, 0.0) _outflow = [] nr_loop = np.max([int(timestepsecs / 21600), 1]) for n in range(0, nr_loop): np_waterlevel = pcr.pcr2numpy(waterlevel, np.nan) np_waterlevel_lower = np_waterlevel.copy() for val in np.unique(np_linkedreslocs): if val > 0: np_waterlevel_lower[np_linkedreslocs == val] = np_waterlevel[ np.where(np_reslocs == val) ] diff_wl = np_waterlevel - np_waterlevel_lower diff_wl[np.isnan(diff_wl)] = mv np_waterlevel_lower[np.isnan(np_waterlevel_lower)] = mv pcr_diff_wl = pcr.numpy2pcr(pcr.Scalar, diff_wl, mv) pcr_wl_lower = pcr.numpy2pcr(pcr.Scalar, np_waterlevel_lower, mv) storage_start = pcr.ifthenelse( ResStorFunc == 1, ResArea * waterlevel, lookupResFunc(ReserVoirLocs, waterlevel, sh, "0-1"), ) outflow = pcr.ifthenelse( ResOutflowFunc == 1, lookupResRegMatr(ReserVoirLocs, waterlevel, hq, JDOY), pcr.ifthenelse( pcr_diff_wl >= 0, pcr.max(res_b * (waterlevel - ResThreshold) ** res_e, 0), pcr.min(-1 * res_b * (pcr_wl_lower - ResThreshold) ** res_e, 0), ), ) np_outflow = pcr.pcr2numpy(outflow, np.nan) np_outflow_linked = np_reslocs * 0.0 with np.errstate(invalid="ignore"): if np_outflow[np_outflow < 0] is not None: np_outflow_linked[ np.in1d(np_reslocs, np_linkedreslocs[np_outflow < 0]).reshape( np_linkedreslocs.shape ) ] = np_outflow[np_outflow < 0] outflow_linked = pcr.numpy2pcr(pcr.Scalar, np_outflow_linked, 0.0) fl_nr_loop = float(nr_loop) storage = ( storage_start + (inflow * timestepsecs / fl_nr_loop) + (prec_av / fl_nr_loop / 1000.0) * ResArea - (pet_av / fl_nr_loop / 1000.0) * ResArea - (pcr.cover(outflow, 0.0) * timestepsecs / fl_nr_loop) + (pcr.cover(outflow_linked, 0.0) * timestepsecs / fl_nr_loop) ) waterlevel = pcr.ifthenelse( ResStorFunc == 1, waterlevel + (storage - storage_start) / ResArea, lookupResFunc(ReserVoirLocs, storage, sh, "1-0"), ) np_outflow_nz = np_outflow * 0.0 with np.errstate(invalid="ignore"): np_outflow_nz[np_outflow > 0] = np_outflow[np_outflow > 0] _outflow.append(np_outflow_nz) outflow_av_temp = np.average(_outflow, 0) outflow_av_temp[np.isnan(outflow_av_temp)] = mv outflow_av = pcr.numpy2pcr(pcr.Scalar, outflow_av_temp, mv) return waterlevel, outflow_av, prec_av, pet_av, storage
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 getReservoirOutflow(self,\ avgChannelDischarge,length_of_time_step,downstreamDemand): # avgOutflow (m3/s) avgOutflow = self.avgOutflow # The following is needed when new lakes/reservoirs introduced (its avgOutflow is still zero). #~ # - alternative 1 #~ avgOutflow = pcr.ifthenelse(\ #~ avgOutflow > 0.,\ #~ avgOutflow, #~ pcr.max(avgChannelDischarge, self.avgInflow, 0.001)) # - alternative 2 avgOutflow = pcr.ifthenelse(\ avgOutflow > 0.,\ avgOutflow, pcr.max(avgChannelDischarge, self.avgInflow)) avgOutflow = pcr.ifthenelse(\ avgOutflow > 0.,\ avgOutflow, pcr.downstream(self.lddMap, avgOutflow)) avgOutflow = pcr.areamaximum(avgOutflow, self.waterBodyIds) # calculate resvOutflow (m2/s) (based on reservoir storage and avgDischarge): # - using reductionFactor in such a way that: # - if relativeCapacity < minResvrFrac : release is terminated # - if relativeCapacity > maxResvrFrac : longterm average reductionFactor = \ pcr.cover(\ pcr.min(1., pcr.max(0., \ self.waterBodyStorage - self.minResvrFrac*self.waterBodyCap)/\ (self.maxResvrFrac - self.minResvrFrac)*self.waterBodyCap),0.0) # resvOutflow = reductionFactor * avgOutflow * length_of_time_step # unit: m3 # maximum release <= average inflow (especially during dry condition) resvOutflow = pcr.max(0, pcr.min(resvOutflow, self.avgInflow * length_of_time_step)) # unit: m3 # downstream demand (m3/s) # reduce demand if storage < lower limit reductionFactor = vos.getValDivZero( downstreamDemand, self.minResvrFrac * self.waterBodyCap, vos.smallNumber) reductionFactor = pcr.cover(reductionFactor, 0.0) downstreamDemand = pcr.min(downstreamDemand, downstreamDemand * reductionFactor) # resvOutflow > downstreamDemand resvOutflow = pcr.max(resvOutflow, downstreamDemand * length_of_time_step) # unit: m3 # floodOutflow: additional release if storage > upper limit ratioQBankfull = 2.3 estmStorage = pcr.max(0., self.waterBodyStorage - resvOutflow) floodOutflow = \ pcr.max(0.0, estmStorage - self.waterBodyCap) +\ pcr.cover(\ pcr.max(0.0, estmStorage - self.maxResvrFrac*\ self.waterBodyCap)/\ ((1.-self.maxResvrFrac)*self.waterBodyCap),0.0)*\ pcr.max(0.0,ratioQBankfull*avgOutflow* vos.secondsPerDay()-\ resvOutflow) floodOutflow = pcr.max(0.0, pcr.min(floodOutflow,\ estmStorage - self.maxResvrFrac*\ self.waterBodyCap*0.75)) # maximum limit of floodOutflow: bring the reservoir storages only to 3/4 of upper limit capacities # update resvOutflow after floodOutflow resvOutflow = pcr.cover(resvOutflow , 0.0) +\ pcr.cover(floodOutflow, 0.0) # maximum release if storage > upper limit : bring the reservoir storages only to 3/4 of upper limit capacities resvOutflow = pcr.ifthenelse(self.waterBodyStorage > self.maxResvrFrac*self.waterBodyCap,\ pcr.min(resvOutflow,\ pcr.max(0,self.waterBodyStorage - \ self.maxResvrFrac*self.waterBodyCap*0.75)), resvOutflow) # if storage > upper limit : resvOutflow > avgInflow resvOutflow = pcr.ifthenelse(self.waterBodyStorage > self.maxResvrFrac*self.waterBodyCap,\ pcr.max(0.0, resvOutflow, self.avgInflow), resvOutflow) # resvOutflow < waterBodyStorage resvOutflow = pcr.min(self.waterBodyStorage, resvOutflow) resvOutflow = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0., resvOutflow) resvOutflow = pcr.ifthen( pcr.scalar(self.waterBodyTyp) == 2, resvOutflow) return (resvOutflow) # unit: m3
def readSoilMapOfFAO(self, iniItems, optionDict = None): # a dictionary/section of options that will be used if optionDict == None: optionDict = iniItems._sections["landSurfaceOptions"] #iniItems.landSurfaceOptions # soil variable names given either in the ini or netCDF file: soilParameters = ['airEntryValue1','airEntryValue2', 'poreSizeBeta1','poreSizeBeta2', 'resVolWC1','resVolWC2', 'satVolWC1','satVolWC2', 'KSat1','KSat2', 'percolationImp'] if optionDict['soilPropertiesNC'] == str(None): for var in soilParameters: input = optionDict[str(var)] vars(self)[var] = \ vos.readPCRmapClone(input,self.cloneMap,\ self.tmpDir,self.inputDir) vars(self)[var] = pcr.scalar(vars(self)[var]) if input == "percolationImp": vars(self)[var] = pcr.cover(vars(self)[var], 0.0) # extrapolation # - TODO: Make a general extrapolation option as a function in the virtualOS.py vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 0.75)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], 0.0) else: soilPropertiesNC = vos.getFullPath(\ optionDict['soilPropertiesNC'], self.inputDir) for var in soilParameters: vars(self)[var] = vos.netcdf2PCRobjCloneWithoutTime(\ soilPropertiesNC,var, \ cloneMapFileName = self.cloneMap) if var == "percolationImp": vars(self)[var] = pcr.cover(vars(self)[var], 0.0) # extrapolation # - TODO: Make a general extrapolation option as a function in the virtualOS.py vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 0.75)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00)) vars(self)[var] = pcr.cover(vars(self)[var], 0.01) # make sure that resVolWC1 <= satVolWC1 self.resVolWC1 = pcr.min(self.resVolWC1, self.satVolWC1) self.resVolWC2 = pcr.min(self.resVolWC2, self.satVolWC2) if self.numberOfLayers == 2: self.satVolMoistContUpp = self.satVolWC1 # saturated volumetric moisture content (m3.m-3) self.satVolMoistContLow = self.satVolWC2 self.resVolMoistContUpp = self.resVolWC1 # residual volumetric moisture content (m3.m-3) self.resVolMoistContLow = self.resVolWC2 self.airEntryValueUpp = self.airEntryValue1 # air entry value (m) according to soil water retention curve of Clapp & Hornberger (1978) self.airEntryValueLow = self.airEntryValue2 self.poreSizeBetaUpp = self.poreSizeBeta1 # pore size distribution parameter according to Clapp & Hornberger (1978) self.poreSizeBetaLow = self.poreSizeBeta2 self.kSatUpp = self.KSat1 # saturated hydraulic conductivity (m.day-1) self.kSatLow = self.KSat2 if self.numberOfLayers == 3: self.satVolMoistContUpp000005 = self.satVolWC1 self.satVolMoistContUpp005030 = self.satVolWC1 self.satVolMoistContLow030150 = self.satVolWC2 self.resVolMoistContUpp000005 = self.resVolWC1 self.resVolMoistContUpp005030 = self.resVolWC1 self.resVolMoistContLow030150 = self.resVolWC2 self.airEntryValueUpp000005 = self.airEntryValue1 self.airEntryValueUpp005030 = self.airEntryValue1 self.airEntryValueLow030150 = self.airEntryValue2 self.poreSizeBetaUpp000005 = self.poreSizeBeta1 self.poreSizeBetaUpp005030 = self.poreSizeBeta1 self.poreSizeBetaLow030150 = self.poreSizeBeta2 self.kSatUpp000005 = self.KSat1 self.kSatUpp005030 = self.KSat1 self.kSatLow030150 = self.KSat2 self.percolationImp = pcr.cover(self.percolationImp, 0.0) # fractional area where percolation to groundwater store is impeded (dimensionless) # soil thickness and storage variable names # as given either in the ini or netCDF file: soilStorages = ['firstStorDepth', 'secondStorDepth', 'soilWaterStorageCap1','soilWaterStorageCap2'] if optionDict['soilPropertiesNC'] == str(None): for var in soilStorages: input = optionDict[str(var)] temp = str(var)+'Inp' vars(self)[temp] = vos.readPCRmapClone(input,\ self.cloneMap, self.tmpDir,self.inputDir) # extrapolation # - TODO: Make a general extrapolation option as a function in the virtualOS.py vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 0.75)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], 0.0) else: soilPropertiesNC = vos.getFullPath(\ optionDict['soilPropertiesNC'], self.inputDir) for var in soilStorages: temp = str(var)+'Inp' vars(self)[temp] = vos.netcdf2PCRobjCloneWithoutTime(\ soilPropertiesNC,var, \ cloneMapFileName = self.cloneMap) # extrapolation # - TODO: Make a general extrapolation option as a function in the virtualOS.py vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 0.75)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05)) vars(self)[temp] = pcr.cover(vars(self)[temp], 0.0) # layer thickness if self.numberOfLayers == 2: self.thickUpp = (0.30/0.30)*self.firstStorDepthInp self.thickLow = (1.20/1.20)*self.secondStorDepthInp if self.numberOfLayers == 3: self.thickUpp000005 = (0.05/0.30)*self.firstStorDepthInp self.thickUpp005030 = (0.25/0.30)*self.firstStorDepthInp self.thickLow030150 = (1.20/1.20)*self.secondStorDepthInp # soil storage if self.numberOfLayers == 2: #~ self.storCapUpp = (0.30/0.30)*self.soilWaterStorageCap1Inp #~ self.storCapLow = (1.20/1.20)*self.soilWaterStorageCap2Inp # 22 Feb 2014: We can calculate this based on thickness and porosity. self.storCapUpp = self.thickUpp * \ (self.satVolMoistContUpp - self.resVolMoistContUpp) self.storCapLow = self.thickLow * \ (self.satVolMoistContLow - self.resVolMoistContLow) self.rootZoneWaterStorageCap = self.storCapUpp + \ self.storCapLow # This is called as WMAX in the original pcrcalc script. if self.numberOfLayers == 3: self.storCapUpp000005 = self.thickUpp000005 * \ (self.satVolMoistContUpp000005 - self.resVolMoistContUpp000005) self.storCapUpp005030 = self.thickUpp005030 * \ (self.satVolMoistContUpp005030 - self.resVolMoistContUpp005030) self.storCapLow030150 = self.thickLow030150 * \ (self.satVolMoistContLow030150 - self.resVolMoistContLow030150) self.rootZoneWaterStorageCap = self.storCapUpp000005 + \ self.storCapUpp005030 + \ self.storCapLow030150
def simplereservoir( storage, inflow, ResArea, maxstorage, target_perc_full, maximum_Q, demand, minimum_full_perc, ReserVoirLocs, precip, pet, ReservoirSimpleAreas, timestepsecs=86400, ): """ :param storage: initial storage m^3 :param inflow: inflow m^3/s :param maxstorage: maximum storage (above which water is spilled) m^3 :param target_perc_full: target fraction full (of max storage) - :param maximum_Q: maximum Q to release m^3/s if below spillway :param demand: water demand (all combined) m^3/s :param minimum_full_perc: target minimum full fraction (of max storage) - :param ReserVoirLocs: map with reservoir locations :param timestepsecs: timestep of the model in seconds (default = 86400) :return: storage (m^3), outflow (m^3/s), PercentageFull (0-1), Release (m^3/sec) """ inflow = pcr.ifthen(pcr.boolean(ReserVoirLocs), inflow) prec_av = pcr.cover( pcr.ifthen( pcr.boolean(ReserVoirLocs), pcr.areaaverage(precip, ReservoirSimpleAreas) ), pcr.scalar(0.0), ) pet_av = pcr.cover( pcr.ifthen( pcr.boolean(ReserVoirLocs), pcr.areaaverage(pet, ReservoirSimpleAreas) ), pcr.scalar(0.0), ) oldstorage = storage storage = ( storage + (inflow * timestepsecs) + (prec_av / 1000.0) * ResArea - (pet_av / 1000.0) * ResArea ) percfull = ((storage + oldstorage) * 0.5) / maxstorage # first determine minimum (environmental) flow using a simple sigmoid curve to scale for target level fac = sCurve(percfull, a=minimum_full_perc, c=30.0) demandRelease = pcr.min(fac * demand * timestepsecs, storage) storage = storage - demandRelease # Re-determine percfull percfull = ((storage + oldstorage) * 0.5) / maxstorage wantrel = pcr.max(0.0, storage - (maxstorage * target_perc_full)) # Assume extra maximum Q if spilling overflowQ = (percfull - 1.0) * (storage - maxstorage) torelease = pcr.min(wantrel, overflowQ + maximum_Q * timestepsecs) storage = storage - torelease outflow = (torelease + demandRelease) / timestepsecs percfull = storage / maxstorage return storage, outflow, percfull, prec_av, pet_av, demandRelease / timestepsecs
pcr.windowaverage(fraction_reserved_recharge, 0.5)) fraction_reserved_recharge = pcr.cover(fraction_reserved_recharge, \ pcr.windowaverage(fraction_reserved_recharge, 0.5)) fraction_reserved_recharge = pcr.cover(fraction_reserved_recharge, \ pcr.windowaverage(fraction_reserved_recharge, 0.5)) fraction_reserved_recharge = pcr.cover(fraction_reserved_recharge, \ pcr.windowaverage(fraction_reserved_recharge, 0.5)) fraction_reserved_recharge = pcr.cover(fraction_reserved_recharge, \ pcr.windowaverage(fraction_reserved_recharge, 0.5)) fraction_reserved_recharge = pcr.cover(fraction_reserved_recharge, \ pcr.windowaverage(fraction_reserved_recharge, 0.5)) fraction_reserved_recharge = pcr.cover(fraction_reserved_recharge, 0.1) # - set minimum value to 0.10 fraction_reserved_recharge = pcr.max(0.10, fraction_reserved_recharge) # - set maximum value to 0.75 fraction_reserved_recharge = pcr.min(0.75, fraction_reserved_recharge) # areal_groundwater_abstraction (unit: m/year) #~ groundwater_abstraction = pcr.cover(pcr.readmap("/nfsarchive/edwin-emergency-backup-DO-NOT-DELETE/rapid/edwin/05min_runs_results/2015_04_27/non_natural_2015_04_27/global/analysis/avg_values_1990_to_2010/totalGroundwaterAbstraction_annuaTot_output_1990to2010.map"), 0.0) groundwater_abstraction = pcr.scalar(0.0) for year in range(start_year, end_year + 1, 1): date_input_in_string = str(year) + "-12-31" netcdf_file = "/nfsarchive/edwin-emergency-backup-DO-NOT-DELETE/rapid/edwin/05min_runs_results/2015_04_27/non_natural_2015_04_27/global/netcdf/totalGroundwaterAbstraction_annuaTot_output_1981to2010.nc" variable_name = "total_groundwater_abstraction" groundwater_abstraction += vos.netcdf2PCRobjClone(ncFile = netcdf_file, varName = variable_name, dateInput = date_input_in_string,\ useDoy = None, cloneMapFileName = None,\ LatitudeLongitude = True,\ specificFillValue = None) areal_groundwater_abstraction = pcr.cover(pcr.areatotal(groundwater_abstraction * cell_area, class_map)/pcr.areatotal(cell_area, class_map), 0.0)
import pcraster as pcr import netCDF4 as nc # clone map clone_map_file_name = "/projects/0/dfguu/data/hydroworld/PCRGLOBWB20/input5min/routing/lddsound_05min.map" pcr.setclone(clone_map_file_name) # aquifer thickness map: aquifer_thickness_file_name = "/projects/0/dfguu/users/edwin/data/aquifer_properties/thickness_05min.map" aquifer_thickness = pcr.readmap(aquifer_thickness_file_name) # extent of confining layer extent_of_confining_layer_file_name = "/home/edwin/data/inge_confining_layer_parameters/conflayers4.map" confining_layer_extent = pcr.boolean(pcr.readmap(extent_of_confining_layer_file_name)) # thickness of confining layer = 10 percent from the first 250 m confining_layer_thickness = pcr.ifthen(confining_layer_extent, pcr.min(250.0, aquifer_thickness)) * 0.10 # extrapolate confining_layer_thickness = pcr.cover(confining_layer_thickness, pcr.windowaverage(pcr.cover(confining_layer_thickness, 0.0), 0.50)) confining_layer_thickness = pcr.cover(confining_layer_thickness, 0.0) confining_layer_thickness_output_filename = "/home/edwin/data/inge_confining_layer_parameters/confining_layer_thickness_edwin.map" pcr.report(confining_layer_thickness, confining_layer_thickness_output_filename) # masking only to the landmask landmask_file_name = "/projects/0/dfguu/data/hydroworld/PCRGLOBWB20/input5min/routing/lddsound_05min.map" landmask = pcr.defined(landmask_file_name) confining_layer_thickness_masked_output_filename = "/home/edwin/data/inge_confining_layer_parameters/confining_layer_thickness_edwin.masked.map" pcr.report(pcr.ifthen(landmask, confining_layer_thickness), confining_layer_thickness_masked_output_filename)
def getParameterFiles( self, currTimeStep, cellArea, ldd, initial_condition_dictionary=None ): # parameters for Water Bodies: fracWat # waterBodyIds # waterBodyOut # waterBodyArea # waterBodyTyp # waterBodyCap # cell surface area (m2) and ldd self.cellArea = cellArea ldd = pcr.ifthen(self.landmask, ldd) # date used for accessing/extracting water body information date_used = currTimeStep.fulldate year_used = currTimeStep.year if self.onlyNaturalWaterBodies == True: date_used = self.dateForNaturalCondition year_used = self.dateForNaturalCondition[0:4] # fracWat = fraction of surface water bodies (dimensionless) self.fracWat = pcr.scalar(0.0) if self.useNetCDF: self.fracWat = vos.netcdf2PCRobjClone( self.ncFileInp, "fracWaterInp", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) else: self.fracWat = vos.readPCRmapClone( self.fracWaterInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, ) self.fracWat = pcr.cover(self.fracWat, 0.0) self.fracWat = pcr.max(0.0, self.fracWat) self.fracWat = pcr.min(1.0, self.fracWat) self.waterBodyIds = pcr.nominal(0) # waterBody ids self.waterBodyOut = pcr.boolean(0) # waterBody outlets self.waterBodyArea = pcr.scalar(0.0) # waterBody surface areas # water body ids if self.useNetCDF: self.waterBodyIds = vos.netcdf2PCRobjClone( self.ncFileInp, "waterBodyIds", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) else: self.waterBodyIds = vos.readPCRmapClone( self.waterBodyIdsInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, False, None, True, ) # self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, pcr.nominal(self.waterBodyIds) ) # water body outlets (correcting outlet positions) wbCatchment = pcr.catchmenttotal(pcr.scalar(1), ldd) self.waterBodyOut = pcr.ifthen( wbCatchment == pcr.areamaximum(wbCatchment, self.waterBodyIds), self.waterBodyIds, ) # = outlet ids self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, self.waterBodyOut ) # TODO: Please also consider endorheic lakes! # correcting water body ids self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, pcr.subcatchment(ldd, self.waterBodyOut), ) # boolean map for water body outlets: self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyOut) > 0.0, pcr.boolean(1) ) # reservoir surface area (m2): if self.useNetCDF: resSfArea = ( 1000.0 * 1000.0 * vos.netcdf2PCRobjClone( self.ncFileInp, "resSfAreaInp", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) ) else: resSfArea = ( 1000.0 * 1000.0 * vos.readPCRmapClone( self.resSfAreaInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, ) ) resSfArea = pcr.areaaverage(resSfArea, self.waterBodyIds) resSfArea = pcr.cover(resSfArea, 0.0) # water body surface area (m2): (lakes and reservoirs) self.waterBodyArea = pcr.max( pcr.areatotal( pcr.cover(self.fracWat * self.cellArea, 0.0), self.waterBodyIds ), pcr.areaaverage(pcr.cover(resSfArea, 0.0), self.waterBodyIds), ) self.waterBodyArea = pcr.ifthen(self.waterBodyArea > 0.0, self.waterBodyArea) # correcting water body ids and outlets (exclude all water bodies with surfaceArea = 0) self.waterBodyIds = pcr.ifthen(self.waterBodyArea > 0.0, self.waterBodyIds) self.waterBodyOut = pcr.ifthen( pcr.boolean(self.waterBodyIds), self.waterBodyOut ) # water body types: # - 2 = reservoirs (regulated discharge) # - 1 = lakes (weirFormula) # - 0 = non lakes or reservoirs (e.g. wetland) self.waterBodyTyp = pcr.nominal(0) if self.useNetCDF: self.waterBodyTyp = vos.netcdf2PCRobjClone( self.ncFileInp, "waterBodyTyp", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) else: self.waterBodyTyp = vos.readPCRmapClone( self.waterBodyTypInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, False, None, True, ) # excluding wetlands (waterBodyTyp = 0) in all functions related to lakes/reservoirs # self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0, pcr.nominal(self.waterBodyTyp) ) self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0, pcr.nominal(self.waterBodyTyp) ) self.waterBodyTyp = pcr.areamajority( self.waterBodyTyp, self.waterBodyIds ) # choose only one type: either lake or reservoir self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0, pcr.nominal(self.waterBodyTyp) ) self.waterBodyTyp = pcr.ifthen( pcr.boolean(self.waterBodyIds), self.waterBodyTyp ) # correcting lakes and reservoirs ids and outlets self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0, self.waterBodyIds ) self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0, self.waterBodyOut ) # reservoir maximum capacity (m3): self.resMaxCap = pcr.scalar(0.0) self.waterBodyCap = pcr.scalar(0.0) if self.useNetCDF: self.resMaxCap = ( 1000.0 * 1000.0 * vos.netcdf2PCRobjClone( self.ncFileInp, "resMaxCapInp", date_used, useDoy="yearly", cloneMapFileName=self.cloneMap, ) ) else: self.resMaxCap = ( 1000.0 * 1000.0 * vos.readPCRmapClone( self.resMaxCapInp + str(year_used) + ".map", self.cloneMap, self.tmpDir, self.inputDir, ) ) self.resMaxCap = pcr.ifthen(self.resMaxCap > 0, self.resMaxCap) self.resMaxCap = pcr.areaaverage(self.resMaxCap, self.waterBodyIds) # water body capacity (m3): (lakes and reservoirs) self.waterBodyCap = pcr.cover( self.resMaxCap, 0.0 ) # Note: Most of lakes have capacities > 0. self.waterBodyCap = pcr.ifthen( pcr.boolean(self.waterBodyIds), self.waterBodyCap ) # correcting water body types: # Reservoirs that have zero capacities will be assumed as lakes. self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0.0, self.waterBodyTyp ) self.waterBodyTyp = pcr.ifthenelse( self.waterBodyCap > 0.0, self.waterBodyTyp, pcr.ifthenelse( pcr.scalar(self.waterBodyTyp) == 2, pcr.nominal(1), self.waterBodyTyp ), ) # final corrections: self.waterBodyTyp = pcr.ifthen( self.waterBodyArea > 0.0, self.waterBodyTyp ) # make sure that all lakes and/or reservoirs have surface areas self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0.0, self.waterBodyTyp ) # make sure that only types 1 and 2 will be considered in lake/reservoir functions self.waterBodyIds = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0.0, self.waterBodyIds ) # make sure that all lakes and/or reservoirs have ids self.waterBodyOut = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, self.waterBodyOut ) # make sure that all lakes and/or reservoirs have outlets # for a natural run (self.onlyNaturalWaterBodies == True) # which uses only the year 1900, assume all reservoirs are lakes if ( self.onlyNaturalWaterBodies == True and date_used == self.dateForNaturalCondition ): logger.info( "Using only natural water bodies identified in the year 1900. All reservoirs in 1900 are assumed as lakes." ) self.waterBodyTyp = pcr.ifthen( pcr.scalar(self.waterBodyTyp) > 0.0, pcr.nominal(1) ) # check that all lakes and/or reservoirs have types, ids, surface areas and outlets: test = ( pcr.defined(self.waterBodyTyp) & pcr.defined(self.waterBodyArea) & pcr.defined(self.waterBodyIds) & pcr.boolean( pcr.areamaximum(pcr.scalar(self.waterBodyOut), self.waterBodyIds) ) ) a, b, c = vos.getMinMaxMean(pcr.cover(pcr.scalar(test), 1.0) - pcr.scalar(1.0)) threshold = 1e-3 if abs(a) > threshold or abs(b) > threshold: logger.warning("Missing information in some lakes and/or reservoirs.") # at the beginning of simulation period (timeStepPCR = 1) # - we have to define/get the initial conditions # if currTimeStep.timeStepPCR == 1: self.getICs(initial_condition_dictionary) # For each new reservoir (introduced at the beginning of the year) # initiating storage, average inflow and outflow # self.waterBodyStorage = pcr.cover(self.waterBodyStorage, 0.0) self.avgInflow = pcr.cover(self.avgInflow, 0.0) self.avgOutflow = pcr.cover(self.avgOutflow, 0.0) # cropping only in the landmask region: self.fracWat = pcr.ifthen(self.landmask, self.fracWat) self.waterBodyIds = pcr.ifthen(self.landmask, self.waterBodyIds) self.waterBodyOut = pcr.ifthen(self.landmask, self.waterBodyOut) self.waterBodyArea = pcr.ifthen(self.landmask, self.waterBodyArea) self.waterBodyTyp = pcr.ifthen(self.landmask, self.waterBodyTyp) self.waterBodyCap = pcr.ifthen(self.landmask, self.waterBodyCap) self.waterBodyStorage = pcr.ifthen(self.landmask, self.waterBodyStorage) self.avgInflow = pcr.ifthen(self.landmask, self.avgInflow) self.avgOutflow = pcr.ifthen(self.landmask, self.avgOutflow)
def __init__(self, iniItems,landmask,spinUp): object.__init__(self) self.cloneMap = iniItems.cloneMap self.tmpDir = iniItems.tmpDir self.inputDir = iniItems.globalOptions['inputDir'] self.landmask = landmask # option to activate water balance check self.debugWaterBalance = True if iniItems.routingOptions['debugWaterBalance'] == "False": self.debugWaterBalance = False if iniItems.groundwaterOptions['groundwaterPropertiesNC'] == str(None): # assign the recession coefficient parameter(s) self.recessionCoeff = vos.readPCRmapClone(\ iniItems.groundwaterOptions['recessionCoeff'], self.cloneMap,self.tmpDir,self.inputDir) else: groundwaterPropertiesNC = vos.getFullPath(\ iniItems.groundwaterOptions[\ 'groundwaterPropertiesNC'], self.inputDir) self.recessionCoeff = vos.netcdf2PCRobjCloneWithoutTime(\ groundwaterPropertiesNC,'recessionCoeff',\ cloneMapFileName = self.cloneMap) # groundwater recession coefficient (day-1) self.recessionCoeff = pcr.cover(self.recessionCoeff,0.00) self.recessionCoeff = pcr.min(1.0000,self.recessionCoeff) # if 'minRecessionCoeff' in iniItems.groundwaterOptions.keys(): minRecessionCoeff = float(iniItems.groundwaterOptions['minRecessionCoeff']) else: minRecessionCoeff = 1.0e-4 # This is the minimum value used in Van Beek et al. (2011). self.recessionCoeff = pcr.max(minRecessionCoeff,self.recessionCoeff) if iniItems.groundwaterOptions['groundwaterPropertiesNC'] == str(None): # assign aquifer specific yield self.specificYield = vos.readPCRmapClone(\ iniItems.groundwaterOptions['specificYield'], self.cloneMap,self.tmpDir,self.inputDir) else: self.specificYield = vos.netcdf2PCRobjCloneWithoutTime(\ groundwaterPropertiesNC,'specificYield',\ cloneMapFileName = self.cloneMap) self.specificYield = pcr.cover(self.specificYield,0.0) self.specificYield = pcr.max(0.010,self.specificYield) # TODO: TO BE CHECKED: The resample process of specificYield self.specificYield = pcr.min(1.000,self.specificYield) if iniItems.groundwaterOptions['groundwaterPropertiesNC'] == str(None): # assign aquifer saturated conductivity self.kSatAquifer = vos.readPCRmapClone(\ iniItems.groundwaterOptions['kSatAquifer'], self.cloneMap,self.tmpDir,self.inputDir) else: self.kSatAquifer = vos.netcdf2PCRobjCloneWithoutTime(\ groundwaterPropertiesNC,'kSatAquifer',\ cloneMapFileName = self.cloneMap) self.kSatAquifer = pcr.cover(self.kSatAquifer,0.0) self.kSatAquifer = pcr.max(0.010,self.kSatAquifer) # limitAbstraction options self.limitAbstraction = False if iniItems.landSurfaceOptions['limitAbstraction'] == "True": self.limitAbstraction = True # option for limitting regional groundwater abstractions if iniItems.groundwaterOptions['pumpingCapacityNC'] != "None": logger.info('Limit for annual regional groundwater abstraction is used.') self.limitRegionalAnnualGroundwaterAbstraction = True self.pumpingCapacityNC = vos.getFullPath(\ iniItems.groundwaterOptions['pumpingCapacityNC'],self.inputDir,False) else: logger.warning('NO LIMIT for regional groundwater (annual) pumping. It may result too high groundwater abstraction.') self.limitRegionalAnnualGroundwaterAbstraction = False # option for limitting fossil groundwater abstractions: self.limitFossilGroundwaterAbstraction = False # # estimate of fossil groundwater capacity: if iniItems.groundwaterOptions['limitFossilGroundWaterAbstraction'] == "True": logger.info('Fossil groundwater abstractions are allowed with LIMIT.') self.limitFossilGroundwaterAbstraction = True # estimate of thickness (unit: m) of accesible groundwater: shallow and deep totalGroundwaterThickness = vos.readPCRmapClone(\ iniItems.groundwaterOptions['estimateOfTotalGroundwaterThickness'], self.cloneMap,self.tmpDir,self.inputDir) # extrapolation totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 1.0)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 1.5)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 2.5)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 5.0)) # totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, 0.0) # # set minimum thickness minimumThickness = pcr.scalar(float(\ iniItems.groundwaterOptions['minimumTotalGroundwaterThickness'])) totalGroundwaterThickness = pcr.max(minimumThickness, totalGroundwaterThickness) # # estimate of capacity (unit: m) of renewable groundwater (shallow) storGroundwaterCap = pcr.cover( vos.readPCRmapClone(\ iniItems.groundwaterOptions['estimateOfRenewableGroundwaterCapacity'], self.cloneMap,self.tmpDir,self.inputDir), 0.0) # # fossil groundwater capacity (unit: m) self.fossilWaterCap = pcr.ifthen(self.landmask,\ pcr.max(0.0,\ totalGroundwaterThickness*self.specificYield - storGroundwaterCap)) # zones at which groundwater allocations are determined self.usingAllocSegments = False if iniItems.landSurfaceOptions['allocationSegmentsForGroundSurfaceWater'] != "None": self.usingAllocSegments = True # incorporating groundwater distribution network: if self.usingAllocSegments: self.allocSegments = vos.readPCRmapClone(\ iniItems.landSurfaceOptions['allocationSegmentsForGroundSurfaceWater'], self.cloneMap,self.tmpDir,self.inputDir,isLddMap=False,cover=None,isNomMap=True) self.allocSegments = pcr.ifthen(self.landmask, self.allocSegments) cellArea = vos.readPCRmapClone(\ iniItems.routingOptions['cellAreaMap'], self.cloneMap,self.tmpDir,self.inputDir) cellArea = pcr.ifthen(self.landmask, cellArea) # TODO: integrate this one with the one coming from the routing module self.segmentArea = pcr.areatotal(pcr.cover(cellArea, 0.0), self.allocSegments) self.segmentArea = pcr.ifthen(self.landmask, self.segmentArea) # get initial conditions self.getICs(iniItems,spinUp) # initiate old style reporting # TODO: remove this! self.initiate_old_style_groundwater_reporting(iniItems)
def __init__(self, iniItems, landmask): object.__init__(self) # cloneMap, temporary directory, absolute path for input directory, landmask self.cloneMap = iniItems.cloneMap self.tmpDir = iniItems.tmpDir self.inputDir = iniItems.globalOptions['inputDir'] self.landmask = landmask # configuration from the ini file self.iniItems = iniItems # topography properties: read several variables from the netcdf file for var in ['dem_minimum','dem_maximum','dem_average','dem_standard_deviation',\ 'slopeLength','orographyBeta','tanslope',\ 'dzRel0000','dzRel0001','dzRel0005',\ 'dzRel0010','dzRel0020','dzRel0030','dzRel0040','dzRel0050',\ 'dzRel0060','dzRel0070','dzRel0080','dzRel0090','dzRel0100']: vars(self)[var] = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['topographyNC'], \ var, self.cloneMap) vars(self)[var] = pcr.cover(vars(self)[var], 0.0) # channel properties: read several variables from the netcdf file for var in [ 'lddMap', 'cellAreaMap', 'gradient', 'bankfull_width', 'bankfull_depth', 'dem_floodplain', 'dem_riverbed' ]: vars(self)[var] = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['channelNC'], \ var, self.cloneMap) vars(self)[var] = pcr.cover(vars(self)[var], 0.0) # minimum channel width minimum_channel_width = 0.1 self.bankfull_width = pcr.max(minimum_channel_width, self.bankfull_width) #~ # cell fraction if channel water reaching the flood plan # NOT USED #~ self.flood_plain_fraction = self.return_innundation_fraction(pcr.max(0.0, self.dem_floodplain - self.dem_minimum)) # coefficient of Manning self.manningsN = vos.readPCRmapClone(self.iniItems.modflowParameterOptions['manningsN'],\ self.cloneMap,self.tmpDir,self.inputDir) # minimum channel gradient minGradient = 0.00005 self.gradient = pcr.max(minGradient, pcr.cover(self.gradient, minGradient)) # correcting lddMap self.lddMap = pcr.ifthen(pcr.scalar(self.lddMap) > 0.0, self.lddMap) self.lddMap = pcr.lddrepair(pcr.ldd(self.lddMap)) # channelLength = approximation of channel length (unit: m) # This is approximated by cell diagonal. cellSizeInArcMin = np.round(pcr.clone().cellSize() * 60.) verticalSizeInMeter = cellSizeInArcMin * 1852. self.channelLength = ((self.cellAreaMap/verticalSizeInMeter)**(2)+\ (verticalSizeInMeter)**(2))**(0.5) # option for lakes and reservoir self.onlyNaturalWaterBodies = False if self.iniItems.modflowParameterOptions[ 'onlyNaturalWaterBodies'] == "True": self.onlyNaturalWaterBodies = True # groundwater linear recession coefficient (day-1) ; the linear reservoir concept is still being used to represent fast response flow # particularly from karstic aquifer in mountainous regions self.recessionCoeff = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['groundwaterPropertiesNC'],\ 'recessionCoeff', self.cloneMap) self.recessionCoeff = pcr.cover(self.recessionCoeff, 0.00) self.recessionCoeff = pcr.min(1.0000, self.recessionCoeff) # if 'minRecessionCoeff' in iniItems.modflowParameterOptions.keys(): minRecessionCoeff = float( iniItems.modflowParameterOptions['minRecessionCoeff']) else: minRecessionCoeff = 1.0e-4 # This is the minimum value used in Van Beek et al. (2011). self.recessionCoeff = pcr.max(minRecessionCoeff, self.recessionCoeff) # aquifer saturated conductivity (m/day) self.kSatAquifer = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['groundwaterPropertiesNC'],\ 'kSatAquifer', self.cloneMap) self.kSatAquifer = pcr.cover(self.kSatAquifer, pcr.mapmaximum(self.kSatAquifer)) self.kSatAquifer = pcr.max(0.010, self.kSatAquifer) self.kSatAquifer *= 0.001 # aquifer specific yield (dimensionless) self.specificYield = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['groundwaterPropertiesNC'],\ 'specificYield', self.cloneMap) self.specificYield = pcr.cover(self.specificYield, pcr.mapmaximum(self.specificYield)) self.specificYield = pcr.max( 0.010, self.specificYield ) # TODO: TO BE CHECKED: The resample process of specificYield self.specificYield = pcr.min(1.000, self.specificYield) # estimate of thickness (unit: m) of accesible groundwater totalGroundwaterThickness = vos.netcdf2PCRobjCloneWithoutTime(self.iniItems.modflowParameterOptions['estimateOfTotalGroundwaterThicknessNC'],\ 'thickness', self.cloneMap) # extrapolation totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness,\ pcr.windowaverage(totalGroundwaterThickness, 1.0)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness,\ pcr.windowaverage(totalGroundwaterThickness, 1.5)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, 0.0) # # set minimum thickness minimumThickness = pcr.scalar(float(\ self.iniItems.modflowParameterOptions['minimumTotalGroundwaterThickness'])) totalGroundwaterThickness = pcr.max(minimumThickness, totalGroundwaterThickness) # # set maximum thickness: 250 m. maximumThickness = 250. self.totalGroundwaterThickness = pcr.min(maximumThickness, totalGroundwaterThickness) # river bed resistance (unit: day) self.bed_resistance = 1.0 # option to ignore capillary rise self.ignoreCapRise = True if self.iniItems.modflowParameterOptions['ignoreCapRise'] == "False": self.ignoreCapRise = False # initiate old style reporting # TODO: remove this! self.initiate_old_style_groundwater_reporting(iniItems)
def update(self,landSurface,routing,currTimeStep): logger.info("Updating groundwater") 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 # unit: m self.storGroundwater += self.surfaceWaterInf # get net recharge (percolation-capRise) and update storage: self.storGroundwater = pcr.max(0.,\ self.storGroundwater + landSurface.gwRecharge) # non fossil groundwater abstraction self.nonFossilGroundwaterAbs = landSurface.nonFossilGroundwaterAbs self.storGroundwater = pcr.max(0.,\ self.storGroundwater - self.nonFossilGroundwaterAbs) # baseflow 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) # fossil groundwater abstraction: self.fossilGroundwaterAbstr = landSurface.fossilGroundwaterAbstr self.storGroundwaterFossil -= self.fossilGroundwaterAbstr # fossil groundwater cannot be negative if limitFossilGroundwaterAbstraction is used if self.limitFossilGroundwaterAbstraction: self.storGroundwaterFossil = pcr.max(0.0, self.storGroundwaterFossil) # groundwater allocation (Note: This is done in the landSurface module) self.allocNonFossilGroundwater = landSurface.allocNonFossilGroundwater self.fossilGroundwaterAlloc = landSurface.fossilGroundwaterAlloc # Note: The following variable (unmetDemand) is a bad name and used in the past. # Its definition is actually as follows: (the amount of demand that is satisfied/allocated from fossil groundwater) self.unmetDemand = self.fossilGroundwaterAlloc # calculate the average total groundwater abstraction (m/day) from the last 365 days: totalAbstraction = self.fossilGroundwaterAbstr + 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) # calculate the average non fossil groundwater allocation (m/day) # - from the last 365 days: deltaAllocation = self.allocNonFossilGroundwater - self.avgNonFossilAllocation self.avgNonFossilAllocation = self.avgNonFossilAllocation +\ deltaAllocation/\ pcr.min(365., pcr.max(1.0, routing.timestepsToAvgDischarge)) self.avgNonFossilAllocation = pcr.max(0.0, self.avgNonFossilAllocation) # - from the last 7 days: deltaAllocationShort = self.allocNonFossilGroundwater - self.avgNonFossilAllocationShort self.avgNonFossilAllocationShort = self.avgNonFossilAllocationShort +\ deltaAllocationShort/\ pcr.min(7., pcr.max(1.0, routing.timestepsToAvgDischarge)) self.avgNonFossilAllocationShort = pcr.max(0.0, self.avgNonFossilAllocationShort) # calculate the average total (fossil + non fossil) groundwater allocation (m/day) totalGroundwaterAllocation = self.allocNonFossilGroundwater + self.fossilGroundwaterAlloc # - from the last 365 days: deltaAllocation = totalGroundwaterAllocation - self.avgAllocation self.avgAllocation = self.avgAllocation +\ deltaAllocation/\ pcr.min(365., pcr.max(1.0, routing.timestepsToAvgDischarge)) self.avgAllocation = pcr.max(0.0, self.avgAllocation) # - from the last 7 days: deltaAllocationShort = totalGroundwaterAllocation - self.avgAllocationShort self.avgAllocationShort = self.avgAllocationShort +\ deltaAllocationShort/\ pcr.min(7., pcr.max(1.0, routing.timestepsToAvgDischarge)) self.avgAllocationShort = pcr.max(0.0, self.avgAllocationShort) 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.fossilGroundwaterAbstr],\ [ preStorGroundwaterFossil],\ [self.storGroundwaterFossil],\ 'storGroundwaterFossil',\ True,\ currTimeStep.fulldate,threshold=1e-3) if self.debugWaterBalance: vos.waterBalanceCheck([landSurface.desalinationAllocation,\ self.unmetDemand, \ self.allocNonFossilGroundwater, \ landSurface.allocSurfaceWaterAbstract],\ [landSurface.totalPotentialGrossDemand],\ [pcr.scalar(0.)],\ [pcr.scalar(0.)],\ 'demand allocation (desalination, surface water, groundwater & unmetDemand. Error here may be due to rounding error.',\ True,\ currTimeStep.fulldate,threshold=1e-3) # old-style reporting self.old_style_groundwater_reporting(currTimeStep) # TODO: remove this one
def adusting_parameters(self, configuration, system_argument): # global pre-multipliers given in the argument: if len(system_argument) > 4: logger.info( "Adjusting some model parameters based on given values in the system argument." ) # pre-multipliers for minSoilDepthFrac, kSat, recessionCoeff, storCap and degreeDayFactor multiplier_for_minSoilDepthFrac = float( system_argument[4] ) # linear scale # Note that this one does NOT work for the changing WMIN or Joyce land cover options. multiplier_for_kSat = float(system_argument[5]) # log scale multiplier_for_recessionCoeff = float( system_argument[6]) # log scale multiplier_for_storCap = float(system_argument[7]) # linear scale multiplier_for_degreeDayFactor = float( system_argument[8]) # linear scale # pre-multiplier for the reference potential ET self.multiplier_for_refPotET = float( system_argument[9]) # linear scale # it is also possible to define prefactors via the ini/configuration file: # - this will be overwrite any previous given pre-multipliers if 'prefactorOptions' in configuration.allSections: logger.info( "Adjusting some model parameters based on given values in the ini/configuration file." ) self.multiplier_for_refPotET = float( configuration. prefactorOptions['linear_multiplier_for_refPotET'] ) # linear scale # Note that this one does NOT work for the changing WMIN or Joyce land cover options. multiplier_for_degreeDayFactor = float( configuration.prefactorOptions[ 'linear_multiplier_for_degreeDayFactor']) # linear scale multiplier_for_minSoilDepthFrac = float( configuration.prefactorOptions[ 'linear_multiplier_for_minSoilDepthFrac']) # linear scale multiplier_for_kSat = float( configuration.prefactorOptions['log_10_multiplier_for_kSat'] ) # log scale multiplier_for_storCap = float( configuration.prefactorOptions['linear_multiplier_for_storCap'] ) # linear scale multiplier_for_recessionCoeff = float( configuration.prefactorOptions[ 'log_10_multiplier_for_recessionCoeff']) # log scale # saving global pre-multipliers to the log file: msg = "\n" msg += "\n" msg += "Multiplier values used: " + "\n" msg += "For minSoilDepthFrac : " + str( multiplier_for_minSoilDepthFrac) + "\n" msg += "For kSat (log-scale) : " + str( multiplier_for_kSat) + "\n" msg += "For recessionCoeff (log-scale) : " + str( multiplier_for_recessionCoeff) + "\n" msg += "For storCap : " + str( multiplier_for_storCap) + "\n" msg += "For degreeDayFactor : " + str( multiplier_for_degreeDayFactor) + "\n" msg += "For refPotET : " + str( self.multiplier_for_refPotET) + "\n" logger.info(msg) # - also to a txt file f = open( "multiplier.txt", "w" ) # this will be stored in the "map" folder of the 'outputDir' (as we set the current working directory to this "map" folder, see configuration.py) f.write(msg) f.close() # set parameter "recessionCoeff" based on the given pre-multiplier # - also saving the adjusted parameter maps to pcraster files # - these will be stored in the "map" folder of the 'outputDir' (as we set the current working directory to this "map" folder, see configuration.py) # "recessionCoeff" # minimum value is zero and using log-scale self.model.groundwater.recessionCoeff = pcr.max( 0.0, (10**(multiplier_for_recessionCoeff)) * self.model.groundwater.recessionCoeff) self.model.groundwater.recessionCoeff = pcr.min( 1.0, self.model.groundwater.recessionCoeff) # report the map pcr.report(self.model.groundwater.recessionCoeff, "recessionCoeff.map") # set parameters "kSat", "storCap", "minSoilDepthFrac", and "degreeDayFactor" based on the given pre-multipliers for coverType in self.model.landSurface.coverTypes: # "degreeDayFactor" self.model.landSurface.landCoverObj[coverType].degreeDayFactor = pcr.max(0.0, multiplier_for_degreeDayFactor *\ self.model.landSurface.landCoverObj[coverType].degreeDayFactor) # report the map pcraster_filename = "degreeDayFactor" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].degreeDayFactor, pcraster_filename) # "kSat" and "storCap" for 2 layer model if self.model.landSurface.numberOfSoilLayers == 2: # "kSat" # minimum value is zero and using-log-scale self.model.landSurface.landCoverObj[coverType].parameters.kSatUpp = \ pcr.max(0.0, (10**(multiplier_for_kSat)) * self.model.landSurface.landCoverObj[coverType].parameters.kSatUpp) self.model.landSurface.landCoverObj[coverType].parameters.kSatLow = \ pcr.max(0.0, (10**(multiplier_for_kSat)) * self.model.landSurface.landCoverObj[coverType].parameters.kSatLow) # report the maps pcraster_filename = "kSatUpp" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. kSatUpp, pcraster_filename) pcraster_filename = "kSatLow" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. kSatLow, pcraster_filename) # "storCap" # minimum value is zero self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp = pcr.max(0.0, multiplier_for_storCap*\ self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp) self.model.landSurface.landCoverObj[coverType].parameters.storCapLow = pcr.max(0.0, multiplier_for_storCap*\ self.model.landSurface.landCoverObj[coverType].parameters.storCapLow) # report the maps pcraster_filename = "storCapUpp" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. storCapUpp, pcraster_filename) pcraster_filename = "storCapLow" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. storCapLow, pcraster_filename) # "kSat" and "storCap" for 3 layer model if self.model.landSurface.numberOfSoilLayers == 3: # "kSat" # minimum value is zero and using-log-scale self.model.landSurface.landCoverObj[coverType].parameters.kSatUpp000005 = \ pcr.max(0.0, (10**(multiplier_for_kSat)) * self.model.landSurface.landCoverObj[coverType].parameters.kSatUpp000005) self.model.landSurface.landCoverObj[coverType].parameters.kSatUpp005030 = \ pcr.max(0.0, (10**(multiplier_for_kSat)) * self.model.landSurface.landCoverObj[coverType].parameters.kSatUpp005030) self.model.landSurface.landCoverObj[coverType].parameters.kSatLow030150 = \ pcr.max(0.0, (10**(multiplier_for_kSat)) * self.model.landSurface.landCoverObj[coverType].parameters.kSatLow030150) # report the maps pcraster_filename = "kSatUpp000005" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. kSatUpp000005, pcraster_filename) pcraster_filename = "kSatUpp005030" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. kSatUpp005030, pcraster_filename) pcraster_filename = "kSatLow030150" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. kSatLow030150, pcraster_filename) # "storCap" # minimum value is zero self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp000005 = pcr.max(0.0, multiplier_for_storCap*\ self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp000005) self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp005030 = pcr.max(0.0, multiplier_for_storCap*\ self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp005030) self.model.landSurface.landCoverObj[coverType].parameters.storCapLow030150 = pcr.max(0.0, multiplier_for_storCap*\ self.model.landSurface.landCoverObj[coverType].parameters.storCapLow030150) # report the maps pcraster_filename = "storCapUpp000005" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. storCapUpp000005, pcraster_filename) pcraster_filename = "storCapUpp005030" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. storCapUpp005030, pcraster_filename) pcraster_filename = "storCapLow030150" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. storCapLow030150, pcraster_filename) # re-calculate rootZoneWaterStorageCap as the consequence of the modification of "storCap" # This is WMAX in the oldcalc script. if self.model.landSurface.numberOfSoilLayers == 2: self.model.landSurface.landCoverObj[coverType].parameters.rootZoneWaterStorageCap = self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp +\ self.model.landSurface.landCoverObj[coverType].parameters.storCapLow if self.model.landSurface.numberOfSoilLayers == 3: self.model.landSurface.landCoverObj[coverType].parameters.rootZoneWaterStorageCap = self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp000005 +\ self.model.landSurface.landCoverObj[coverType].parameters.storCapUpp005030 +\ self.model.landSurface.landCoverObj[coverType].parameters.storCapLow030150 # report the map pcraster_filename = "rootZoneWaterStorageCap" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].parameters. rootZoneWaterStorageCap, pcraster_filename) # "minSoilDepthFrac" if multiplier_for_minSoilDepthFrac != 1.0: # minimum value is zero self.model.landSurface.landCoverObj[coverType].minSoilDepthFrac = pcr.max(0.0, multiplier_for_minSoilDepthFrac*\ self.model.landSurface.landCoverObj[coverType].minSoilDepthFrac) # for minSoilDepthFrac - values will be limited by maxSoilDepthFrac self.model.landSurface.landCoverObj[coverType].minSoilDepthFrac = pcr.min(\ self.model.landSurface.landCoverObj[coverType].minSoilDepthFrac,\ self.model.landSurface.landCoverObj[coverType].maxSoilDepthFrac) # maximum value is 1.0 self.model.landSurface.landCoverObj[ coverType].minSoilDepthFrac = pcr.min( 1.0, self.model.landSurface.landCoverObj[coverType]. minSoilDepthFrac) # report the map pcraster_filename = "minSoilDepthFrac" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType]. minSoilDepthFrac, pcraster_filename) # re-calculate arnoBeta (as the consequence of the modification of minSoilDepthFrac) self.model.landSurface.landCoverObj[coverType].arnoBeta = pcr.max(0.001,\ (self.model.landSurface.landCoverObj[coverType].maxSoilDepthFrac-1.)/(1.-self.model.landSurface.landCoverObj[coverType].minSoilDepthFrac)+\ self.model.landSurface.landCoverObj[coverType].parameters.orographyBeta-0.01) self.model.landSurface.landCoverObj[coverType].arnoBeta = pcr.cover(pcr.max(0.001,\ self.model.landSurface.landCoverObj[coverType].arnoBeta), 0.001) # report the map pcraster_filename = "arnoBeta" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType].arnoBeta, pcraster_filename) # re-calculate rootZoneWaterStorageMin (as the consequence of the modification of minSoilDepthFrac) # This is WMIN in the oldcalc script. # WMIN (unit: m): minimum local soil water capacity within the grid-cell self.model.landSurface.landCoverObj[coverType].rootZoneWaterStorageMin = self.model.landSurface.landCoverObj[coverType].minSoilDepthFrac *\ self.model.landSurface.landCoverObj[coverType].parameters.rootZoneWaterStorageCap # report the map pcraster_filename = "rootZoneWaterStorageMin" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType]. rootZoneWaterStorageMin, pcraster_filename) # re-calculate rootZoneWaterStorageRange (as the consequence of the modification of rootZoneWaterStorageRange and minSoilDepthFrac) # WMAX - WMIN (unit: m) self.model.landSurface.landCoverObj[coverType].rootZoneWaterStorageRange = self.model.landSurface.landCoverObj[coverType].parameters.rootZoneWaterStorageCap -\ self.model.landSurface.landCoverObj[coverType].rootZoneWaterStorageMin # report the map pcraster_filename = "rootZoneWaterStorageRange" + "_" + coverType + ".map" pcr.report( self.model.landSurface.landCoverObj[coverType]. rootZoneWaterStorageRange, pcraster_filename)
def getReservoirOutflow( self, avgChannelDischarge, length_of_time_step, downstreamDemand ): # avgOutflow (m3/s) avgOutflow = self.avgOutflow # The following is needed when new lakes/reservoirs introduced (its avgOutflow is still zero). # ~ # - alternative 1 # ~ avgOutflow = pcr.ifthenelse(\ # ~ avgOutflow > 0.,\ # ~ avgOutflow, # ~ pcr.max(avgChannelDischarge, self.avgInflow, 0.001)) # - alternative 2 avgOutflow = pcr.ifthenelse( avgOutflow > 0.0, avgOutflow, pcr.max(avgChannelDischarge, self.avgInflow) ) avgOutflow = pcr.ifthenelse( avgOutflow > 0.0, avgOutflow, pcr.downstream(self.lddMap, avgOutflow) ) avgOutflow = pcr.areamaximum(avgOutflow, self.waterBodyIds) # calculate resvOutflow (m2/s) (based on reservoir storage and avgDischarge): # - using reductionFactor in such a way that: # - if relativeCapacity < minResvrFrac : release is terminated # - if relativeCapacity > maxResvrFrac : longterm average reductionFactor = pcr.cover( pcr.min( 1.0, pcr.max( 0.0, self.waterBodyStorage - self.minResvrFrac * self.waterBodyCap ) / (self.maxResvrFrac - self.minResvrFrac) * self.waterBodyCap, ), 0.0, ) # resvOutflow = reductionFactor * avgOutflow * length_of_time_step # unit: m3 # maximum release <= average inflow (especially during dry condition) resvOutflow = pcr.max( 0, pcr.min(resvOutflow, self.avgInflow * length_of_time_step) ) # unit: m3 # downstream demand (m3/s) # reduce demand if storage < lower limit reductionFactor = vos.getValDivZero( downstreamDemand, self.minResvrFrac * self.waterBodyCap, vos.smallNumber ) reductionFactor = pcr.cover(reductionFactor, 0.0) downstreamDemand = pcr.min(downstreamDemand, downstreamDemand * reductionFactor) # resvOutflow > downstreamDemand resvOutflow = pcr.max( resvOutflow, downstreamDemand * length_of_time_step ) # unit: m3 # floodOutflow: additional release if storage > upper limit ratioQBankfull = 2.3 estmStorage = pcr.max(0.0, self.waterBodyStorage - resvOutflow) floodOutflow = pcr.max(0.0, estmStorage - self.waterBodyCap) + pcr.cover( pcr.max(0.0, estmStorage - self.maxResvrFrac * self.waterBodyCap) / ((1.0 - self.maxResvrFrac) * self.waterBodyCap), 0.0, ) * pcr.max( 0.0, ratioQBankfull * avgOutflow * vos.secondsPerDay() - resvOutflow ) floodOutflow = pcr.max( 0.0, pcr.min( floodOutflow, estmStorage - self.maxResvrFrac * self.waterBodyCap * 0.75 ), ) # maximum limit of floodOutflow: bring the reservoir storages only to 3/4 of upper limit capacities # update resvOutflow after floodOutflow resvOutflow = pcr.cover(resvOutflow, 0.0) + pcr.cover(floodOutflow, 0.0) # maximum release if storage > upper limit : bring the reservoir storages only to 3/4 of upper limit capacities resvOutflow = pcr.ifthenelse( self.waterBodyStorage > self.maxResvrFrac * self.waterBodyCap, pcr.min( resvOutflow, pcr.max( 0, self.waterBodyStorage - self.maxResvrFrac * self.waterBodyCap * 0.75, ), ), resvOutflow, ) # if storage > upper limit : resvOutflow > avgInflow resvOutflow = pcr.ifthenelse( self.waterBodyStorage > self.maxResvrFrac * self.waterBodyCap, pcr.max(0.0, resvOutflow, self.avgInflow), resvOutflow, ) # resvOutflow < waterBodyStorage resvOutflow = pcr.min(self.waterBodyStorage, resvOutflow) resvOutflow = pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0.0, resvOutflow) resvOutflow = pcr.ifthen(pcr.scalar(self.waterBodyTyp) == 2, resvOutflow) return resvOutflow # unit: m3
def __init__(self, iniItems,landmask,spinUp): object.__init__(self) self.cloneMap = iniItems.cloneMap self.tmpDir = iniItems.tmpDir self.inputDir = iniItems.globalOptions['inputDir'] self.landmask = landmask # option to activate water balance check self.debugWaterBalance = True if iniItems.routingOptions['debugWaterBalance'] == "False": self.debugWaterBalance = False if iniItems.groundwaterOptions['groundwaterPropertiesNC'] == str(None): # assign the recession coefficient parameter(s) self.recessionCoeff = vos.readPCRmapClone(\ iniItems.groundwaterOptions['recessionCoeff'], self.cloneMap,self.tmpDir,self.inputDir) else: groundwaterPropertiesNC = vos.getFullPath(\ iniItems.groundwaterOptions[\ 'groundwaterPropertiesNC'], self.inputDir) self.recessionCoeff = vos.netcdf2PCRobjCloneWithoutTime(\ groundwaterPropertiesNC,'recessionCoeff',\ cloneMapFileName = self.cloneMap) # groundwater recession coefficient (day-1_ self.recessionCoeff = pcr.cover(self.recessionCoeff,0.00) self.recessionCoeff = pcr.min(1.0000,self.recessionCoeff) # if 'minRecessionCoeff' in iniItems.groundwaterOptions.keys(): minRecessionCoeff = float(iniItems.groundwaterOptions['minRecessionCoeff']) else: minRecessionCoeff = 1.0e-4 # This is the minimum value used in Van Beek et al. (2011). self.recessionCoeff = pcr.max(minRecessionCoeff,self.recessionCoeff) if iniItems.groundwaterOptions['groundwaterPropertiesNC'] == str(None): # assign aquifer specific yield self.specificYield = vos.readPCRmapClone(\ iniItems.groundwaterOptions['specificYield'], self.cloneMap,self.tmpDir,self.inputDir) else: self.specificYield = vos.netcdf2PCRobjCloneWithoutTime(\ groundwaterPropertiesNC,'specificYield',\ cloneMapFileName = self.cloneMap) self.specificYield = pcr.cover(self.specificYield,0.0) self.specificYield = pcr.max(0.010,self.specificYield) # TODO: TO BE CHECKED: The resample process of specificYield self.specificYield = pcr.min(1.000,self.specificYield) if iniItems.groundwaterOptions['groundwaterPropertiesNC'] == str(None): # assign aquifer saturated conductivity self.kSatAquifer = vos.readPCRmapClone(\ iniItems.groundwaterOptions['kSatAquifer'], self.cloneMap,self.tmpDir,self.inputDir) else: self.kSatAquifer = vos.netcdf2PCRobjCloneWithoutTime(\ groundwaterPropertiesNC,'kSatAquifer',\ cloneMapFileName = self.cloneMap) self.kSatAquifer = pcr.cover(self.kSatAquifer,0.0) self.kSatAquifer = pcr.max(0.010,self.kSatAquifer) # limitAbstraction options self.limitAbstraction = False if iniItems.landSurfaceOptions['limitAbstraction'] == "True": self.limitAbstraction = True # option for limitting fossil groundwater abstractions - This option is only defined for IWMI project self.limitFossilGroundwaterAbstraction = False if self.limitAbstraction == False and\ "extraOptionsforProjectWithIWMI" in iniItems.allSections and\ iniItems.extraOptionsforProjectWithIWMI['limitFossilGroundWaterAbstraction'] == "True": logger.info('Fossil groundwater abstraction limit is used (IWMI project).') self.limitFossilGroundwaterAbstraction = True # estimate of thickness (unit: mm) of aceesible groundwater: shallow and deep totalGroundwaterThickness = vos.readPCRmapClone(\ iniItems.extraOptionsforProjectWithIWMI['estimateOfTotalGroundwaterThickness'], self.cloneMap,self.tmpDir,self.inputDir) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 1.0)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 1.5)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 2.5)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 5.0)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.windowaverage(totalGroundwaterThickness, 7.5)) totalGroundwaterThickness = pcr.cover(totalGroundwaterThickness, pcr.mapmaximum(totalGroundwaterThickness)) # set minimum thickness to 50 m: totalGroundwaterThickness = pcr.max(50.0, totalGroundwaterThickness) # estimate of capacity (unit: m) of renewable groundwater (shallow) storGroundwaterCap = pcr.cover( vos.readPCRmapClone(\ iniItems.extraOptionsforProjectWithIWMI['estimateOfRenewableGroundwaterCapacity'], self.cloneMap,self.tmpDir,self.inputDir),\ 0.0) # fossil groundwater capacity (unit: m) self.fossilWaterCap = pcr.max(0.0,\ totalGroundwaterThickness*self.specificYield - storGroundwaterCap) # option for limitting regional groundwater abstractions - This option is only defined self.limitRegionalAnnualGroundwaterAbstraction = False if "extraOptionsforProjectWithIWMI" in iniItems.allSections and\ iniItems.extraOptionsforProjectWithIWMI['limitRegionalAnnualGroundwaterAbstraction'] == "True": logger.info('Limit for regional groundwater abstraction is used (IWMI project).') self.limitRegionalAnnualGroundwaterAbstraction = True region_ids = vos.readPCRmapClone(\ iniItems.extraOptionsforProjectWithIWMI['regionIds'], self.cloneMap,self.tmpDir,self.inputDir) self.region_ids = pcr.nominal(region_ids) self.region_ids = pcr.ifthen(self.landmask, self.region_ids) self.regionalAnnualGroundwaterAbstractionLimit = vos.readPCRmapClone(\ iniItems.extraOptionsforProjectWithIWMI['pumpingCapacity'], self.cloneMap,self.tmpDir,self.inputDir) self.regionalAnnualGroundwaterAbstractionLimit = pcr.roundup(self.regionalAnnualGroundwaterAbstractionLimit*1000.)/1000. self.regionalAnnualGroundwaterAbstractionLimit = pcr.cover(self.regionalAnnualGroundwaterAbstractionLimit, 0.0) self.regionalAnnualGroundwaterAbstractionLimit *= 1000. * 1000. * 1000. # unit: m3/year self.regionalAnnualGroundwaterAbstractionLimit = pcr.ifthen(self.landmask,\ self.regionalAnnualGroundwaterAbstractionLimit) # zones at which water allocation (surface and groundwater allocation) is determined self.usingAllocSegments = False if iniItems.landSurfaceOptions['allocationSegmentsForGroundSurfaceWater'] != "None": self.usingAllocSegments = True # incorporating groundwater distribution network: if self.usingAllocSegments and self.limitAbstraction == False: self.allocSegments = vos.readPCRmapClone(\ iniItems.landSurfaceOptions['allocationSegmentsForGroundSurfaceWater'], self.cloneMap,self.tmpDir,self.inputDir,isLddMap=False,cover=None,isNomMap=True) self.allocSegments = pcr.ifthen(self.landmask, self.allocSegments) cellArea = vos.readPCRmapClone(\ iniItems.routingOptions['cellAreaMap'], self.cloneMap,self.tmpDir,self.inputDir) cellArea = pcr.ifthen(self.landmask, cellArea) # TODO: integrate this one with the one coming from the routing module self.segmentArea = pcr.areatotal(pcr.cover(cellArea, 0.0), self.allocSegments) self.segmentArea = pcr.ifthen(self.landmask, self.segmentArea) self.report = True try: self.outDailyTotNC = iniItems.groundwaterOptions['outDailyTotNC'].split(",") self.outMonthTotNC = iniItems.groundwaterOptions['outMonthTotNC'].split(",") self.outMonthAvgNC = iniItems.groundwaterOptions['outMonthAvgNC'].split(",") self.outMonthEndNC = iniItems.groundwaterOptions['outMonthEndNC'].split(",") self.outAnnuaTotNC = iniItems.groundwaterOptions['outAnnuaTotNC'].split(",") self.outAnnuaAvgNC = iniItems.groundwaterOptions['outAnnuaAvgNC'].split(",") self.outAnnuaEndNC = iniItems.groundwaterOptions['outAnnuaEndNC'].split(",") except: self.report = False if self.report == True: self.outNCDir = iniItems.outNCDir self.netcdfObj = PCR2netCDF(iniItems) # # daily output in netCDF files: if self.outDailyTotNC[0] != "None": for var in self.outDailyTotNC: # creating the netCDF files: self.netcdfObj.createNetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_dailyTot.nc",\ var,"undefined") # MONTHly output in netCDF files: # - cummulative if self.outMonthTotNC[0] != "None": for var in self.outMonthTotNC: # initiating monthlyVarTot (accumulator variable): vars(self)[var+'MonthTot'] = None # creating the netCDF files: self.netcdfObj.createNetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthTot.nc",\ var,"undefined") # - average if self.outMonthAvgNC[0] != "None": for var in self.outMonthAvgNC: # initiating monthlyTotAvg (accumulator variable) vars(self)[var+'MonthTot'] = None # initiating monthlyVarAvg: vars(self)[var+'MonthAvg'] = None # creating the netCDF files: self.netcdfObj.createNetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthAvg.nc",\ var,"undefined") # - last day of the month if self.outMonthEndNC[0] != "None": for var in self.outMonthEndNC: # creating the netCDF files: self.netcdfObj.createNetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_monthEnd.nc",\ var,"undefined") # YEARly output in netCDF files: # - cummulative if self.outAnnuaTotNC[0] != "None": for var in self.outAnnuaTotNC: # initiating yearly accumulator variable: vars(self)[var+'AnnuaTot'] = None # creating the netCDF files: self.netcdfObj.createNetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaTot.nc",\ var,"undefined") # - average if self.outAnnuaAvgNC[0] != "None": for var in self.outAnnuaAvgNC: # initiating annualyVarAvg: vars(self)[var+'AnnuaAvg'] = None # initiating annualyTotAvg (accumulator variable) vars(self)[var+'AnnuaTot'] = None # creating the netCDF files: self.netcdfObj.createNetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaAvg.nc",\ var,"undefined") # - last day of the year if self.outAnnuaEndNC[0] != "None": for var in self.outAnnuaEndNC: # creating the netCDF files: self.netcdfObj.createNetCDF(str(self.outNCDir)+"/"+ \ str(var)+"_annuaEnd.nc",\ var,"undefined") #get initial conditions self.getICs(iniItems,spinUp)
def createInstancesInitial(self): import generalfunctions if readDistributionOfParametersFromDisk: path = '/home/derek/tmp/' maximumInterceptionCapacityPerLAI = pcr.scalar( path + pcrfw.generateNameS('RPic', self.currentSampleNumber()) + '.map') ksat = pcr.scalar( path + pcrfw.generateNameS('RPks', self.currentSampleNumber()) + '.map') regolithThicknessHomogeneous = pcr.scalar( path + pcrfw.generateNameS('RPrt', self.currentSampleNumber()) + '.map') saturatedConductivityMetrePerDay = pcr.scalar( path + pcrfw.generateNameS('RPsc', self.currentSampleNumber()) + '.map') multiplierMaxStomatalConductance = pcr.scalar( path + pcrfw.generateNameS('RPmm', self.currentSampleNumber()) + '.map') else: maximumInterceptionCapacityPerLAI = generalfunctions.areauniformBounds( 0.0001, 0.0005, pcr.nominal(1), pcr.scalar(cfg.maximumInterceptionCapacityValue), createRealizations) ksat = generalfunctions.areauniformBounds( 0.025, 0.05, pcr.nominal(1), pcr.scalar(cfg.ksatValue), createRealizations) regolithThicknessHomogeneous = generalfunctions.areauniformBounds( 1.0, 3.5, cfg.areas, pcr.scalar(cfg.regolithThicknessHomogeneousValue), createRealizations) saturatedConductivityMetrePerDay = generalfunctions.mapuniformBounds( 25.0, 40.0, pcr.scalar(cfg.saturatedConductivityMetrePerDayValue), createRealizations) multiplierMaxStomatalConductance = generalfunctions.mapuniformBounds( 0.8, 1.1, pcr.scalar(cfg.multiplierMaxStomatalConductanceValue), createRealizations) if swapCatchments: regolithThicknessHomogeneous = generalfunctions.swapValuesOfTwoRegions( cfg.areas, regolithThicknessHomogeneous, True) self.d_randomparameters = randomparameters.RandomParameters( timeStepsToReportRqs, setOfVariablesToReport, maximumInterceptionCapacityPerLAI, ksat, regolithThicknessHomogeneous, saturatedConductivityMetrePerDay, multiplierMaxStomatalConductance) # class for exchange variables in initial and dynamic # introduced to make filtering possible self.d_exchangevariables = exchangevariables.ExchangeVariables( timeStepsToReportSome, setOfVariablesToReport, ) ################ # interception # ################ self.ldd = cfg.lddMap initialInterceptionStore = pcr.scalar(0.000001) leafAreaIndex = pcr.scalar(cfg.leafAreaIndexValue) if swapCatchments: leafAreaIndex = generalfunctions.swapValuesOfTwoRegions( cfg.areas, leafAreaIndex, True) gapFraction = pcr.exp( -0.5 * leafAreaIndex) # equation 40 in Brolsma et al 2010a maximumInterceptionStore = maximumInterceptionCapacityPerLAI * leafAreaIndex self.d_interceptionuptomaxstore = interceptionuptomaxstore.InterceptionUpToMaxStore( self.ldd, initialInterceptionStore, maximumInterceptionStore, gapFraction, self.timeStepDurationHours, timeStepsToReportSome, setOfVariablesToReport) ################# # surface store # ################# initialSurfaceStore = pcr.scalar(0.0) maxSurfaceStore = pcr.scalar(cfg.maxSurfaceStoreValue) self.d_surfaceStore = surfacestore.SurfaceStore( initialSurfaceStore, maxSurfaceStore, self.timeStepDurationHours, timeStepsToReportSome, setOfVariablesToReport) ################ # infiltration # ################ # N initialMoistureContentFraction taken from 1st July # DK # we do not use rts and Gs as input to calculate initial moisture fraction to avoid # problems when the initial regolith thickness is calibrated (it might be thinner than # initialMoistureThick -> problems!) # instead, we use initial moisture content fraction as input, read from disk, it is just calculated # by pcrcalc 'mergeInitialMoistureContentFraction=Gs000008.761/rts00008.761' # note that I also changed the name for the initial soil moisture as a fraction initialSoilMoistureFractionFromDisk = pcr.scalar( cfg.initialSoilMoistureFractionFromDiskValue) if swapCatchments: initialSoilMoistureFractionFromDisk = generalfunctions.swapValuesOfTwoRegions( cfg.areas, initialSoilMoistureFractionFromDisk, True) # initial soil moisture as a fraction should not be above soil porosity as a fraction, just a check soilPorosityFraction = pcr.scalar(cfg.soilPorosityFractionValue) if swapCatchments: soilPorosityFraction = generalfunctions.swapValuesOfTwoRegions( cfg.areas, soilPorosityFraction, True) initialSoilMoistureFraction = pcr.min( soilPorosityFraction, initialSoilMoistureFractionFromDisk) hf = pcr.scalar(-0.0000001) self.d_infiltrationgreenandampt = infiltrationgreenandampt.InfiltrationGreenAndAmpt( soilPorosityFraction, initialSoilMoistureFraction, ksat, hf, self.timeStepDurationHours, timeStepsToReportSome, setOfVariablesToReport) #################### # subsurface water # #################### demOfBedrockTopography = self.dem stream = pcr.boolean(cfg.streamValue) theSlope = pcr.slope(self.dem) regolithThickness = pcr.ifthenelse(stream, 0.01, regolithThicknessHomogeneous) self.multiplierWiltingPoint = pcr.scalar(1.0) limitingPointFraction = pcr.scalar(cfg.limitingPointFractionValue) if swapCatchments: limitingPointFraction = generalfunctions.swapValuesOfTwoRegions( cfg.areas, limitingPointFraction, True) mergeWiltingPointFractionFS = pcr.scalar( cfg.mergeWiltingPointFractionFSValue) if swapCatchments: mergeWiltingPointFractionFS = generalfunctions.swapValuesOfTwoRegions( cfg.areas, mergeWiltingPointFractionFS, True) wiltingPointFractionNotChecked = mergeWiltingPointFractionFS * self.multiplierWiltingPoint wiltingPointFraction = pcr.min(wiltingPointFractionNotChecked, limitingPointFraction) fieldCapacityFraction = pcr.scalar(cfg.fieldCapacityFractionValue) if swapCatchments: fieldCapacityFraction = generalfunctions.swapValuesOfTwoRegions( cfg.areas, fieldCapacityFraction, True) self.d_subsurfaceWaterOneLayer = subsurfacewateronelayer.SubsurfaceWaterOneLayer( self.ldd, demOfBedrockTopography, regolithThickness, initialSoilMoistureFraction, soilPorosityFraction, wiltingPointFraction, fieldCapacityFraction, limitingPointFraction, saturatedConductivityMetrePerDay, self.timeStepDurationHours, timeStepsToReportSome, setOfVariablesToReport) ########## # runoff # ########## self.d_runoffAccuthreshold = runoffaccuthreshold.RunoffAccuthreshold( self.ldd, self.timeStepDurationHours, timeStepsToReportRqs, setOfVariablesToReport) ###################### # evapotranspiration # ###################### albedo = pcr.scalar(cfg.albedoValue) if swapCatchments: albedo = generalfunctions.swapValuesOfTwoRegions( cfg.areas, albedo, True) maxStomatalConductance = pcr.scalar( cfg.maxStomatalConductanceValue) * multiplierMaxStomatalConductance if swapCatchments: maxStomatalConductance = generalfunctions.swapValuesOfTwoRegions( cfg.areas, maxStomatalConductance, True) vegetationHeight = pcr.scalar(cfg.vegetationHeightValue) if swapCatchments: vegetationHeight = generalfunctions.swapValuesOfTwoRegions( cfg.areas, vegetationHeight, True) self.d_evapotranspirationPenman = evapotranspirationpenman.EvapotranspirationPenman( self.timeStepDurationHours, albedo, maxStomatalConductance, vegetationHeight, leafAreaIndex, timeStepsToReportSome, setOfVariablesToReport)
def readSoilMapOfFAO(self, iniItems, optionDict=None): # a dictionary/section of options that will be used if optionDict == None: optionDict = iniItems._sections[ "landSurfaceOptions" ] # iniItems.landSurfaceOptions # soil variable names given either in the ini or netCDF file: soilParameters = [ "airEntryValue1", "airEntryValue2", "poreSizeBeta1", "poreSizeBeta2", "resVolWC1", "resVolWC2", "satVolWC1", "satVolWC2", "KSat1", "KSat2", "percolationImp", ] if optionDict["soilPropertiesNC"] == str(None): for var in soilParameters: input = optionDict[str(var)] vars(self)[var] = vos.readPCRmapClone( input, self.cloneMap, self.tmpDir, self.inputDir ) vars(self)[var] = pcr.scalar(vars(self)[var]) if input == "percolationImp": vars(self)[var] = pcr.cover(vars(self)[var], 0.0) # extrapolation # - TODO: Make a general extrapolation option as a function in the virtualOS.py vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 0.75) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover(vars(self)[var], 0.0) else: soilPropertiesNC = vos.getFullPath( optionDict["soilPropertiesNC"], self.inputDir ) for var in soilParameters: vars(self)[var] = vos.netcdf2PCRobjCloneWithoutTime( soilPropertiesNC, var, cloneMapFileName=self.cloneMap ) if var == "percolationImp": vars(self)[var] = pcr.cover(vars(self)[var], 0.0) # extrapolation # - TODO: Make a general extrapolation option as a function in the virtualOS.py vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 0.75) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover( vars(self)[var], pcr.windowaverage(vars(self)[var], 1.00) ) vars(self)[var] = pcr.cover(vars(self)[var], 0.01) # make sure that resVolWC1 <= satVolWC1 self.resVolWC1 = pcr.min(self.resVolWC1, self.satVolWC1) self.resVolWC2 = pcr.min(self.resVolWC2, self.satVolWC2) if self.numberOfLayers == 2: self.satVolMoistContUpp = ( self.satVolWC1 ) # saturated volumetric moisture content (m3.m-3) self.satVolMoistContLow = self.satVolWC2 self.resVolMoistContUpp = ( self.resVolWC1 ) # residual volumetric moisture content (m3.m-3) self.resVolMoistContLow = self.resVolWC2 self.airEntryValueUpp = ( self.airEntryValue1 ) # air entry value (m) according to soil water retention curve of Clapp & Hornberger (1978) self.airEntryValueLow = self.airEntryValue2 self.poreSizeBetaUpp = ( self.poreSizeBeta1 ) # pore size distribution parameter according to Clapp & Hornberger (1978) self.poreSizeBetaLow = self.poreSizeBeta2 self.kSatUpp = self.KSat1 # saturated hydraulic conductivity (m.day-1) self.kSatLow = self.KSat2 if self.numberOfLayers == 3: self.satVolMoistContUpp000005 = self.satVolWC1 self.satVolMoistContUpp005030 = self.satVolWC1 self.satVolMoistContLow030150 = self.satVolWC2 self.resVolMoistContUpp000005 = self.resVolWC1 self.resVolMoistContUpp005030 = self.resVolWC1 self.resVolMoistContLow030150 = self.resVolWC2 self.airEntryValueUpp000005 = self.airEntryValue1 self.airEntryValueUpp005030 = self.airEntryValue1 self.airEntryValueLow030150 = self.airEntryValue2 self.poreSizeBetaUpp000005 = self.poreSizeBeta1 self.poreSizeBetaUpp005030 = self.poreSizeBeta1 self.poreSizeBetaLow030150 = self.poreSizeBeta2 self.kSatUpp000005 = self.KSat1 self.kSatUpp005030 = self.KSat1 self.kSatLow030150 = self.KSat2 self.percolationImp = pcr.cover( self.percolationImp, 0.0 ) # fractional area where percolation to groundwater store is impeded (dimensionless) # soil thickness and storage variable names # as given either in the ini or netCDF file: soilStorages = [ "firstStorDepth", "secondStorDepth", "soilWaterStorageCap1", "soilWaterStorageCap2", ] if optionDict["soilPropertiesNC"] == str(None): for var in soilStorages: input = optionDict[str(var)] temp = str(var) + "Inp" vars(self)[temp] = vos.readPCRmapClone( input, self.cloneMap, self.tmpDir, self.inputDir ) # extrapolation # - TODO: Make a general extrapolation option as a function in the virtualOS.py vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 0.75) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover(vars(self)[temp], 0.0) else: soilPropertiesNC = vos.getFullPath( optionDict["soilPropertiesNC"], self.inputDir ) for var in soilStorages: temp = str(var) + "Inp" vars(self)[temp] = vos.netcdf2PCRobjCloneWithoutTime( soilPropertiesNC, var, cloneMapFileName=self.cloneMap ) # extrapolation # - TODO: Make a general extrapolation option as a function in the virtualOS.py vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 0.75) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover( vars(self)[temp], pcr.windowaverage(vars(self)[temp], 1.05) ) vars(self)[temp] = pcr.cover(vars(self)[temp], 0.0) # layer thickness if self.numberOfLayers == 2: self.thickUpp = (0.30 / 0.30) * self.firstStorDepthInp self.thickLow = (1.20 / 1.20) * self.secondStorDepthInp if self.numberOfLayers == 3: self.thickUpp000005 = (0.05 / 0.30) * self.firstStorDepthInp self.thickUpp005030 = (0.25 / 0.30) * self.firstStorDepthInp self.thickLow030150 = (1.20 / 1.20) * self.secondStorDepthInp # soil storage if self.numberOfLayers == 2: # ~ self.storCapUpp = (0.30/0.30)*self.soilWaterStorageCap1Inp # ~ self.storCapLow = (1.20/1.20)*self.soilWaterStorageCap2Inp # 22 Feb 2014: We can calculate this based on thickness and porosity. self.storCapUpp = self.thickUpp * ( self.satVolMoistContUpp - self.resVolMoistContUpp ) self.storCapLow = self.thickLow * ( self.satVolMoistContLow - self.resVolMoistContLow ) self.rootZoneWaterStorageCap = ( self.storCapUpp + self.storCapLow ) # This is called as WMAX in the original pcrcalc script. if self.numberOfLayers == 3: self.storCapUpp000005 = self.thickUpp000005 * ( self.satVolMoistContUpp000005 - self.resVolMoistContUpp000005 ) self.storCapUpp005030 = self.thickUpp005030 * ( self.satVolMoistContUpp005030 - self.resVolMoistContUpp005030 ) self.storCapLow030150 = self.thickLow030150 * ( self.satVolMoistContLow030150 - self.resVolMoistContLow030150 ) self.rootZoneWaterStorageCap = ( self.storCapUpp000005 + self.storCapUpp005030 + self.storCapLow030150 )
def setMaximumStore(self, maximumStore): self.maximumStore = pcr.scalar(maximumStore) self.store = pcr.min(self.store, self.maximumStore)
def naturalLake( waterlevel, LakeLocs, LinkedLakeLocs, LakeArea, LakeThreshold, LakeStorFunc, LakeOutflowFunc, sh, hq, lake_b, lake_e, inflow, precip, pet, LakeAreasMap, JDOY, timestepsecs=86400, ): """ Run Natural Lake module to compute the new waterlevel and outflow. Solves lake water balance with linearisation and iteration procedure, for any rating and storage curve. For the case where storage curve is S = AH and Q=b(H-Ho)^2, uses the direct solution from the Modified Puls Approach (LISFLOOD). :ivar waterlevel: water level H in the lake :ivar LakeLocs: location of lake's outlet :ivar LinkedLakeLocs: ID of linked lakes :ivar LakeArea: total lake area :ivar LakeThreshold: water level threshold Ho under which outflow is zero :ivar LakeStorFunc: type of lake storage curve 1: S = AH 2: S = f(H) from lake data and interpolation :ivar LakeOutflowFunc: type of lake rating curve 1: Q = f(H) from lake data and interpolation 2: General Q = b(H - Ho)^e 3: Case of Puls Approach Q = b(H - Ho)^2 :ivar sh: data for storage curve :ivar hq: data for rating curve :ivar lake_b: rating curve coefficient :ivar lake_e: rating curve exponent :ivar inflow: inflow to the lake (surface runoff + river discharge + seepage) :ivar precip: precipitation map :ivar pet: PET map :ivar LakeAreasMap: lake extent map (for filtering P and PET) :ivar JDOY: Julian Day of Year to read storage/rating curve from data :ivar timestepsecs: model timestep in seconds :returns: waterlevel, outflow, prec_av, pet_av, storage """ mv = -999.0 LakeZeros = LakeArea * 0.0 waterlevel_start = waterlevel inflow = pcr.ifthen(pcr.boolean(LakeLocs), inflow) prec_av = pcr.ifthen( pcr.boolean(LakeLocs), pcr.areaaverage(precip, LakeAreasMap) ) pet_av = pcr.ifthen( pcr.boolean(LakeLocs), pcr.areaaverage(pet, LakeAreasMap) ) ### Modified Puls Approach (Burek et al., 2013, LISFLOOD) ### #ResOutflowFunc = 3 #Calculate lake factor and SI parameter LakeFactor = pcr.ifthenelse( LakeOutflowFunc == 3, LakeArea / (timestepsecs * (lake_b) ** 0.5), mv ) storage_start = pcr.ifthenelse( LakeStorFunc == 1, LakeArea * waterlevel_start, lookupResFunc(LakeLocs, waterlevel_start, sh, "0-1"), ) SIFactor = pcr.ifthenelse( LakeOutflowFunc == 3, ((storage_start + (prec_av-pet_av)*LakeArea/1000.0) / timestepsecs + inflow), mv ) #Adjust SIFactor for ResThreshold != 0 SIFactorAdj = SIFactor - LakeArea * LakeThreshold / timestepsecs #Calculate the new lake outflow/waterlevel/storage outflow = pcr.ifthenelse( LakeOutflowFunc == 3, pcr.ifthenelse( SIFactorAdj > 0.0, (-LakeFactor + (LakeFactor**2 + 2*SIFactorAdj) ** 0.5) ** 2, 0.0), LakeZeros ) storage = pcr.ifthenelse( LakeOutflowFunc == 3, (SIFactor - outflow) * timestepsecs, LakeZeros ) waterlevel = pcr.ifthenelse( LakeOutflowFunc == 3, storage / LakeArea, LakeZeros ) ### Linearisation and iteration for specific storage/rating curves ### np_lakeoutflowfunc = pcr.pcr2numpy(LakeOutflowFunc, 0.0) if ((bool(np.isin(1, np.unique(np_lakeoutflowfunc)))) or (bool(np.isin(2, np.unique(np_lakeoutflowfunc))))): np_lakelocs = pcr.pcr2numpy(LakeLocs, 0.0) np_linkedlakelocs = pcr.pcr2numpy(LinkedLakeLocs, 0.0) waterlevel_loop = waterlevel_start _outflow = [] nr_loop = np.max([int(timestepsecs / 21600), 1]) for n in range(0, nr_loop): np_waterlevel = pcr.pcr2numpy(waterlevel_loop, np.nan) np_waterlevel_lower = np_waterlevel.copy() for val in np.unique(np_linkedlakelocs): if val > 0: np_waterlevel_lower[np_linkedlakelocs == val] = np_waterlevel[ np.where(np_lakelocs == val) ] diff_wl = np_waterlevel - np_waterlevel_lower diff_wl[np.isnan(diff_wl)] = mv np_waterlevel_lower[np.isnan(np_waterlevel_lower)] = mv pcr_diff_wl = pcr.numpy2pcr(pcr.Scalar, diff_wl, mv) pcr_wl_lower = pcr.numpy2pcr(pcr.Scalar, np_waterlevel_lower, mv) storage_start_loop = pcr.ifthenelse( LakeStorFunc == 1, LakeArea * waterlevel_loop, lookupResFunc(LakeLocs, waterlevel_loop, sh, "0-1"), ) outflow_loop = pcr.ifthenelse( LakeOutflowFunc == 1, lookupResRegMatr(LakeLocs, waterlevel_loop, hq, JDOY), pcr.ifthenelse( pcr_diff_wl >= 0, pcr.max(lake_b * (waterlevel_loop - LakeThreshold) ** lake_e, 0), pcr.min(-1 * lake_b * (pcr_wl_lower - LakeThreshold) ** lake_e, 0), ), ) np_outflow = pcr.pcr2numpy(outflow_loop, np.nan) np_outflow_linked = np_lakelocs * 0.0 with np.errstate(invalid="ignore"): if np_outflow[np_outflow < 0] is not None: np_outflow_linked[ np.in1d(np_lakelocs, np_linkedlakelocs[np_outflow < 0]).reshape( np_linkedlakelocs.shape ) ] = np_outflow[np_outflow < 0] outflow_linked = pcr.numpy2pcr(pcr.Scalar, np_outflow_linked, 0.0) fl_nr_loop = float(nr_loop) storage_loop = ( storage_start_loop + (inflow * timestepsecs / fl_nr_loop) + (prec_av / fl_nr_loop / 1000.0) * LakeArea - (pet_av / fl_nr_loop / 1000.0) * LakeArea - (pcr.cover(outflow_loop, 0.0) * timestepsecs / fl_nr_loop) + (pcr.cover(outflow_linked, 0.0) * timestepsecs / fl_nr_loop) ) waterlevel_loop = pcr.ifthenelse( LakeStorFunc == 1, waterlevel_loop + (storage_loop - storage_start_loop) / LakeArea, lookupResFunc(LakeLocs, storage_loop, sh, "1-0"), ) np_outflow_nz = np_outflow * 0.0 with np.errstate(invalid="ignore"): np_outflow_nz[np_outflow > 0] = np_outflow[np_outflow > 0] _outflow.append(np_outflow_nz) outflow_av_temp = np.average(_outflow, 0) outflow_av_temp[np.isnan(outflow_av_temp)] = mv outflow_av = pcr.numpy2pcr(pcr.Scalar, outflow_av_temp, mv) #Add the discharge/waterlevel/storage from the loop to the one from puls approach outflow = pcr.ifthenelse( LakeOutflowFunc == 3, outflow, outflow_av ) waterlevel = pcr.ifthenelse( LakeOutflowFunc == 3, waterlevel, waterlevel_loop ) storage = pcr.ifthenelse( LakeOutflowFunc == 3, storage, storage_loop ) return waterlevel, outflow, prec_av, pet_av, storage
def __init__(self,startDate,endDate,nrIterDefault= 12.): pcrm.DynamicModel.__init__(self) ############## # * __init__ # ############## #-echo to screen print 'PCR-GLOBWB dynamic floodplain model - version 3.0 June 2012' print '\tbeta version with smoothed floodplain elevations' print '\tincluding lakes and reservoirs' #-constants: duration of time step (day) in seconds self.timeSec= duration*timeSec #-passing global variables to local ones #-cloone and cell area self.clone= clone self.cellArea= cellArea #-model settings self.nrIterDefault= nrIterDefault self.duration= duration self.currentDate= startDate self.endDate= endDate self.areaFractions= areaFractions self.relZFileName= os.path.join(mapsDir,relZFileName) self.reductionKK= reductionKK self.criterionKK= criterionKK #-number of entries in list and derived slopes and volumes self.nrEntries= len(self.areaFractions) self.relZ= [0.]*self.nrEntries self.floodVolume= [0.]*(self.nrEntries) #-flood plain and channel characteristics self.channelGradient= pcr.max(1.e-7,channelGradient) self.LDD= LDD self.channelWidth= channelWidth self.channelLength= channelLength self.channelDepth= channelDepth self.channelManN= channelManN self.floodplainManN= floodplainManN self.floodplainMask= floodplainMask self.waterFractionMask= fractionWater #-waterBodies self.waterBodies= waterBodies self.waterBodies.actualArea= self.waterBodies.retrieveMapValue(pcr.areatotal(self.waterFractionMask*\ self.cellArea,self.waterBodies.distribution)) #self.reservoirDemandTSS= readTSS(reservoirDemandTSS) #-map names: initial maps of discharge and storage self.QIniMap= pcrm.generateNameT(QFileName,0).replace('.000','.ini') self.actualStorageIniMap= pcrm.generateNameT(actualStorageFileName,0).replace('.000','.ini') self.averageQ= averageQ self.bankfulQ= bankfulQ #-patch elevations: those that are part of sills are updated on the basis of the floodplain gradient # using local distances deltaX per increment upto z[N] and the sum over sills #-fill all lists including smoothing interval and slopes for iCnt in range(1,self.nrEntries): self.relZ[iCnt]= clippedRead.get(self.relZFileName %\ (self.areaFractions[iCnt]*100)) #-minimum slope of floodplain, being defined as the longest sill, first used to retrieve # longest cumulative distance deltaX= [self.cellArea**0.5]*self.nrEntries deltaX[0]= 0. sumX= deltaX[:] minSlope= 0. for iCnt in range(self.nrEntries): if iCnt < self.nrEntries-1: deltaX[iCnt]= (self.areaFractions[iCnt+1]**0.5-self.areaFractions[iCnt]**0.5)*deltaX[iCnt] else: deltaX[iCnt]= (1.-self.areaFractions[iCnt-1]**0.5)*deltaX[iCnt] if iCnt > 0: sumX[iCnt]= pcr.ifthenelse(self.relZ[iCnt] == self.relZ[iCnt-1],sumX[iCnt-1]+deltaX[iCnt],0.) minSlope= pcr.ifthenelse(self.relZ[iCnt] == self.relZ[iCnt-1],\ pcr.max(sumX[iCnt],minSlope),minSlope) minSlope= pcr.min(self.channelGradient,0.5*pcr.max(deltaX[1],minSlope)**-1.) #-add small increment to elevations to each sill except in the case of lakes for iCnt in range(self.nrEntries): self.relZ[iCnt]= self.relZ[iCnt]+sumX[iCnt]*pcr.ifthenelse(self.relZ[self.nrEntries-1] > 0.,\ minSlope,0.) #-set slope and smoothing interval between dy= y(i+1)-y(i) and dx= x(i+1)-x(i) # on the basis of volume #-slope and smoothing interval self.kSlope= [0.]*(self.nrEntries) self.mInterval= [0.]*(self.nrEntries) for iCnt in range(1,self.nrEntries): self.floodVolume[iCnt]= self.floodVolume[iCnt-1]+\ 0.5*(self.areaFractions[iCnt]+self.areaFractions[iCnt-1])*\ (self.relZ[iCnt]-self.relZ[iCnt-1])*self.cellArea self.kSlope[iCnt-1]= (self.areaFractions[iCnt]-self.areaFractions[iCnt-1])/\ pcr.max(0.001,self.floodVolume[iCnt]-self.floodVolume[iCnt-1]) for iCnt in range(1,self.nrEntries): if iCnt < (self.nrEntries-1): self.mInterval[iCnt]= 0.5*self.reductionKK*pcr.min(self.floodVolume[iCnt+1]-self.floodVolume[iCnt],\ self.floodVolume[iCnt]-self.floodVolume[iCnt-1]) else: self.mInterval[iCnt]= 0.5*self.reductionKK*(self.floodVolume[iCnt]-self.floodVolume[iCnt-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 dynamic(self): import generalfunctions # time self.d_dateTimePCRasterPython.update() timeDatetimeFormat = self.d_dateTimePCRasterPython.getTimeDatetimeFormat( ) # precipitation # for calibration rainfallFluxDeterm = pcr.timeinputscalar( cfg.rainfallFluxDetermTimeSeries, pcr.nominal(cfg.rainfallFluxDetermTimeSeriesAreas)) # for the experiments rainfallFlux = rainfallFluxDeterm #generalfunctions.mapNormalRelativeError(rainfallFluxDeterm,0.25) self.d_exchangevariables.cumulativePrecipitation = \ self.d_exchangevariables.cumulativePrecipitation + rainfallFlux * self.timeStepDuration # interception store actualAdditionFluxToInterceptionStore = self.d_interceptionuptomaxstore.addWater( rainfallFlux) throughfallFlux = rainfallFlux - actualAdditionFluxToInterceptionStore # surface store totalToSurfaceFlux = throughfallFlux + self.d_exchangevariables.upwardSeepageFlux potentialToSurfaceStoreFlux = self.d_surfaceStore.potentialToFlux() # potential infiltration potentialHortonianInfiltrationFlux = self.d_infiltrationgreenandampt.potentialInfiltrationFluxFunction( ) maximumSaturatedOverlandFlowInfiltrationFlux = self.d_subsurfaceWaterOneLayer.getMaximumAdditionFlux( ) potentialInfiltrationFlux = pcr.min( potentialHortonianInfiltrationFlux, maximumSaturatedOverlandFlowInfiltrationFlux) # abstraction from surface water potentialAbstractionFromSurfaceWaterFlux = potentialToSurfaceStoreFlux + potentialInfiltrationFlux actualAbstractionFromSurfaceWaterFlux, runoffCubicMetresPerHour = self.d_runoffAccuthreshold.update( totalToSurfaceFlux, potentialAbstractionFromSurfaceWaterFlux) potentialOutSurfaceStoreFlux = self.d_surfaceStore.potentialOutFlux() # infiltration availableForInfiltrationFlux = potentialOutSurfaceStoreFlux + actualAbstractionFromSurfaceWaterFlux availableForInfiltrationNotExceedingMaximumSaturatedOverlandFlowFlux = pcr.min( availableForInfiltrationFlux, maximumSaturatedOverlandFlowInfiltrationFlux) actualInfiltrationFlux = self.d_infiltrationgreenandampt.update( availableForInfiltrationNotExceedingMaximumSaturatedOverlandFlowFlux ) # surface store surfaceStoreChange = actualAbstractionFromSurfaceWaterFlux - actualInfiltrationFlux self.d_surfaceStore.update(surfaceStoreChange) actualAdditionFlux = self.d_subsurfaceWaterOneLayer.addWater( actualInfiltrationFlux) if cfg.with_shading: # solar radiation (POTRAD, shading effect and inclination) fractionReceived, fractionReceivedFlatSurface, shaded = \ self.d_shading.update(timeDatetimeFormat) # we assume all cells receive the same solar radiation as measured by the device # except for shading, if shading, there is nothing received fractionReceived = pcr.ifthenelse(shaded, pcr.scalar(0.0), pcr.scalar(1.0)) else: fractionReceived = pcr.scalar(cfg.fractionReceivedValue) fractionReceivedFlatSurface = pcr.scalar( cfg.fractionReceivedFlatSurfaceValue) fWaterPotential = self.d_subsurfaceWaterOneLayer.getFWaterPotential() # potential evapotranspiration airTemperatureDeterm = pcr.timeinputscalar( cfg.airTemperatureDetermString, self.clone) airTemperature = airTemperatureDeterm #airTemperatureDeterm+mapnormal() relativeHumidityDeterm = pcr.timeinputscalar( cfg.relativeHumidityDetermString, self.clone) relativeHumidity = relativeHumidityDeterm #pcr.max(pcr.min(relativeHumidityDeterm+mapnormal()*0.1,pcr.scalar(1.0)),pcr.scalar(0)) incomingShortwaveRadiationFlatSurface = pcr.timeinputscalar( cfg.incomingShortwaveRadiationFlatSurfaceString, self.clone) # incomingShortwaveRadiationFlatSurface = pcr.max(pcr.scalar(0), # generalfunctions.mapNormalRelativeError(incomingShortwaveRadiationFlatSurfaceDeterm,0.25)) incomingShortwaveRadiationAtSurface = incomingShortwaveRadiationFlatSurface * fractionReceived windVelocityDeterm = pcr.timeinputscalar(cfg.windVelocityDetermString, self.clone) windVelocity = windVelocityDeterm #generalfunctions.mapNormalRelativeError(windVelocityDeterm,0.25) elevationAboveSeaLevelOfMeteoStation = cfg.elevationAboveSeaLevelOfMeteoStationValue potentialEvapotranspirationFlux, \ potentialEvapotranspirationAmount, \ potentialEvapotranspirationFromCanopyFlux, \ potentialEvapotranspirationFromCanopyAmount = \ self.d_evapotranspirationPenman.potentialEvapotranspiration( airTemperature, relativeHumidity, incomingShortwaveRadiationAtSurface, incomingShortwaveRadiationFlatSurface, fractionReceivedFlatSurface, windVelocity, elevationAboveSeaLevelOfMeteoStation, fWaterPotential, rainfallFlux < 0.000000000001) potentialEvapotranspirationFluxNoNegativeValues = pcr.max( 0.0, potentialEvapotranspirationFlux) potentialEvapotranspirationFluxFromCanopyNoNegativeValues = pcr.max( 0.0, potentialEvapotranspirationFromCanopyFlux) # evapotranspirate first from interception store actualAbstractionFluxFromInterceptionStore = self.d_interceptionuptomaxstore.abstractWater( potentialEvapotranspirationFluxFromCanopyNoNegativeValues) # fraction of soil evapotranspiration depends on evapo from canopy evapFromSoilMultiplierMV = (potentialEvapotranspirationFluxFromCanopyNoNegativeValues - actualAbstractionFluxFromInterceptionStore) / \ potentialEvapotranspirationFluxFromCanopyNoNegativeValues self.d_exchangevariables.evapFromSoilMultiplier = \ pcr.ifthenelse(potentialEvapotranspirationFluxNoNegativeValues < 0.0000000000001, pcr.scalar(1), evapFromSoilMultiplierMV) # evapotranspirate from subsurface store # potentialEvapotranspirationFluxFromSubsurface= \ # pcr.max(0.0,potentialEvapotranspirationFluxNoNegativeValues-actualAbstractionFluxFromInterceptionStore) potentialEvapotranspirationFluxFromSubsurface = self.d_exchangevariables.evapFromSoilMultiplier * \ potentialEvapotranspirationFluxNoNegativeValues actualAbstractionFluxFromSubsurface = self.d_subsurfaceWaterOneLayer.abstractWater( potentialEvapotranspirationFluxFromSubsurface) # upward seepage from subsurfacestore self.d_exchangevariables.upwardSeepageFlux = self.d_subsurfaceWaterOneLayer.lateralFlow( ) # reports self.reportComponentsDynamic() self.reportRandomParametersDynamic() self.printComponentsDynamic() if doReportComponentsDynamicAsNumpy: self.reportComponentsDynamicAsNumpy()
def agriZone_Ep_Sa_beta_frostSamax_surfTemp(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on LP - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa --> incorporation of beta function - Fa is based on storage in Sa - Fa is decreased in case of frozen soil - Code for ini-file: 13 """ JarvisCoefficients.calcEp(self, k) self.PotEvaporation = self.EpHour self.FrDur[k] = pcr.min( self.FrDur[k] + pcr.ifthenelse( self.TempSurf > 0, self.ratFT[k] * self.TempSurf, self.TempSurf ) * self.dayDeg[k], 0, ) self.Ft = pcr.min( pcr.max( self.FrDur[k] / (self.FrDur1[k] - self.FrDur0[k]) - self.FrDur0[k] / (self.FrDur1[k] - self.FrDur0[k]), self.samin[k], ), 1, ) self.samax2 = self.samax[k] * pcr.scalar(self.catchArea) * self.Ft self.Qaadd = pcr.max(self.Sa_t[k] + self.Pe - self.samax2, 0) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) self.SaN = pcr.min(self.Sa[k] / self.samax2, 1) self.SuN = self.Su[k] / self.sumax[k] self.Ea1 = pcr.max((self.PotEvaporation - self.Ei), 0) * pcr.min( self.Sa[k] / (self.samax2 * self.LP[k]), 1 ) self.Qa1 = (self.Pe - self.Qaadd) * (1 - (1 - self.SaN) ** self.beta[k]) self.Fa1 = pcr.ifthenelse( self.SaN > 0, self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * e ** (-self.decF[k] * (1 - self.SaN)), 0, ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Qa1 - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Qa = ( self.Qa1 + ( self.Qa1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Fa = ( self.Fa1 + ( self.Fa1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Ea = ( self.Ea1 + ( self.Ea1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Ea - self.Fa - self.Qa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[k] = ( self.Pe - self.Ea - self.Qa - self.Qaadd - self.Fa - self.Sa[k] + self.Sa_t[k] ) self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa + self.Qaadd self.Fa_[k] = self.Fa self.Ft_[k] = self.Ft
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, availableForInfiltrationFlux): self.actualInfiltrationFlux = pcr.min(availableForInfiltrationFlux, self.potentialInfiltrationFlux) self.store = self.store + self.fluxToAmount( self.actualInfiltrationFlux) return self.actualInfiltrationFlux
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