def subcatch_order_a(ldd, oorder): """ Determines subcatchments using the catchment order This version uses the last cell BELOW order to derive the catchments. In general you want the _b version Input: - ldd - order - order to use Output: - map with catchment for the given streamorder """ outl = find_outlet(ldd) large = pcr.subcatchment(ldd, pcr.boolean(outl)) stt = pcr.streamorder(ldd) sttd = pcr.downstream(ldd, stt) pts = pcr.ifthen((pcr.scalar(sttd) - pcr.scalar(stt)) > 0.0, sttd) dif = pcr.upstream( ldd, pcr.cover( pcr.ifthen( large, pcr.uniqueid(pcr.boolean(pcr.ifthen(stt == pcr.ordinal(oorder), pts))), ), 0, ), ) dif = pcr.cover(pcr.scalar(outl), dif) # Add catchment outlet dif = pcr.ordinal(pcr.uniqueid(pcr.boolean(dif))) sc = pcr.subcatchment(ldd, dif) return sc, dif, stt
def subcatch_stream(ldd, stream, threshold): """ Derive catchments based upon strahler threshold Input: ldd -- pcraster object direction, local drain directions stream -- pcraster object direction, streamorder threshold -- integer, strahler threshold, subcatchments ge threshold are derived output: stream_ge -- pcraster object, streams of strahler order ge threshold subcatch -- pcraster object, subcatchments of strahler order ge threshold """ # derive stream order # stream = pcr.streamorder(ldd) stream_ge = pcr.ifthen(stream >= threshold, stream) stream_up_sum = pcr.ordinal(pcr.upstream(ldd, pcr.cover(pcr.scalar(stream_ge), 0))) # detect any transfer of strahler order, to a higher strahler order. transition_strahler = pcr.ifthenelse(pcr.downstream(ldd, stream_ge) != stream_ge, pcr.boolean(1), pcr.ifthenelse(pcr.nominal(ldd) == 5, pcr.boolean(1), pcr.ifthenelse(pcr.downstream(ldd, pcr.scalar(stream_up_sum)) > pcr.scalar(stream_ge), pcr.boolean(1), pcr.boolean(0)))) # make unique ids (write to file) transition_unique = pcr.ordinal(pcr.uniqueid(transition_strahler)) # derive upstream catchment areas (write to file) subcatch = pcr.nominal(pcr.subcatchment(ldd, transition_unique)) return stream_ge, subcatch
def subcatch_stream(ldd, stream, threshold): """ Derive catchments based upon strahler threshold Input: ldd -- pcraster object direction, local drain directions stream -- pcraster object direction, streamorder threshold -- integer, strahler threshold, subcatchments ge threshold are derived output: stream_ge -- pcraster object, streams of strahler order ge threshold subcatch -- pcraster object, subcatchments of strahler order ge threshold """ # derive stream order # stream = pcr.streamorder(ldd) stream_ge = pcr.ifthen(stream >= threshold, stream) stream_up_sum = pcr.ordinal( pcr.upstream(ldd, pcr.cover(pcr.scalar(stream_ge), 0))) # detect any transfer of strahler order, to a higher strahler order. transition_strahler = pcr.ifthenelse( pcr.downstream(ldd, stream_ge) != stream_ge, pcr.boolean(1), pcr.ifthenelse( pcr.nominal(ldd) == 5, pcr.boolean(1), pcr.ifthenelse( pcr.downstream(ldd, pcr.scalar(stream_up_sum)) > pcr.scalar(stream_ge), pcr.boolean(1), pcr.boolean(0)))) # make unique ids (write to file) transition_unique = pcr.ordinal(pcr.uniqueid(transition_strahler)) # derive upstream catchment areas (write to file) subcatch = pcr.nominal(pcr.subcatchment(ldd, transition_unique)) return stream_ge, subcatch
def initialSecond(self): """ initial part of the second channel routing module """ settings = LisSettings.instance() option = settings.options binding = settings.binding self.var.ChannelAlpha2 = None # default value, if split-routing is not active and only water is routed only in the main channel # ************************************************************ # ***** CHANNEL INITIAL SPLIT UP IN SECOND CHANNEL************ # ************************************************************ if option['SplitRouting']: ChanMan2 = (self.var.ChanMan / self.var.CalChanMan) * loadmap('CalChanMan2') AlpTermChan2 = (ChanMan2 / (np.sqrt(self.var.ChanGrad))) ** self.var.Beta self.var.ChannelAlpha2 = (AlpTermChan2 * (self.var.ChanWettedPerimeterAlpha ** self.var.AlpPow)).astype(float) self.var.InvChannelAlpha2 = 1 / self.var.ChannelAlpha2 # calculating second Alpha for second (virtual) channel if not(option['InitLisflood']): self.var.QLimit = loadmap('AvgDis') * loadmap('QSplitMult') # Over bankful discharge starts at QLimit # lower discharge limit for second line of routing # set to mutiple of average discharge (map from prerun) # QSplitMult =2 is around 90 to 95% of Q self.var.M3Limit = self.var.ChannelAlpha * self.var.ChanLength * (self.var.QLimit ** self.var.Beta) # Water volume in bankful when over bankful discharge starts ############################################### # CM mod # TEMPORARY WORKAROUND FOR EFAS XDOM!!!!!!!!!! # This must be removed # QLimit should NOT be dependent on the NoRoutSteps (number of routing steps) # self.var.QLimit = self.var.QLimit / self.var.NoRoutSteps #original # TEMPORARY WORKAROUND FOR EFAS XDOM!!!!!!!!!! # This must be removed self.var.QLimit = self.var.QLimit / 24.0 ############################################### self.var.Chan2M3Start = self.var.ChannelAlpha2 * self.var.ChanLength * (self.var.QLimit ** self.var.Beta) # virtual amount of water in the channel through second line self.var.Chan2QStart = self.var.QLimit - compressArray(upstream(self.var.LddKinematic, decompress(self.var.QLimit))) # because kinematic routing with a low amount of discharge leads to long travel time: # Starting Q for second line is set to a higher value self.var.Chan2M3Kin = self.var.CrossSection2Area * self.var.ChanLength + self.var.Chan2M3Start self.var.ChanM3Kin = self.var.ChanM3 - self.var.Chan2M3Kin + self.var.Chan2M3Start self.var.Chan2QKin = (self.var.Chan2M3Kin*self.var.InvChanLength*self.var.InvChannelAlpha2)**(self.var.InvBeta) self.var.ChanQKin = (self.var.ChanM3Kin*self.var.InvChanLength*self.var.InvChannelAlpha)**(self.var.InvBeta) # Initialise parallel kinematic wave router: main channel-only routing if self.var.ChannelAlpha2 is None; else split-routing(main channel + floodplains) maskinfo = MaskInfo.instance() self.river_router = kinematicWave(compressArray(self.var.LddKinematic), ~maskinfo.info.mask, self.var.ChannelAlpha, self.var.Beta, self.var.ChanLength, self.var.DtRouting, int(binding["numCPUs_parallelKinematicWave"]), alpha_floodplains=self.var.ChannelAlpha2)
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
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,\ pcr.ifthen((waterBodiesType == 2) & (waterBodies.location != 0),\ waterBodies.returnMapValue(pcr.spatial(pcr.scalar(0.)),waterBodies.capacity*waterBodies.maxLimit)),\ pcr.ifthen((waterBodiesType == 1) & (waterBodies.location != 0),\ pcr.areatotal(fractionWater*cellArea,waterBodies.distribution)*(averageQ/(waterBodies.cLake*channelWidth))**(2./3.))) actualStorage= pcr.ifthen(waterBodies.distribution != 0,pcr.cover(actualStorage,0)) #-storage over rivers actualStorage= pcr.cover(actualStorage,channelLength* (channelManN*averageQ*channelWidth**(2./3.)*channelGradient**-0.5 )**0.6) pcr.report(actualStorage,targetFileName) #-initial conditions created, execute the followingl until exitCode becomes true # set run counter and completion identifier runCnt= 0
def subcatch_stream( ldd, threshold, min_strahler=-999, max_strahler=999, assign_edge=False, assign_existing=False, up_area=None, ): """ (From Deltares Hydrotools) Derive catchments based upon strahler threshold Input: ldd -- pcraster object direction, local drain directions threshold -- integer, strahler threshold, subcatchments ge threshold are derived min_strahler -- integer, minimum strahler threshold of river catchments to return max_strahler -- integer, maximum strahler threshold of river catchments to return assign_unique=False -- if set to True, unassigned connected areas at the edges of the domain are assigned a unique id as well. If set to False, edges are not assigned assign_existing=False == if set to True, unassigned edges are assigned to existing basins with an upstream weighting. If set to False, edges are assigned to unique IDs, or not assigned output: stream_ge -- pcraster object, streams of strahler order ge threshold subcatch -- pcraster object, subcatchments of strahler order ge threshold """ # derive stream order stream = pcr.streamorder(ldd) stream_ge = pcr.ifthen(stream >= threshold, stream) stream_up_sum = pcr.ordinal(pcr.upstream(ldd, pcr.cover(pcr.scalar(stream_ge), 0))) # detect any transfer of strahler order, to a higher strahler order. transition_strahler = pcr.ifthenelse( pcr.downstream(ldd, stream_ge) != stream_ge, pcr.boolean(1), pcr.ifthenelse( pcr.nominal(ldd) == 5, pcr.boolean(1), pcr.ifthenelse( pcr.downstream(ldd, pcr.scalar(stream_up_sum)) > pcr.scalar(stream_ge), pcr.boolean(1), pcr.boolean(0), ), ), ) # make unique ids (write to file) transition_unique = pcr.ordinal(pcr.uniqueid(transition_strahler)) # derive upstream catchment areas (write to file) subcatch = pcr.nominal(pcr.subcatchment(ldd, transition_unique)) if assign_edge: # fill unclassified areas (in pcraster equal to zero) with a unique id, above the maximum id assigned so far unique_edge = pcr.clump(pcr.ifthen(subcatch == 0, pcr.ordinal(0))) subcatch = pcr.ifthenelse( subcatch == 0, pcr.nominal(pcr.mapmaximum(pcr.scalar(subcatch)) + pcr.scalar(unique_edge)), pcr.nominal(subcatch), ) elif assign_existing: # unaccounted areas are added to largest nearest draining basin if up_area is None: up_area = pcr.ifthen( pcr.boolean(pcr.cover(stream_ge, 0)), pcr.accuflux(ldd, 1) ) riverid = pcr.ifthen(pcr.boolean(pcr.cover(stream_ge, 0)), subcatch) friction = 1.0 / pcr.scalar( pcr.spreadzone(pcr.cover(pcr.ordinal(up_area), 0), 0, 0) ) # *(pcr.scalar(ldd)*0+1) delta = pcr.ifthen( pcr.scalar(ldd) >= 0, pcr.ifthen( pcr.cover(subcatch, 0) == 0, pcr.spreadzone(pcr.cover(riverid, 0), 0, friction), ), ) subcatch = pcr.ifthenelse(pcr.boolean(pcr.cover(subcatch, 0)), subcatch, delta) # finally, only keep basins with minimum and maximum river order flowing through them strahler_subcatch = pcr.areamaximum(stream, subcatch) subcatch = pcr.ifthen( pcr.ordinal(strahler_subcatch) >= min_strahler, pcr.ifthen(pcr.ordinal(strahler_subcatch) <= max_strahler, subcatch), ) return stream_ge, pcr.ordinal(subcatch)
if not keepall: catchments = pcr.nominal(pcr.ifthen(pcr.mapmaximum(pcr.areatotal(pcr.scalar(catchments)*0+1,pcr.nominal(catchments))) == pcr.areatotal(pcr.scalar(catchments)*0+1,pcr.nominal(catchments)),catchments)) pcr.report(ldd,ldd_map) pcr.report(streamorder,streamorder_map) pcr.report(river,river_map) pcr.report(catchments,catchments_map) if not EPSG == None: call(('gdal_translate','-of','GTiff','-stats','-a_srs',EPSG,'-ot','Float32',catchments_map,catchments_tif)) else: call(('gdal_translate','-of','GTiff','-stats','-ot','Float32',catchments_map,catchments_tif)) wt.Raster2Pol(catchments_tif,catchshp,srs) riversid_map = workdir + 'riverid.map' drain_map = workdir + 'drain.map' ldd_mask = pcr.ifthen(river, ldd) upstream = pcr.upstream(ldd_mask, pcr.scalar(river)) downstream = pcr.downstream(ldd_mask, upstream) #pcr.report(downstream,'downstream.map') confluences = pcr.boolean(pcr.ifthen(downstream >= 2, pcr.boolean(1))) #pcr.report(confluences,'confluences.map') boundaries = pcr.boolean(pcr.ifthen(pcr.scalar(ldd_mask) == 5, pcr.boolean(1))) catch_points = pcr.nominal(pcr.uniqueid(pcr.cover(confluences,boundaries))) catchmentsid = pcr.nominal(pcr.subcatchment(ldd, catch_points)) drain = pcr.accuflux(ldd_mask, 1) riversid = pcr.ifthen(river, catchmentsid) if not keepall: riversid = pcr.nominal(pcr.ifthen(pcr.mapmaximum(pcr.areatotal(pcr.scalar(catchments)*0+1,pcr.nominal(catchments))) == pcr.areatotal(pcr.scalar(catchments)*0+1,pcr.nominal(catchments)),riversid)) pcr.report(riversid,riversid_map) pcr.report(drain,drain_map)
def initial(self): """ initial part of the water abstraction module """ # self.testmap=windowaverage(self.var.Elevation,5) # self.report(self.testmap,"test.map") # ************************************************************ # ***** WATER USE # ************************************************************ settings = LisSettings.instance() option = settings.options binding = settings.binding maskinfo = MaskInfo.instance() if option['wateruse']: self.var.WUsePercRemain = loadmap('WUsePercRemain') self.var.NoWaterUseSteps = int(loadmap('maxNoWateruse')) self.var.GroundwaterBodies = loadmap('GroundwaterBodies') self.var.FractionGroundwaterUsed = np.minimum( np.maximum(loadmap('FractionGroundwaterUsed'), maskinfo.in_zero()), 1.0) self.var.FractionNonConventionalWaterUsed = loadmap( 'FractionNonConventionalWaterUsed') self.var.FractionLakeReservoirWaterUsed = loadmap( 'FractionLakeReservoirWaterUsed') self.var.EFlowThreshold = loadmap('EFlowThreshold') # EFlowThreshold is map with m3/s discharge, e.g. the 10th percentile discharge of the baseline run self.var.WUseRegionC = loadmap('WUseRegion').astype(int) self.var.IrrigationMult = loadmap('IrrigationMult') # ************************************************************ # ***** water use constant maps ****************************** # ************************************************************ self.var.IndustryConsumptiveUseFraction = loadmap( 'IndustryConsumptiveUseFraction') # fraction (0-1) self.var.WaterReUseFraction = loadmap('WaterReUseFraction') # fraction of water re-used (0-1) self.var.EnergyConsumptiveUseFraction = loadmap( 'EnergyConsumptiveUseFraction') # fraction (0-1), value depends on cooling technology of power plants self.var.LivestockConsumptiveUseFraction = loadmap( 'LivestockConsumptiveUseFraction') # fraction (0-1) self.var.LeakageFraction = np.minimum( np.maximum( loadmap('LeakageFraction') * (1 - loadmap('LeakageReductionFraction')), maskinfo.in_zero()), 1.0) self.var.DomesticLeakageConstant = np.minimum( np.maximum(1 / (1 - self.var.LeakageFraction), maskinfo.in_zero()), 1.0) # Domestic Water Abstraction becomes larger in case of leakage # LeakageFraction is LeakageFraction (0-1) multiplied by reduction scenario (10% reduction is 0.1 in map) # 0.65 leakage and 0.1 reduction leads to 0.585 effective leakage, resulting in 2.41 times more water abstraction self.var.DomesticWaterSavingConstant = np.minimum( np.maximum(1 - loadmap('WaterSavingFraction'), maskinfo.in_zero()), 1.0) # Domestic water saving if in place, changes this value from 1 to a value between 0 and 1, and will reduce demand and abstraction # so value = 0.9 if WaterSavingFraction equals 0.1 (10%) self.var.DomesticConsumptiveUseFraction = loadmap( 'DomesticConsumptiveUseFraction') # fraction (0-1), typically rather low ~ 0.10 self.var.LeakageWaterLossFraction = loadmap('LeakageWaterLoss') # fraction (0-1), 0=no leakage # Initialize water demand. Read from a static map either value or pcraster map or netcdf (single or stack). # If reading from NetCDF stack, get time step corresponding to model step. # Added management for sub-daily modelling time steps # Added possibility to use one single average year to be repeated during the simulation if option['useWaterDemandAveYear']: # CM: using one water demand average year throughout the model simulation self.var.DomesticDemandMM = loadmap( 'DomesticDemandMaps', timestampflag='closest', averageyearflag=True) * self.var.DtDay self.var.IndustrialDemandMM = loadmap( 'IndustrialDemandMaps', timestampflag='closest', averageyearflag=True) * self.var.DtDay self.var.LivestockDemandMM = loadmap( 'LivestockDemandMaps', timestampflag='closest', averageyearflag=True) * self.var.DtDay self.var.EnergyDemandMM = loadmap( 'EnergyDemandMaps', timestampflag='closest', averageyearflag=True) * self.var.DtDay else: # CM: using information on water demand from NetCDF files self.var.DomesticDemandMM = loadmap( 'DomesticDemandMaps', timestampflag='closest') * self.var.DtDay self.var.IndustrialDemandMM = loadmap( 'IndustrialDemandMaps', timestampflag='closest') * self.var.DtDay self.var.LivestockDemandMM = loadmap( 'LivestockDemandMaps', timestampflag='closest') * self.var.DtDay self.var.EnergyDemandMM = loadmap( 'EnergyDemandMaps', timestampflag='closest') * self.var.DtDay # Check consistency with the reference calendar that is read from the precipitation forcing file (global_modules.zusatz.optionBinding) if option['TransientWaterDemandChange'] and option[ 'readNetcdfStack']: for k in ('DomesticDemandMaps', 'IndustrialDemandMaps', 'LivestockDemandMaps', 'EnergyDemandMaps'): with Dataset(binding[k] + '.nc') as nc: cal_type = get_calendar_type(nc) if cal_type != binding['calendar_type']: warnings.warn( calendar_inconsistency_warning( binding[k], cal_type, binding['calendar_type'])) if option['groundwaterSmooth']: self.var.GroundwaterBodiesPcr = decompress( self.var.GroundwaterBodies) self.var.groundwaterCatch = boolean( decompress((self.var.GroundwaterBodies * self.var.Catchments).astype(int))) # nominal(scalar(GroundwaterBodies)*scalar(self.var.Catchments)); # smoothing for groundwater to correct error by using windowtotal, based on groundwater bodies and catchments self.var.LZSmoothRange = loadmap('LZSmoothRange') if option['wateruseRegion']: WUseRegion = nominal(loadmap('WUseRegion', pcr=True)) pitWuse1 = ifthen(self.var.AtLastPoint != 0, boolean(1)) pitWuse1b = ifthen(defined(pitWuse1), WUseRegion) # use every existing pit in the Ldd and number them by the water regions # coastal water regions can have more than one pit per water region pitWuseMax = areamaximum(self.var.UpArea, WUseRegion) pitWuse2 = ifthen(pitWuseMax == self.var.UpArea, WUseRegion) # search outlets in the inland water regions by using the maximum upstream area as criterium pitWuse3 = downstream(self.var.LddStructuresKinematic, WUseRegion) pitWuse3b = ifthen(pitWuse3 != WUseRegion, WUseRegion) # search point where ldd leaves a water region pitWuse = cover(pitWuse1b, pitWuse2, pitWuse3b, nominal(0)) # join all sources of pits LddWaterRegion = lddrepair( ifthenelse(pitWuse == 0, self.var.LddStructuresKinematic, 5)) # create a Ldd with pits at every water region outlet # this results in a interrupted ldd, so water cannot be transfered to the next water region lddC = compressArray(LddWaterRegion) inAr = decompress( np.arange(maskinfo.info.mapC[0], dtype="int32")) # giving a number to each non missing pixel as id self.var.downWRegion = (compressArray( downstream(LddWaterRegion, inAr))).astype(np.int32) # each upstream pixel gets the id of the downstream pixel self.var.downWRegion[lddC == 5] = maskinfo.info.mapC[0] # all pits gets a high number # ************************************************************ # ***** OUTFLOW AND INFLOW POINTS FOR WATER REGIONS ********** # ************************************************************ self.var.WaterRegionOutflowPoints = ifthen( pitWuse != 0, boolean(1)) # outflowpoints to calculate upstream inflow for balances and Water Exploitation Index # both inland outflowpoints to downstream subbasin, and coastal outlets WaterRegionInflow1 = boolean( upstream( self.var.LddStructuresKinematic, cover(scalar(self.var.WaterRegionOutflowPoints), 0))) self.var.WaterRegionInflowPoints = ifthen( WaterRegionInflow1, boolean(1)) # inflowpoints to calculate upstream inflow for balances and Water Exploitation Index else: self.var.downWRegion = self.var.downstruct.copy() self.var.downWRegion = self.var.downWRegion.astype(np.int32) # ************************************************************ # ***** Initialising cumulative output variables ************* # ************************************************************ # These are all needed to compute the cumulative mass balance error self.var.wateruseCum = maskinfo.in_zero() # water use cumulated amount self.var.WUseAddM3Dt = maskinfo.in_zero() self.var.WUseAddM3 = maskinfo.in_zero() self.var.IrriLossCUM = maskinfo.in_zero() # Cumulative irrigation loss [mm] # Cumulative abstraction from surface water [mm] self.var.TotalAbstractionFromSurfaceWaterM3 = maskinfo.in_zero() self.var.TotalAbstractionFromGroundwaterM3 = maskinfo.in_zero() self.var.TotalIrrigationAbstractionM3 = maskinfo.in_zero() self.var.TotalPaddyRiceIrrigationAbstractionM3 = maskinfo.in_zero() self.var.TotalLivestockAbstractionM3 = maskinfo.in_zero() self.var.IrrigationType = loadmap('IrrigationType') self.var.IrrigationEfficiency = loadmap('IrrigationEfficiency') self.var.ConveyanceEfficiency = loadmap('ConveyanceEfficiency') self.var.GroundwaterRegionPixels = np.take( np.bincount(self.var.WUseRegionC, weights=self.var.GroundwaterBodies), self.var.WUseRegionC) self.var.AllRegionPixels = np.take( np.bincount(self.var.WUseRegionC, weights=self.var.GroundwaterBodies * 0.0 + 1.0), self.var.WUseRegionC) self.var.RatioGroundWaterUse = self.var.AllRegionPixels / ( self.var.GroundwaterRegionPixels + 0.01) self.var.FractionGroundwaterUsed = np.minimum( self.var.FractionGroundwaterUsed * self.var.RatioGroundWaterUse, 1 - self.var.FractionNonConventionalWaterUsed) # FractionGroundwaterUsed is a percentage given at national scale # since the water needs to come from the GroundwaterBodies pixels, # the fraction needs correction for the non-Groundwaterbodies; this is done here self.var.EFlowIndicator = maskinfo.in_zero() self.var.ReservoirAbstractionM3 = maskinfo.in_zero() self.var.PotentialSurfaceWaterAvailabilityForIrrigationM3 = maskinfo.in_zero( ) self.var.LakeAbstractionM3 = maskinfo.in_zero() self.var.FractionAbstractedFromChannels = maskinfo.in_zero() self.var.AreatotalIrrigationUseM3 = maskinfo.in_zero() self.var.totalAddM3 = maskinfo.in_zero() self.var.TotalDemandM3 = maskinfo.in_zero()
water_body_outlet = pcr.ifthen(\ pcr.areaorder(pcr.scalar( water_body_outlet), \ water_body_outlet) == 1., water_body_outlet) water_body_outlet = pcr.ifthen(\ pcr.scalar(water_body_outlet) > 0., water_body_outlet) # calculate overbank volume from reservoirs (and lakes) lake_reservoir_volume = pcr.areatotal(extreme_value_map, water_body_id) lake_reservoir_overbank_volume = pcr.cover( pcr.max(0.0, lake_reservoir_volume - reservoir_capacity), 0.0) #~ pcr.aguila(lake_reservoir_overbank_volume) # # transfer 75% of overbank volume to the downstream (several cells downstream) transfer_to_downstream = pcr.cover(\ pcr.ifthen(pcr.scalar(water_body_outlet) > 0., lake_reservoir_overbank_volume * 0.50), 0.0) transfer_to_downstream = pcr.upstream(ldd_map_low_resolution, transfer_to_downstream) transfer_to_downstream = pcr.upstream(ldd_map_low_resolution, transfer_to_downstream) transfer_to_downstream = pcr.upstream(ldd_map_low_resolution, transfer_to_downstream) extreme_value_map = transfer_to_downstream + \ pcr.ifthenelse(pcr.cover(pcr.scalar(water_body_id), 0.0) > 0.00, 0.00, extreme_value_map) # # the remaining overbank volume (50%) will be distributed to the shores lake_reservoir_overbank_volume = lake_reservoir_overbank_volume * 0.50 land_area = cell_area * pcr.max(0.0, 1.0 - fracwat) land_area_average = pcr.areaaverage(land_area, water_body_id) land_area_weight = pcr.ifthenelse(land_area < land_area_average, 0.0, land_area_average) distributed_lake_reservoir_overbank_volume = pcr.cover(\ lake_reservoir_overbank_volume * land_area_weight / pcr.max(0.00, pcr.areatotal(land_area_weight, water_body_id)), 0.0)
def subcatch_stream(ldd, threshold, stream=None, min_strahler=-999, max_strahler=999, assign_edge=False, assign_existing=False, up_area=None, basin=None): """ Derive catchments based upon strahler threshold Input: ldd -- pcraster object direction, local drain directions threshold -- integer, strahler threshold, subcatchments ge threshold are derived stream=None -- pcraster object ordinal, stream order map (made with pcr.streamorder), if provided, stream order map is not generated on the fly but used from this map. Useful when a subdomain within a catchment is provided, which would cause edge effects in the stream order map min_strahler=-999 -- integer, minimum strahler threshold of river catchments to return max_strahler=999 -- integer, maximum strahler threshold of river catchments to return assign_unique=False -- if set to True, unassigned connected areas at the edges of the domain are assigned a unique id as well. If set to False, edges are not assigned assign_existing=False == if set to True, unassigned edges are assigned to existing basins with an upstream weighting. If set to False, edges are assigned to unique IDs, or not assigned output: stream_ge -- pcraster object, streams of strahler order ge threshold subcatch -- pcraster object, subcatchments of strahler order ge threshold """ # derive stream order if stream is None: stream = pcr.streamorder(ldd) stream_ge = pcr.ifthen(stream >= threshold, stream) stream_up_sum = pcr.ordinal(pcr.upstream(ldd, pcr.cover(pcr.scalar(stream_ge), 0))) # detect any transfer of strahler order, to a higher strahler order. transition_strahler = pcr.ifthenelse(pcr.downstream(ldd, stream_ge) != stream_ge, pcr.boolean(1), pcr.ifthenelse(pcr.nominal(ldd) == 5, pcr.boolean(1), pcr.ifthenelse(pcr.downstream(ldd, pcr.scalar(stream_up_sum)) > pcr.scalar(stream_ge), pcr.boolean(1), pcr.boolean(0)))) # make unique ids (write to file) transition_unique = pcr.ordinal(pcr.uniqueid(transition_strahler)) # derive upstream catchment areas (write to file) subcatch = pcr.nominal(pcr.subcatchment(ldd, transition_unique)) # mask out areas outside basin if basin is not None: subcatch = pcr.ifthen(basin, subcatch) if assign_edge: # fill unclassified areas (in pcraster equal to zero) with a unique id, above the maximum id assigned so far unique_edge = pcr.clump(pcr.ifthen(subcatch==0, pcr.ordinal(0))) subcatch = pcr.ifthenelse(subcatch==0, pcr.nominal(pcr.mapmaximum(pcr.scalar(subcatch)) + pcr.scalar(unique_edge)), pcr.nominal(subcatch)) elif assign_existing: # unaccounted areas are added to largest nearest draining basin if up_area is None: up_area = pcr.ifthen(pcr.boolean(pcr.cover(stream_ge, 0)), pcr.accuflux(ldd, 1)) riverid = pcr.ifthen(pcr.boolean(pcr.cover(stream_ge, 0)), subcatch) friction = 1./pcr.scalar(pcr.spreadzone(pcr.cover(pcr.ordinal(up_area), 0), 0, 0)) # *(pcr.scalar(ldd)*0+1) delta = pcr.ifthen(pcr.scalar(ldd)>=0, pcr.ifthen(pcr.cover(subcatch, 0)==0, pcr.spreadzone(pcr.cover(riverid, 0), 0, friction))) subcatch = pcr.ifthenelse(pcr.boolean(pcr.cover(subcatch, 0)), subcatch, delta) # finally, only keep basins with minimum and maximum river order flowing through them strahler_subcatch = pcr.areamaximum(stream, subcatch) subcatch = pcr.ifthen(pcr.ordinal(strahler_subcatch) >= min_strahler, pcr.ifthen(pcr.ordinal(strahler_subcatch) <= max_strahler, subcatch)) return stream_ge, pcr.ordinal(subcatch)
def dynamic(self): """ dynamic part of the indicator calculation module """ settings = LisSettings.instance() binding = settings.binding option = settings.options maskinfo = MaskInfo.instance() if option['TransientLandUseChange']: self.var.Population = readnetcdf(binding['PopulationMaps'], self.var.currentTimeStep()) self.var.RegionPopulation = np.take( np.bincount(self.var.WUseRegionC, weights=self.var.Population), self.var.WUseRegionC) # population sum in Regions if option['wateruse'] and option['indicator']: # check if it is the last monthly or annual time step next_date_time = self.var.CalendarDate + datetime.timedelta( seconds=int(binding["DtSec"])) self.var.monthend = next_date_time.month != self.var.CalendarDate.month self.var.yearend = next_date_time.year != self.var.CalendarDate.year # sum up every day self.var.DayCounter = self.var.DayCounter + 1.0 self.var.MonthETpotMM = self.var.MonthETpotMM + self.var.ETRef self.var.MonthETactMM = self.var.MonthETactMM + self.var.deffraction( self.var.TaInterception ) + self.var.TaPixel + self.var.ESActPixel if option['openwaterevapo']: self.var.MonthETactMM += self.var.EvaAddM3 * self.var.M3toMM self.var.MonthETdifMM = np.maximum( (self.var.MonthETpotMM - self.var.MonthETactMM) * self.var.LandUseMask, maskinfo.in_zero()) # ; land use mask can be used to mask out deserts and high mountains, where no agriculture is possible self.var.MonthWDemandM3 = self.var.MonthWDemandM3 + self.var.TotalDemandM3 self.var.MonthWAbstractionM3 = self.var.MonthWAbstractionM3 + self.var.TotalAbstractionFromSurfaceWaterM3 + self.var.ReservoirAbstractionM3 + self.var.LakeAbstractionM3 + self.var.TotalAbstractionFromGroundwaterM3 self.var.MonthWConsumptionM3 = self.var.MonthWConsumptionM3 + self.var.WUseAddM3 + self.var.ReservoirAbstractionM3 + self.var.LakeAbstractionM3 + self.var.TotalAbstractionFromGroundwaterM3 self.var.MonthDisM3 = self.var.MonthDisM3 + self.var.ChanQAvg * self.var.DtSec self.var.MonthWaterAbstractedfromLakesReservoirsM3 = self.var.MonthWaterAbstractedfromLakesReservoirsM3 + self.var.ReservoirAbstractionM3 + self.var.LakeAbstractionM3 self.var.RegionMonthIrrigationShortageM3 = self.var.RegionMonthIrrigationShortageM3 + self.var.AreatotalIrrigationShortageM3 # INTERNAL FLOW self.var.MonthInternalFlowM3 = self.var.MonthInternalFlowM3 + self.var.ToChanM3Runoff # -------------------------------------------------------------------------- if self.var.monthend: # INTERNAL FLOW if option['simulateReservoirs'] or option['simulateLakes']: # available LakeStorageM3 and ReservoirStorageM3 for potential abstraction at end of month in region self.var.RegionMonthReservoirAndLakeStorageM3 = np.take( np.bincount(self.var.WUseRegionC, weights=(self.var.ReservoirStorageM3 + self.var.LakeStorageM3)), self.var.WUseRegionC) # monthly abstraction from lakes and reservoirs self.var.RegionMonthWaterAbstractedfromLakesReservoirsM3 = np.take( np.bincount(self.var.WUseRegionC, weights=self.var. MonthWaterAbstractedfromLakesReservoirsM3), self.var.WUseRegionC) #PerMonthInternalFlowM3 = areatotal(cover(decompress(self.var.MonthInternalFlow),0.0),wreg) self.var.RegionMonthInternalFlowM3 = np.take( np.bincount(self.var.WUseRegionC, weights=self.var.MonthInternalFlowM3), self.var.WUseRegionC) # note Reservoir and Lake storage need to be taken into account seperately # EXTERNAL FLOW wreg = decompress(self.var.WUseRegionC) # to pcraster map because of the following expression! self.var.RegionMonthExternalInflowM3 = compressArray( areatotal( cover( ifthen( self.var.WaterRegionInflowPoints != 0, upstream(self.var.LddStructuresKinematic, decompress(self.var.MonthDisM3))), 0), wreg)) self.var.RegionMonthWAbstractionM3 = np.take( np.bincount(self.var.WUseRegionC, weights=self.var.MonthWAbstractionM3), self.var.WUseRegionC) self.var.RegionMonthWConsumptionM3 = np.take( np.bincount(self.var.WUseRegionC, weights=self.var.MonthWConsumptionM3), self.var.WUseRegionC) self.var.RegionMonthWDemandM3 = np.take( np.bincount(self.var.WUseRegionC, weights=self.var.MonthWDemandM3), self.var.WUseRegionC) # Calculation of WEI: everything in m3, totalled over the water region UpstreamInflowM3 = self.var.RegionMonthExternalInflowM3 LocalFreshwaterM3 = self.var.RegionMonthInternalFlowM3 # still to be decided if reservoir and lake water availability is added here LocalTotalWaterDemandM3 = self.var.RegionMonthWDemandM3 RemainingDemandM3 = np.maximum( LocalTotalWaterDemandM3 - LocalFreshwaterM3, 0.0) # this is the demand that cannot be met by local water supply UpstreamInflowUsedM3 = np.minimum(RemainingDemandM3, UpstreamInflowM3) # the amount of upstream water an area really depends on FossilGroundwaterUsedM3 = np.maximum( RemainingDemandM3 - UpstreamInflowUsedM3, 0.0) # the amount of water that cannot be met locally nor with international water # likely this is the amount of water drawn from deep groundwater self.var.WEI_Cns = np.where( (UpstreamInflowM3 + LocalFreshwaterM3) > 0.0, self.var.RegionMonthWConsumptionM3 / (UpstreamInflowM3 + LocalFreshwaterM3), 0.0) self.var.WEI_Abs = np.where( (UpstreamInflowM3 + LocalFreshwaterM3) > 0.0, self.var.RegionMonthWAbstractionM3 / (UpstreamInflowM3 + LocalFreshwaterM3), 0.0) self.var.WEI_Dem = np.where( (UpstreamInflowM3 + LocalFreshwaterM3) > 0.0, self.var.RegionMonthWDemandM3 / (UpstreamInflowM3 + LocalFreshwaterM3), 0.0) self.var.WaterSustainabilityIndex = np.where( LocalTotalWaterDemandM3 > 0.0, FossilGroundwaterUsedM3 / (LocalTotalWaterDemandM3 + 1), 0.0) # De Roo 2015: WTI, if above zero, indicates that situation is unsustainable # if index is 0 means sustainable situtation: no groundwater or desalination water used # if index is 1 means area relies completely on groundwater or desalination water # the '+1' is to prevent division by small values, leading to very large and misleading indicator values self.var.WaterDependencyIndex = np.where( LocalTotalWaterDemandM3 > 0.0, UpstreamInflowUsedM3 / (LocalTotalWaterDemandM3 + 1), 0.0) # De Roo 2015: WDI, dependency on upstreamwater, as a fraction of the total local demand # the '+1' is to prevent division by small values, leading to very large and misleading indicator values self.var.WaterSecurityIndex = np.where( UpstreamInflowM3 > 0.0, UpstreamInflowUsedM3 / (UpstreamInflowM3 + 1), 0.0) # De Roo 2015: WSI, indicates the vulnerability to the available upstream water; # if only 10% of upstream inflow is use, WSI would be 0.1 indicating low vulnerability # if WSI is close to 1, situation is very vulnerable # the '+1' is to prevent division by small values, leading to very large and misleading indicator values self.var.FalkenmarkM3Capita1 = np.where( self.var.RegionPopulation > 0.0, self.var.RegionMonthInternalFlowM3 * 12 / self.var.RegionPopulation, maskinfo.in_zero()) self.var.FalkenmarkM3Capita2 = np.where( self.var.RegionPopulation > 0.0, LocalFreshwaterM3 * 12 / self.var.RegionPopulation, maskinfo.in_zero()) self.var.FalkenmarkM3Capita3 = np.where( self.var.RegionPopulation > 0.0, (UpstreamInflowM3 + LocalFreshwaterM3) * 12 / self.var.RegionPopulation, maskinfo.in_zero())
water_body_outlet = pcr.ifthen(\ pcr.areaorder(pcr.scalar( water_body_outlet), \ water_body_outlet) == 1., water_body_outlet) water_body_outlet = pcr.ifthen(\ pcr.scalar(water_body_outlet) > 0., water_body_outlet) # calculate overbank volume from reservoirs (and lakes) lake_reservoir_volume = pcr.areatotal(extreme_value_map, water_body_id) lake_reservoir_overbank_volume = pcr.cover( pcr.max(0.0, lake_reservoir_volume - reservoir_capacity), 0.0) #~ pcr.aguila(lake_reservoir_overbank_volume) # # transfer 75% of overbank volume to the downstream (several cells downstream) transfer_to_downstream = pcr.cover(\ pcr.ifthen(pcr.scalar(water_body_outlet) > 0., lake_reservoir_overbank_volume * 0.50), 0.0) transfer_to_downstream = pcr.upstream(ldd_map_low_resolution, transfer_to_downstream) transfer_to_downstream = pcr.upstream(ldd_map_low_resolution, transfer_to_downstream) transfer_to_downstream = pcr.upstream(ldd_map_low_resolution, transfer_to_downstream) extreme_value_map = transfer_to_downstream + \ pcr.ifthenelse(pcr.cover(pcr.scalar(water_body_id), 0.0) > 0.00, 0.00, extreme_value_map) # # the remaining overbank volume (50%) will be distributed to the shores lake_reservoir_overbank_volume = lake_reservoir_overbank_volume * 0.50 land_area = cell_area * pcr.max(0.0, 1.0 - fracwat) land_area_average = pcr.areaaverage(land_area, water_body_id) land_area_weight = pcr.ifthenelse( land_area < land_area_average, 0.0, land_area_average) distributed_lake_reservoir_overbank_volume = pcr.cover(\ lake_reservoir_overbank_volume * land_area_weight / pcr.max(0.00, pcr.areatotal(land_area_weight, water_body_id)), 0.0) extreme_value_map = pcr.ifthenelse(pcr.cover(pcr.scalar(water_body_id), 0.0) > 0.00, distributed_lake_reservoir_overbank_volume, extreme_value_map) # # - cover the rests to zero (so they will not contribute to any flood/inundation)
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 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)