def initial(self): ##################### # * initial section # ##################### #-constants # betaQ [-]: constant of kinematic wave momentum equation self.betaQ = 0.6 #-channel LDD self.channelLDD= pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.ldd(5),self.LDD) #-channel area and storage self.channelArea = self.channelWidth * self.channelLength self.channelStorageCapacity= pcr.ifthenelse(self.waterBodies.distribution == 0,\ self.channelArea*self.channelDepth,pcr.scalar(0.)) #-basin outlets self.basinOutlet = pcr.pit(self.LDD) != 0 #-read initial conditions self.Q = clippedRead.get(self.QIniMap) self.actualStorage = clippedRead.get(self.actualStorageIniMap) self.actualStorage= pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.ifthenelse(self.waterBodies.location != 0,\ pcr.areatotal(self.actualStorage,self.waterBodies.distribution),0),\ self.actualStorage) self.waterBodies.actualStorage = self.waterBodies.retrieveMapValue( self.actualStorage) #-update targets of average and bankful discharge self.waterBodies.averageQ = self.waterBodies.retrieveMapValue( self.averageQ) self.waterBodies.bankfulQ = self.waterBodies.retrieveMapValue( self.bankfulQ) #-return the parameters for the kinematic wave, # including alpha, wetted area, flood fraction, flood volume and depth # and the corresponding land area 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) #-update on velocity and check on Q - NOTE: does not work in case of reservoirs! self.flowVelocity = pcr.ifthenelse(self.wettedArea > 0, self.Q / self.wettedArea, 0.) pcr.report( self.flowVelocity, pcrm.generateNameT(flowVelocityFileName, 0).replace('.000', '.ini')) #-setting initial values for specific runoff and surface water extraction self.landSurfaceQ = pcr.scalar(0.) self.potWaterSurfaceQ = pcr.scalar(0.) self.surfaceWaterExtraction = pcr.scalar(0.) #-budget check: setting initial values for cumulative discharge and # net cumulative input, including initial storage [m3] self.totalDischarge = pcr.scalar(0.) self.cumulativeDeltaStorage = pcr.catchmenttotal( self.actualStorage, self.LDD)
def extractDailyData(arcPath,arcFile,varPath,varList,leapYear): #-extracts daily data from tar file nDays= 365 if leapYear: nDays= nDays+1 arcList= [] for var in varList: for day in range(1,nDays+1): arcList.append(generateNameT(var,day)) archive= tarfile.open(os.path.join(arcPath,arcFile),'r:gz') for tarInfo in archive: if tarInfo.name in arcList: archive.extract(tarInfo,varPath) archive.close()
def extractDailyData(arcPath, arcFile, varPath, varList, leapYear): #-extracts daily data from tar file nDays = 365 if leapYear: nDays = nDays + 1 arcList = [] for var in varList: for day in range(1, nDays + 1): arcList.append(generateNameT(var, day)) archive = tarfile.open(os.path.join(arcPath, arcFile), 'r:gz') for tarInfo in archive: if tarInfo.name in arcList: archive.extract(tarInfo, varPath) archive.close()
def initial(self): ##################### # * initial section # ##################### #-constants # betaQ [-]: constant of kinematic wave momentum equation self.betaQ= 0.6 #-channel LDD self.channelLDD= pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.ldd(5),self.LDD) #-channel area and storage self.channelArea= self.channelWidth*self.channelLength self.channelStorageCapacity= pcr.ifthenelse(self.waterBodies.distribution == 0,\ self.channelArea*self.channelDepth,pcr.scalar(0.)) #-basin outlets self.basinOutlet= pcr.pit(self.LDD) != 0 #-read initial conditions self.Q= clippedRead.get(self.QIniMap) self.actualStorage= clippedRead.get(self.actualStorageIniMap) self.actualStorage= pcr.ifthenelse(self.waterBodies.distribution != 0,\ pcr.ifthenelse(self.waterBodies.location != 0,\ pcr.areatotal(self.actualStorage,self.waterBodies.distribution),0),\ self.actualStorage) self.waterBodies.actualStorage= self.waterBodies.retrieveMapValue(self.actualStorage) #-update targets of average and bankful discharge self.waterBodies.averageQ= self.waterBodies.retrieveMapValue(self.averageQ) self.waterBodies.bankfulQ= self.waterBodies.retrieveMapValue(self.bankfulQ) #-return the parameters for the kinematic wave, # including alpha, wetted area, flood fraction, flood volume and depth # and the corresponding land area 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) #-update on velocity and check on Q - NOTE: does not work in case of reservoirs! self.flowVelocity= pcr.ifthenelse(self.wettedArea > 0,self.Q/self.wettedArea,0.) pcr.report(self.flowVelocity,pcrm.generateNameT(flowVelocityFileName,0).replace('.000','.ini')) #-setting initial values for specific runoff and surface water extraction self.landSurfaceQ= pcr.scalar(0.) self.potWaterSurfaceQ= pcr.scalar(0.) self.surfaceWaterExtraction= pcr.scalar(0.) #-budget check: setting initial values for cumulative discharge and # net cumulative input, including initial storage [m3] self.totalDischarge= pcr.scalar(0.) self.cumulativeDeltaStorage= pcr.catchmenttotal(self.actualStorage,self.LDD)
def stackAverage(path,Root,StackStart,StackEnd): #calculates the average from a stack of maps, missing maps are skipped #Initialization MV= pcr.scalar(-999) NCount= pcr.scalar(0) SumStack= pcr.scalar(0) for StackNumber in range(StackStart,StackEnd): try: InMap= pcr.readmap(generateNameT(os.path.join(path,Root),StackNumber)) except: InMap= MV; InMap= pcr.cover(InMap,MV) SumStack= SumStack+InMap NCount= NCount+pcr.ifthenelse(InMap <> MV,pcr.scalar(1),pcr.scalar(0)) AvgStack= pcr.ifthenelse(NCount>0,SumStack/NCount,MV) return AvgStack
def stackAverage(path, Root, StackStart, StackEnd): #calculates the average from a stack of maps, missing maps are skipped #Initialization MV = pcr.scalar(-999) NCount = pcr.scalar(0) SumStack = pcr.scalar(0) for StackNumber in range(StackStart, StackEnd): try: InMap = pcr.readmap( generateNameT(os.path.join(path, Root), StackNumber)) except: InMap = MV InMap = pcr.cover(InMap, MV) SumStack = SumStack + InMap NCount = NCount + pcr.ifthenelse(InMap <> MV, pcr.scalar(1), pcr.scalar(0)) AvgStack = pcr.ifthenelse(NCount > 0, SumStack / NCount, MV) return AvgStack
pcr.report(bankfulQ,targetFileName) targetFileName= os.path.join(pathIniSource,'qwat_avg_longterm.map') pcr.report(averageQWat,targetFileName) #-define initital values for waterbodies #-initializing class of water bodies for the first year if not initializeRoutingModel: pathIniSource= os.path.join(pathIni,modelSignature,'ini_%04d' % startYear) fractionWater= clippedRead.get(os.path.join(tempDir,'fracwat_%d.map' % initYear)) waterBodies= pcrglobWaterBodies(os.path.join(tempDir,'waterbodyid_%d.map' % initYear),\ os.path.join(tempDir,'waterbodyoutlet_%d.map' % initYear),os.path.join(tempDir,'waterbodytype_%d.map' % initYear),\ os.path.join(mapsDir,'channel_width.map'),os.path.join(pathIniSource,'qavg_longterm.map'),\ os.path.join(pathIniSource,'qbank_longterm.map'),LDDMap,os.path.join(tempDir,'reservoirparameters_%d_30min_.tbl' % initYear),timeSec,clippedRead) #-create initial files if the routing model is initialized if initializeRoutingModel: for variable in resStackList[iniOutVarStart:addOutVarStart]: targetFileName= os.path.join(pathIniSource,pcrm.generateNameT(variable,0).replace('.000','.ini')) if 'qc' in variable: pcr.report(pcr.scalar(0),targetFileName) elif 'wst'in variable: waterBodiesType= pcr.nominal(waterBodies.returnMapValue(pcr.spatial(pcr.scalar(0.)),waterBodies.type)) endorheicLakes= pcr.ifthenelse((pcr.areatotal(pcr.scalar(waterBodies.outlet != 0),waterBodies.distribution) == 0) & \ (waterBodies.distribution != 0),waterBodies.distribution,0) #-storage of water bodies is assigned at outlets #-storage over endorheic lakes actualStorage= pcr.ifthen(endorheicLakes != 0,\ pcr.upstream(LDD,pcr.ifthenelse((endorheicLakes == 0) & ((waterBodies.distribution == 0) | (waterBodies.outlet != 0)),\ averageQ,0))) actualStorage= pcr.ifthen((waterBodies.location != 0) & (endorheicLakes != 0),\ pcr.max(0.,pcr.areatotal(pcr.cover(actualStorage*timeSec*365.25+averageQWat,0),endorheicLakes))) #-storage over other water bodies actualStorage= pcr.cover(actualStorage,\
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 __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 dynamic(self): self.counter += 1 print( str(self.curdate.day) + '-' + str(self.curdate.month) + '-' + str(self.curdate.year) + ' t = ' + str(self.counter)) #-Snow and rain fraction settings for non-glacier part of model cell SnowFrac = pcr.ifthenelse(self.SnowStore > 0, pcr.scalar(1 - self.GlacFrac), 0) RainFrac = pcr.ifthenelse(self.SnowStore == 0, pcr.scalar(1 - self.GlacFrac), 0) #-Read the precipitation time-series if self.precNetcdfFLAG == 1: #-read forcing by netcdf input Precip = self.netcdf2PCraster.netcdf2pcrDynamic(self, pcr, 'Prec') else: #-read forcing by map input Precip = pcr.readmap(pcrm.generateNameT(self.Prec, self.counter)) PrecipTot = Precip #-Report Precip self.reporting.reporting(self, pcr, 'TotPrec', Precip) self.reporting.reporting(self, pcr, 'TotPrecF', Precip * (1 - self.GlacFrac)) #-Temperature and determine reference evapotranspiration if self.tempNetcdfFLAG == 1: #-read forcing by netcdf input Temp = self.netcdf2PCraster.netcdf2pcrDynamic(self, pcr, 'Temp') else: #-read forcing by map input Temp = pcr.readmap(pcrm.generateNameT(self.Tair, self.counter)) if self.ETREF_FLAG == 0: if self.TminNetcdfFLAG == 1: #-read forcing by netcdf input TempMin = self.netcdf2PCraster.netcdf2pcrDynamic( self, pcr, 'Tmin') else: #-read forcing by map input TempMin = pcr.readmap( pcrm.generateNameT(self.Tmin, self.counter)) if self.TmaxNetcdfFLAG == 1: #-read forcing by netcdf input TempMax = self.netcdf2PCraster.netcdf2pcrDynamic( self, pcr, 'Tmax') else: #-read forcing by map input TempMax = pcr.readmap( pcrm.generateNameT(self.Tmax, self.counter)) ETref = self.Hargreaves.Hargreaves( pcr, self.Hargreaves.extrarad(self, pcr), Temp, TempMax, TempMin) else: ETref = pcr.readmap(pcrm.generateNameT(self.ETref, self.counter)) self.reporting.reporting(self, pcr, 'TotETref', ETref) self.reporting.reporting(self, pcr, 'TotETrefF', ETref * (1 - self.GlacFrac)) #-Interception and effective precipitation if self.DynVegFLAG == 1: #-read dynamic processes dynamic vegetation Precip = self.dynamic_veg.dynamic(self, pcr, pcrm, np, Precip, ETref) elif self.KcStatFLAG == 0: #-Try to read the KC map series try: self.Kc = pcr.readmap( pcrm.generateNameT(self.Kcmaps, self.counter)) self.KcOld = self.Kc except: self.Kc = self.KcOld #-report mm effective precipitation for sub-basin averages if self.mm_rep_FLAG == 1 and self.Prec_mm_FLAG == 1 and ( self.RoutFLAG == 1 or self.ResFLAG == 1 or self.LakeFLAG == 1): self.PrecSubBasinTSS.sample( pcr.catchmenttotal(Precip * (1 - self.GlacFrac), self.FlowDir) / pcr.catchmenttotal(1, self.FlowDir)) #-Snow, rain, and glacier calculations for glacier fraction of cell if self.GlacFLAG: #-read dynamic processes glacier Rain_GLAC, Snow_GLAC, ActSnowMelt_GLAC, SnowR_GLAC, GlacMelt, GlacPerc, self.GlacR = self.glacier.dynamic( self, pcr, pd, Temp, Precip) #-If glacier module is not used, then else: Rain_GLAC = 0 Snow_GLAC = 0 ActSnowMelt_GLAC = 0 self.TotalSnowStore_GLAC = 0 SnowR_GLAC = 0 self.GlacR = 0 GlacMelt = 0 GlacPerc = 0 # Calculate snow and rain for non-glacier part of cell if self.SnowFLAG == 1: #-read dynamic processes snow Rain, self.SnowR, OldTotalSnowStore = self.snow.dynamic( self, pcr, Temp, Precip, Snow_GLAC, ActSnowMelt_GLAC, SnowFrac, RainFrac, SnowR_GLAC) else: Rain = Precip self.SnowR = 0 OldTotalSnowStore = 0 self.TotalSnowStore = 0 #-Report Rain self.reporting.reporting(self, pcr, 'TotRain', Rain) self.reporting.reporting(self, pcr, 'TotRainF', Rain * (1 - self.GlacFrac) + Rain_GLAC) # for entire cell #-Potential evapotranspiration ETpot = self.ET.ETpot(ETref, self.Kc) if self.ETOpenWaterFLAG == 1: self.ETOpenWater = self.ET.ETpot(ETref, self.kcOpenWater) #-Report ETpot self.reporting.reporting(self, pcr, 'TotETpot', ETpot) self.reporting.reporting(self, pcr, 'TotETpotF', ETpot * RainFrac) #-Rootzone calculations self.RootWater = self.RootWater + self.CapRise #-Calculate rootzone runoff tempvar = self.rootzone.RootRunoff(self, pcr, RainFrac, Rain) #-Rootzone runoff RootRunoff = tempvar[0] #-Infiltration Infil = tempvar[1] #-Report infiltration self.reporting.reporting(self, pcr, 'Infil', Infil) #-Updated rootwater content self.RootWater = pcr.ifthenelse(RainFrac > 0, self.RootWater + Infil, self.RootWater) #-Actual evapotranspiration if self.PlantWaterStressFLAG == 1: etreddry = self.ET.ks(self, pcr, ETpot) else: etreddry = pcr.max( pcr.min((self.RootWater - self.RootDry) / (self.RootWilt - self.RootDry), 1), 0) self.reporting.reporting(self, pcr, 'PlantStress', 1 - etreddry) ETact = self.ET.ETact(pcr, ETpot, self.RootWater, self.RootSat, etreddry, RainFrac) #-Report the actual evapotranspiration self.reporting.reporting( self, pcr, 'TotETact', ETact * (1 - self.openWaterFrac) + self.ETOpenWater * self.openWaterFrac) #-Actual evapotranspiration, corrected for rain fraction ActETact = ETact * RainFrac #-Report the actual evapotranspiration, corrected for rain fraction self.reporting.reporting(self, pcr, 'TotETactF', ActETact) if self.mm_rep_FLAG == 1 and self.ETa_mm_FLAG == 1 and ( self.RoutFLAG == 1 or self.ResFLAG == 1 or self.LakeFLAG == 1): self.ETaSubBasinTSS.sample( pcr.catchmenttotal(ActETact, self.FlowDir) / pcr.catchmenttotal(1, self.FlowDir)) #-Update rootwater content self.RootWater = pcr.max(self.RootWater - ETact, 0) #-Calculate drainage temp_RootDrain = self.rootzone.RootDrainage( pcr, self.RootWater, self.RootDrain, self.RootField, self.RootSat, self.RootDrainVel, self.RootTT) #-Calculate percolation temp_rootperc = self.rootzone.RootPercolation(pcr, self.RootWater, self.SubWater, self.RootField, self.RootTT, self.SubSat) #-Total sum of water able to leave the soil RootOut = temp_RootDrain + temp_rootperc #-Calculate new values for drainage and percolation (to be used when RootOut > RootExcess) newdrain, newperc = self.rootzone.CalcFrac(pcr, self.RootWater, self.RootField, temp_RootDrain, temp_rootperc) #-Determine whether the new values need to be used rootexcess = pcr.max(self.RootWater - self.RootField, 0) self.RootDrain = pcr.ifthenelse(RootOut > rootexcess, newdrain, temp_RootDrain) rootperc = pcr.ifthenelse(RootOut > rootexcess, newperc, temp_rootperc) #-Update the RootWater content # Roottemp = self.RootWater self.RootWater = self.RootWater - (self.RootDrain + rootperc) #-Report rootzone percolation, corrected for fraction self.reporting.reporting(self, pcr, 'TotRootPF', rootperc * (1 - self.GlacFrac)) #-Report rootwater content self.reporting.reporting(self, pcr, 'StorRootW', self.RootWater * (1 - self.openWaterFrac)) #-Sub soil calculations self.SubWater = self.SubWater + rootperc if self.GroundFLAG == 0: if self.SeepStatFLAG == 0: try: self.SeePage = pcr.readmap( pcrm.generateNameT(self.Seepmaps, self.counter)) self.SeepOld = self.SeePage except: self.SeePage = self.SeepOld #-Report seepage self.reporting.reporting(self, pcr, 'TotSeepF', pcr.scalar(self.SeePage)) self.SubWater = pcr.min(pcr.max(self.SubWater - self.SeePage, 0), self.SubSat) if self.mm_rep_FLAG == 1 and self.Seep_mm_FLAG == 1 and ( self.RoutFLAG == 1 or self.ResFLAG == 1 or self.LakeFLAG == 1): self.SeepSubBasinTSS.sample( pcr.catchmenttotal(self.SeePage, self.FlowDir) / pcr.catchmenttotal(1, self.FlowDir)) #-Capillary rise self.CapRise = self.subzone.CapilRise(pcr, self.SubField, self.SubWater, self.CapRiseMax, self.RootWater, self.RootSat, self.RootField) #-Report capillary rise, corrected for fraction self.reporting.reporting(self, pcr, 'TotCapRF', self.CapRise * (1 - self.GlacFrac)) #-Update sub soil water content self.SubWater = self.SubWater - self.CapRise if self.GroundFLAG == 1: # sub percolation will be calculated instead of subdrainage subperc = self.subzone.SubPercolation(pcr, self.SubWater, self.SubField, self.SubTT, self.Gw, self.GwSat) ActSubPerc = subperc * (1 - self.GlacFrac) #-Report the subzone percolation, corrected for the fraction self.reporting.reporting(self, pcr, 'TotSubPF', ActSubPerc) #-Update sub soil water content self.SubWater = self.SubWater - subperc else: # sub drainage will be calculated instead of sub percolation self.SubDrain = self.subzone.SubDrainage(pcr, self.SubWater, self.SubField, self.SubSat, self.SubDrainVel, self.SubDrain, self.SubTT) #-Report drainage from subzone self.reporting.reporting(self, pcr, 'TotSubDF', self.SubDrain) #-Update sub soil water content self.SubWater = self.SubWater - self.SubDrain #-Report rootwater content self.reporting.reporting(self, pcr, 'StorSubW', self.SubWater * (1 - self.openWaterFrac)) #-Changes in soil water storage OldSoilWater = self.SoilWater self.SoilWater = (self.RootWater + self.SubWater) * (1 - self.GlacFrac) #-Rootzone runoff self.RootRR = RootRunoff * RainFrac * (1 - self.openWaterFrac) #-Report rootzone runoff, corrected for fraction self.reporting.reporting(self, pcr, 'TotRootRF', self.RootRR) #-Rootzone drainage self.RootDR = self.RootDrain * (1 - self.GlacFrac) * ( 1 - self.openWaterFrac) #-Report rootzone drainage, corrected for fraction self.reporting.reporting(self, pcr, 'TotRootDF', self.RootDR) #-Rain runoff self.RainR = self.RootRR + self.RootDR #-Report rain runoff self.reporting.reporting(self, pcr, 'TotRainRF', self.RainR) #-Groundwater calculations if self.GroundFLAG == 1: #-read dynamic processes groundwater self.groundwater.dynamic(self, pcr, ActSubPerc, GlacPerc) else: #-Use drainage from subsoil as baseflow self.BaseR = self.SubDrain #-Groundwater level as scaled between min and max measured gwl SoilAct = self.RootWater + self.SubWater SoilRel = (SoilAct - self.SoilMin) / ( self.SoilMax - self.SoilMin ) # scale between 0 (dry) and 1 (wet) GWL = self.GWL_base - (SoilRel - 0.5) * self.GWL_base #-Report groundwater self.reporting.reporting(self, pcr, 'GWL', GWL) #-Report Total runoff TotR = self.BaseR + self.RainR + self.SnowR + self.GlacR self.reporting.reporting(self, pcr, 'TotRF', TotR) #-Routing for lake and/or reservoir modules if self.LakeFLAG == 1 or self.ResFLAG == 1: #-read dynamic processes advanced routing Q = self.advanced_routing.dynamic(self, pcr, pcrm, config, TotR, self.ETOpenWater, PrecipTot) #-Normal routing module elif self.RoutFLAG == 1: self.routing.dynamic(self, pcr, TotR) if self.GlacFLAG: #-read dynamic reporting processes glacier self.glacier.dynamic_reporting(self, pcr, pd, np) #-Water balance if self.GlacFLAG and self.GlacRetreat == 1: GlacTable_MODid = self.GlacTable.loc[:, ['FRAC_GLAC', 'ICE_DEPTH']] GlacTable_MODid['ICE_DEPTH'] = GlacTable_MODid[ 'ICE_DEPTH'] * GlacTable_MODid['FRAC_GLAC'] GlacTable_MODid = GlacTable_MODid.groupby( GlacTable_MODid.index).sum() GlacTable_MODid.fillna(0., inplace=True) #-Report pcraster map of glacier depth iceDepth = pcr.numpy.zeros(self.ModelID_1d.shape) iceDepth[self.GlacierKeys] = GlacTable_MODid['ICE_DEPTH'] iceDepth = iceDepth.reshape(self.ModelID.shape) iceDepth = pcr.numpy2pcr(pcr.Scalar, iceDepth, self.MV) iceDepth = pcr.ifthen( self.clone, iceDepth) #-only use values where clone is True iceDepth = iceDepth * 1000 # in mm #-change in storage dS = ((self.RootWater - self.oldRootWater) + (self.SubWater - self.oldSubWater)) * (1-self.GlacFrac) + (self.Gw - self.oldGw) + \ (self.TotalSnowStore-OldTotalSnowStore) + (iceDepth - self.oldIceDepth) #-set old state variables for glacier self.oldIceDepth = iceDepth iceDepth = None del iceDepth GlacTable_MODid = None del GlacTable_MODid elif self.GroundFLAG: #-change in storage dS = ((self.RootWater - self.oldRootWater) + (self.SubWater - self.oldSubWater)) * (1-self.GlacFrac) + (self.Gw - self.oldGw) + \ (self.TotalSnowStore-OldTotalSnowStore) # set old state variables for groundwater self.oldGw = self.Gw else: #-change in storage dS = ((self.RootWater - self.oldRootWater) + (self.SubWater - self.oldSubWater)) * (1 - self.GlacFrac) + ( self.TotalSnowStore - OldTotalSnowStore) #-water balance per time step if self.GroundFLAG: waterbalance = Precip - ActETact - self.BaseR - self.RainR - self.SnowR - self.GlacR - dS else: waterbalance = Precip - ActETact - self.BaseR - self.RainR - self.SnowR - dS - self.SeePage self.reporting.reporting(self, pcr, 'wbal', waterbalance) #-total water balance self.waterbalanceTot = self.waterbalanceTot + waterbalance #-report water balance and accumulated water balance if self.wbal_TSS_FLAG and (self.RoutFLAG == 1 or self.ResFLAG == 1 or self.LakeFLAG == 1): self.wbalTSS.sample( pcr.catchmenttotal(waterbalance, self.FlowDir) / pcr.catchmenttotal(1., self.FlowDir)) self.wbalTotTSS.sample( pcr.catchmenttotal(self.waterbalanceTot, self.FlowDir) / pcr.catchmenttotal(1., self.FlowDir)) # set old state variables self.oldRootWater = self.RootWater self.oldSubWater = self.SubWater waterbalance = None del waterbalance dS = None del dS #-End of water balance calculations #-Sediment yield if self.SedFLAG == 1: #-determine runoff in mm per day if self.RoutFLAG == 1 or self.ResFLAG == 1 or self.LakeFLAG == 1: Runoff = (Q * 3600 * 24) / pcr.cellarea() * 1000 else: Runoff = TotR #-MUSLE if self.SedModel == 1: #-read dynamic processes musle self.musle.dynamic(self, pcr, Runoff) #-sediment transport if self.SedTransFLAG == 1: #-read dynamic sediment transport processes musle self.sediment_transport.dynamic_musle(self, pcr) #-Modified Morgan-Morgan-Finney model if self.SedModel == 2: #-determine soil erosion in transport (G) G = self.mmf.dynamic(self, pcr, Precip, Runoff) #-sediment transport if self.SedTransFLAG == 1: #-read dynamic sediment transport processes mmf self.sediment_transport.dynamic_mmf( self, pcr, Runoff, np, G) #-update current date self.curdate = self.curdate + self.datetime.timedelta(days=1)
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 __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])