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 getWaterBodyOutflow( self, maxTimestepsToAvgDischargeLong, avgChannelDischarge, length_of_time_step=vos.secondsPerDay(), downstreamDemand=None, ): # outflow in volume from water bodies with lake type (m3): lakeOutflow = self.getLakeOutflow(avgChannelDischarge, length_of_time_step) # outflow in volume from water bodies with reservoir type (m3): if isinstance(downstreamDemand, types.NoneType): downstreamDemand = pcr.scalar(0.0) reservoirOutflow = self.getReservoirOutflow(avgChannelDischarge, length_of_time_step, downstreamDemand) # outgoing/release volume from lakes and/or reservoirs self.waterBodyOutflow = pcr.cover(reservoirOutflow, lakeOutflow) # make sure that all water bodies have outflow: self.waterBodyOutflow = pcr.max(0., pcr.cover(self.waterBodyOutflow, 0.0)) # limit outflow to available storage factor = 0.25 # to avoid flip flop self.waterBodyOutflow = pcr.min(self.waterBodyStorage * factor, self.waterBodyOutflow) # unit: m3 # use round values self.waterBodyOutflow = (pcr.rounddown(self.waterBodyOutflow / 1.) * 1. ) # unit: m3 # outflow rate in m3 per sec waterBodyOutflowInM3PerSec = (self.waterBodyOutflow / length_of_time_step) # unit: m3/s # updating (long term) average outflow (m3/s) ; # - needed to constrain/maintain reservoir outflow: # temp = pcr.max( 1.0, pcr.min( maxTimestepsToAvgDischargeLong, self.timestepsToAvgDischarge - 1.0 + length_of_time_step / vos.secondsPerDay(), ), ) deltaOutflow = waterBodyOutflowInM3PerSec - self.avgOutflow R = deltaOutflow * (length_of_time_step / vos.secondsPerDay()) / temp self.avgOutflow = self.avgOutflow + R self.avgOutflow = pcr.max(0.0, self.avgOutflow) # # for the reference, see the "weighted incremental algorithm" in http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance # update waterBodyStorage (after outflow): self.waterBodyStorage = self.waterBodyStorage - self.waterBodyOutflow self.waterBodyStorage = pcr.max(0.0, self.waterBodyStorage)
def 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., ) # 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 weirWidthUsed = pcr.max( weirWidthUsed, self.minWeirWidth ) # TODO: minWeirWidth based on the GRanD database 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 update( self, newStorageAtLakeAndReservoirs, timestepsToAvgDischarge, maxTimestepsToAvgDischargeShort, maxTimestepsToAvgDischargeLong, currTimeStep, avgChannelDischarge, length_of_time_step=vos.secondsPerDay(), downstreamDemand=None, ): if self.debugWaterBalance: preStorage = self.waterBodyStorage # unit: m self.timestepsToAvgDischarge = ( timestepsToAvgDischarge ) # TODO: include this one in "currTimeStep" # obtain inflow (and update storage) self.moveFromChannelToWaterBody( newStorageAtLakeAndReservoirs, timestepsToAvgDischarge, maxTimestepsToAvgDischargeShort, length_of_time_step, ) # calculate outflow (and update storage) self.getWaterBodyOutflow( maxTimestepsToAvgDischargeLong, avgChannelDischarge, length_of_time_step, downstreamDemand, ) if self.debugWaterBalance: vos.waterBalanceCheck( [pcr.cover(self.inflow / self.waterBodyArea, 0.0)], [pcr.cover(self.waterBodyOutflow / self.waterBodyArea, 0.0)], [pcr.cover(preStorage / self.waterBodyArea, 0.0)], [pcr.cover(self.waterBodyStorage / self.waterBodyArea, 0.0)], "WaterBodyStorage (unit: m)", True, currTimeStep.fulldate, threshold=5e-3, )
def update(self,newStorageAtLakeAndReservoirs,\ timestepsToAvgDischarge,\ maxTimestepsToAvgDischargeShort,\ maxTimestepsToAvgDischargeLong,\ currTimeStep,\ avgChannelDischarge,\ length_of_time_step = vos.secondsPerDay(),\ downstreamDemand = None): if self.debugWaterBalance: \ preStorage = self.waterBodyStorage # unit: m self.timestepsToAvgDischarge = timestepsToAvgDischarge # TODO: include this one in "currTimeStep" # obtain inflow (and update storage) self.moveFromChannelToWaterBody(\ newStorageAtLakeAndReservoirs,\ timestepsToAvgDischarge,\ maxTimestepsToAvgDischargeShort,\ length_of_time_step) # calculate outflow (and update storage) self.getWaterBodyOutflow(\ maxTimestepsToAvgDischargeLong,\ avgChannelDischarge,\ length_of_time_step,\ downstreamDemand) if self.debugWaterBalance: \ vos.waterBalanceCheck([ pcr.cover(self.inflow/self.waterBodyArea,0.0)],\ [pcr.cover(self.waterBodyOutflow/self.waterBodyArea,0.0)],\ [ pcr.cover(preStorage/self.waterBodyArea,0.0)],\ [pcr.cover(self.waterBodyStorage/self.waterBodyArea,0.0)],\ 'WaterBodyStorage (unit: m)',\ True,\ currTimeStep.fulldate,threshold=5e-3) self.waterBodyBalance = (pcr.cover(self.inflow/self.waterBodyArea, 0.0) - pcr.cover(self.waterBodyOutflow/self.waterBodyArea,0.0)) -\ (pcr.cover(self.waterBodyStorage/self.waterBodyArea,0.0) - pcr.cover(preStorage/self.waterBodyArea,0.0))
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