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 returnFloodedFraction(self, volume): #-returns the flooded fraction given the flood volume and the associated water height # using a logistic smoother near intersections (K&K, 2007) #-find the match on the basis of the shortest distance to the available intersections or steps deltaXMin = self.floodVolume[self.nrEntries - 1] y_i = pcr.scalar(1.) k = [pcr.scalar(0.)] * 2 mInt = pcr.scalar(0.) for iCnt in range(self.nrEntries - 1, 0, -1): #-find x_i for current volume and update match if applicable # also update slope and intercept deltaX = volume - self.floodVolume[iCnt] mask = pcr.abs(deltaX) < pcr.abs(deltaXMin) deltaXMin = pcr.ifthenelse(mask, deltaX, deltaXMin) y_i = pcr.ifthenelse(mask, self.areaFractions[iCnt], y_i) k[0] = pcr.ifthenelse(mask, self.kSlope[iCnt - 1], k[0]) k[1] = pcr.ifthenelse(mask, self.kSlope[iCnt], k[1]) mInt = pcr.ifthenelse(mask, self.mInterval[iCnt], mInt) #-all values returned, process data: calculate scaled deltaX and smoothed function # on the basis of the integrated logistic functions PHI(x) and 1-PHI(x) deltaX = deltaXMin deltaXScaled= pcr.ifthenelse(deltaX < 0.,pcr.scalar(-1.),1.)*\ pcr.min(criterionKK,pcr.abs(deltaX/pcr.max(1.,mInt))) logInt = self.integralLogisticFunction(deltaXScaled) #-compute fractional flooded area and flooded depth floodedFraction= pcr.ifthenelse(volume > 0.,\ pcr.ifthenelse(pcr.abs(deltaXScaled) < criterionKK,\ y_i-k[0]*mInt*logInt[0]+k[1]*mInt*logInt[1],\ y_i+pcr.ifthenelse(deltaX < 0.,k[0],k[1])*deltaX),0.) floodedFraction = pcr.max(0., pcr.min(1., floodedFraction)) floodDepth = pcr.ifthenelse(floodedFraction > 0., volume / (floodedFraction * self.cellArea), 0.) return floodedFraction, floodDepth
def detdrainlength(ldd, xl, yl): """ Determines the drainaige length (DCL) for a non square grid Input: - ldd - drainage network - xl - length of cells in x direction - yl - length of cells in y direction Output: - DCL """ # take into account non-square cells # if ldd is 8 or 2 use Ylength # if ldd is 4 or 6 use Xlength draindir = pcr.scalar(ldd) slantlength = pcr.sqrt(xl ** 2 + yl ** 2) drainlength = pcr.ifthenelse( draindir == 2, yl, pcr.ifthenelse( draindir == 8, yl, pcr.ifthenelse( draindir == 4, xl, pcr.ifthenelse(draindir == 6, xl, slantlength) ), ), ) return drainlength
def getWaterBodyDimensions(self): # Lake properties max depth and total lake area lakeMaxDepth = ((3.0 * self.waterBodyStorage) / (self.waterBodyShapeFactor**2))**(1. / 3.) lakeArea = (lakeMaxDepth * self.waterBodyShapeFactor)**2 # reservoir properties max depth at outlet and total reservoir area reservoirMaxDepth = ((6.0 * self.waterBodyStorage) / (self.waterBodyShapeFactor**2))**(1. / 3.) reservoirArea = 0.5 * (reservoirMaxDepth * self.waterBodyShapeFactor)**2 # dynamic waterbody area, cannot exceed static waterbody extent as defined by input self.dynamicArea = pcr.cover(pcr.ifthenelse(pcr.scalar(self.waterBodyTyp) == 1, lakeArea,\ pcr.ifthen(pcr.scalar(self.waterBodyTyp) == 2, reservoirArea)), 0.) self.dynamicArea = pcr.min(self.dynamicArea, self.waterBodyArea) # dynamic water fraction in gridcell self.dynamicFracWat = self.dynamicArea * self.waterBodyRelativeFrac / self.cellArea #TODO check if this is correct self.dynamicFracWat = pcr.cover( pcr.min(pcr.max(self.dynamicFracWat, 0.), self.fracWat), 0.) self.dynamicFracWat = self.fracWat #TODO remove this line self.dynamicFracWat = ifthen(self.landmask, self.dynamicFracWat) self.maxWaterDepth = pcr.cover(pcr.ifthenelse(pcr.scalar(self.waterBodyTyp) == 1, lakeMaxDepth,\ pcr.ifthen(pcr.scalar(self.waterBodyTyp) == 2, reservoirMaxDepth)))
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 detdrainwidth(ldd, xl, yl): """ Determines width of drainage over DEM for a non square grid Input: - ldd - drainage network - xl - length of cells in x direction - yl - length of cells in y direction Output: - DCL """ # take into account non-square cells # if ldd is 8 or 2 use Xlength # if ldd is 4 or 6 use Ylength draindir = pcr.scalar(ldd) slantwidth = (xl + yl) * 0.5 drainwidth = pcr.ifthenelse( draindir == 2, xl, pcr.ifthenelse( draindir == 8, xl, pcr.ifthenelse( draindir == 4, yl, pcr.ifthenelse(draindir == 6, yl, slantwidth) ), ), ) return drainwidth
def returnFloodedFraction(self,volume): #-returns the flooded fraction given the flood volume and the associated water height # using a logistic smoother near intersections (K&K, 2007) #-find the match on the basis of the shortest distance to the available intersections or steps deltaXMin= self.floodVolume[self.nrEntries-1] y_i= pcr.scalar(1.) k= [pcr.scalar(0.)]*2 mInt= pcr.scalar(0.) for iCnt in range(self.nrEntries-1,0,-1): #-find x_i for current volume and update match if applicable # also update slope and intercept deltaX= volume-self.floodVolume[iCnt] mask= pcr.abs(deltaX) < pcr.abs(deltaXMin) deltaXMin= pcr.ifthenelse(mask,deltaX,deltaXMin) y_i= pcr.ifthenelse(mask,self.areaFractions[iCnt],y_i) k[0]= pcr.ifthenelse(mask,self.kSlope[iCnt-1],k[0]) k[1]= pcr.ifthenelse(mask,self.kSlope[iCnt],k[1]) mInt= pcr.ifthenelse(mask,self.mInterval[iCnt],mInt) #-all values returned, process data: calculate scaled deltaX and smoothed function # on the basis of the integrated logistic functions PHI(x) and 1-PHI(x) deltaX= deltaXMin deltaXScaled= pcr.ifthenelse(deltaX < 0.,pcr.scalar(-1.),1.)*\ pcr.min(criterionKK,pcr.abs(deltaX/pcr.max(1.,mInt))) logInt= self.integralLogisticFunction(deltaXScaled) #-compute fractional flooded area and flooded depth floodedFraction= pcr.ifthenelse(volume > 0.,\ pcr.ifthenelse(pcr.abs(deltaXScaled) < criterionKK,\ y_i-k[0]*mInt*logInt[0]+k[1]*mInt*logInt[1],\ y_i+pcr.ifthenelse(deltaX < 0.,k[0],k[1])*deltaX),0.) floodedFraction= pcr.max(0.,pcr.min(1.,floodedFraction)) floodDepth= pcr.ifthenelse(floodedFraction > 0.,volume/(floodedFraction*self.cellArea),0.) return floodedFraction, floodDepth
def SnowPackHBV(Snow, SnowWater, Precipitation, Temperature, TTI, TT, TTM, Cfmax, WHC): """ HBV Type snowpack modelling using a Temperature degree factor. All correction factors (RFCF and SFCF) are set to 1. The refreezing efficiency factor is set to 0.05. :param Snow: :param SnowWater: :param Precipitation: :param Temperature: :param TTI: :param TT: :param TTM: :param Cfmax: :param WHC: :return: Snow,SnowMelt,Precipitation """ RFCF = 1.0 # correction factor for rainfall CFR = 0.05000 # refreeing efficiency constant in refreezing of freewater in snow SFCF = 1.0 # correction factor for snowfall RainFrac = pcr.ifthenelse( 1.0 * TTI == 0.0, pcr.ifthenelse(Temperature <= TT, pcr.scalar(0.0), pcr.scalar(1.0)), pcr.min((Temperature - (TT - TTI / 2)) / TTI, pcr.scalar(1.0)), ) RainFrac = pcr.max( RainFrac, pcr.scalar(0.0)) # fraction of precipitation which falls as rain SnowFrac = 1 - RainFrac # fraction of precipitation which falls as snow Precipitation = (SFCF * SnowFrac * Precipitation + RFCF * RainFrac * Precipitation ) # different correction for rainfall and snowfall SnowFall = SnowFrac * Precipitation # snowfall depth RainFall = RainFrac * Precipitation # rainfall depth PotSnowMelt = pcr.ifthenelse( Temperature > TTM, Cfmax * (Temperature - TTM), pcr.scalar(0.0)) # Potential snow melt, based on temperature PotRefreezing = pcr.ifthenelse( Temperature < TTM, Cfmax * CFR * (TTM - Temperature), 0.0) # Potential refreezing, based on temperature Refreezing = pcr.ifthenelse(Temperature < TTM, pcr.min(PotRefreezing, SnowWater), 0.0) # actual refreezing # No landuse correction here SnowMelt = pcr.min(PotSnowMelt, Snow) # actual snow melt Snow = Snow + SnowFall + Refreezing - SnowMelt # dry snow content SnowWater = SnowWater - Refreezing # free water content in snow MaxSnowWater = Snow * WHC # Max water in the snow SnowWater = (SnowWater + SnowMelt + RainFall ) # Add all water and potentially supersaturate the snowpack RainFall = pcr.max(SnowWater - MaxSnowWater, 0.0) # rain + surpluss snowwater SnowWater = SnowWater - RainFall return Snow, SnowWater, SnowMelt, RainFall, SnowFall
def volume_spread(ldd, hand, subcatch, volume, volume_thres=0., area_multiplier=1., iterations=15): """ Estimate 2D flooding from a 1D simulation per subcatchment reach Input: ldd -- pcraster object direction, local drain directions hand -- pcraster object float32, elevation data normalised to nearest drain subcatch -- pcraster object ordinal, subcatchments with IDs volume -- pcraster object float32, scalar flood volume (i.e. m3 volume outside the river bank within subcatchment) volume_thres=0. -- scalar threshold, at least this amount of m3 of volume should be present in a catchment area_multiplier=1. -- in case the maps are not in m2, set a multiplier other than 1. to convert iterations=15 -- number of iterations to use Output: inundation -- pcraster object float32, scalar inundation estimate """ #initial values pcr.setglobaloption("unittrue") dem_min = pcr.areaminimum(hand, subcatch) # minimum elevation in subcatchments # pcr.report(dem_min, 'dem_min.map') dem_norm = hand - dem_min # pcr.report(dem_norm, 'dem_norm.map') # surface of each subcatchment surface = pcr.areaarea(subcatch) * area_multiplier pcr.report(surface, 'surface.map') error_abs = pcr.scalar(1e10) # initial error (very high) volume_catch = pcr.areatotal(volume, subcatch) # pcr.report(volume_catch, 'volume_catch.map') depth_catch = volume_catch / surface pcr.report(depth_catch, 'depth_catch.map') dem_max = pcr.ifthenelse(volume_catch > volume_thres, pcr.scalar(32.), pcr.scalar(0)) # bizarre high inundation depth dem_min = pcr.scalar(0.) for n in range(iterations): print('Iteration: {:02d}'.format(n + 1)) #####while np.logical_and(error_abs > error_thres, dem_min < dem_max): dem_av = (dem_min + dem_max) / 2 # pcr.report(dem_av, 'dem_av00.{:03d}'.format(n + 1)) # compute value at dem_av average_depth_catch = pcr.areaaverage(pcr.max(dem_av - dem_norm, 0), subcatch) # pcr.report(average_depth_catch, 'depth_c0.{:03d}'.format(n + 1)) error = pcr.cover((depth_catch - average_depth_catch) / depth_catch, depth_catch * 0) # pcr.report(error, 'error000.{:03d}'.format(n + 1)) dem_min = pcr.ifthenelse(error > 0, dem_av, dem_min) dem_max = pcr.ifthenelse(error <= 0, dem_av, dem_max) # error_abs = np.abs(error) # TODO: not needed probably, remove inundation = pcr.max(dem_av - dem_norm, 0) return inundation
def derive_HAND(dem, ldd, accuThreshold, rivers=None, basin=None, up_area=None): """ Function derives Height-Above-Nearest-Drain. See http://www.sciencedirect.com/science/article/pii/S003442570800120X Input: dem -- pcraster object float32, elevation data ldd -- pcraster object direction, local drain directions accuThreshold -- upstream amount of cells as threshold for river delineation rivers=None -- you can provide a rivers layer here. Pixels that are identified as river should have a value > 0, other pixels a value of zero. basin=None -- set a boolean pcraster map where areas with True are estimated using the nearest drain in ldd distance and areas with False by means of the nearest friction distance. Friction distance estimated using the upstream area as weight (i.e. drains with a bigger upstream area have a lower friction) the spreadzone operator is used in this case. up_area=None -- provide the upstream area (if not assigned a guesstimate is prepared, assuming the LDD covers a full catchment area) Output: hand -- pcraster bject float32, height, normalised to nearest stream dist -- distance to nearest stream measured in cell lengths according to D8 directions """ if rivers is None: # prepare stream from a strahler threshold stream = pcr.ifthenelse( pcr.accuflux(ldd, 1) >= accuThreshold, pcr.boolean(1), pcr.boolean(0)) else: # convert stream network to boolean stream = pcr.boolean(pcr.cover(rivers, 0)) # determine height in river (in DEM*100 unit as ordinal) height_river = pcr.ifthenelse(stream, pcr.ordinal(dem * 100), 0) if basin is None: up_elevation = pcr.scalar(pcr.subcatchment(ldd, height_river)) else: # use basin to allocate areas outside basin to the nearest stream. Nearest is weighted by upstream area if up_area is None: up_area = pcr.accuflux(ldd, 1) up_area = pcr.ifthen(stream, up_area) # mask areas outside streams friction = 1. / pcr.scalar( pcr.spreadzone(pcr.cover(pcr.ordinal(up_area), 0), 0, 0)) # if basin, use nearest river within subcatchment, if outside basin, use weighted-nearest river up_elevation = pcr.ifthenelse( basin, pcr.scalar(pcr.subcatchment(ldd, height_river)), pcr.scalar(pcr.spreadzone(height_river, 0, friction))) # replace areas outside of basin by a spread zone calculation. hand = pcr.max(pcr.scalar(pcr.ordinal(dem * 100)) - up_elevation, 0) / 100 # convert back to float in DEM units # hand = (pcr.scalar(pcr.ordinal(dem*100))-up_elevation)/100 # convert back to float in DEM units dist = pcr.ldddist(ldd, stream, 1) # compute horizontal distance estimate return hand, dist
def fractionXY(surface): #-returns the fraction of transport in the x- and y-directions # given a gradient in a surface aspect= pcr.aspect(surface) noAspect= pcr.nodirection(pcr.directional(aspect)) sinAspect= pcr.sin(aspect) cosAspect= pcr.cos(aspect) fracX= pcr.ifthenelse(noAspect,0.,sinAspect/(pcr.abs(sinAspect)+pcr.abs(cosAspect))) fracY= pcr.ifthenelse(noAspect,0.,cosAspect/(pcr.abs(sinAspect)+pcr.abs(cosAspect))) return fracX,fracY
def glacierHBV(GlacierFrac, GlacierStore, Snow, Temperature, TT, Cfmax, G_SIfrac, timestepsecs, basetimestep): """ Run Glacier module and add the snowpack on-top of it. First, a fraction of the snowpack is converted into ice using the HBV-light model (fraction between 0.001-0.005 per day). Glacier melting is modelled using a Temperature degree factor and only occurs if the snow cover < 10 mm. :ivar GlacierFrac: Fraction of wflow cell covered by glaciers :ivar GlacierStore: Volume of the galcier in the cell in mm w.e. :ivar Snow: Snow pack on top of Glacier :ivar Temperature: Air temperature :ivar TT: Temperature threshold for ice melting :ivar Cfmax: Ice degree-day factor in mm/(°C/day) :ivar G_SIfrac: Fraction of the snow part turned into ice each timestep :ivar timestepsecs: Model timestep in seconds :ivar basetimestep: Model base timestep (86 400 seconds) :returns: Snow,Snow2Glacier,GlacierStore,GlacierMelt, """ #Fraction of the snow transformed into ice (HBV-light model) Snow2Glacier = G_SIfrac * Snow Snow2Glacier = pcr.ifthenelse( GlacierFrac > 0.0, Snow2Glacier, pcr.scalar(0.0) ) # Max conversion to 8mm/day Snow2Glacier = ( pcr.min(Snow2Glacier, 8.0) * timestepsecs / basetimestep ) Snow = Snow - (Snow2Glacier * GlacierFrac) GlacierStore = GlacierStore + Snow2Glacier PotMelt = pcr.ifthenelse( Temperature > TT, Cfmax * (Temperature - TT), pcr.scalar(0.0) ) # Potential snow melt, based on temperature GlacierMelt = pcr.ifthenelse( Snow < 10.0, pcr.min(PotMelt, GlacierStore), pcr.cover(0.0) ) # actual Glacier melt GlacierStore = GlacierStore - GlacierMelt # dry snow content return Snow, Snow2Glacier, GlacierStore, GlacierMelt
def glacierHBV(GlacierFrac, GlacierStore, Snow, Temperature, TT, Cfmax, G_SIfrac, timestepsecs, basetimestep): """ Run Glacier module and add the snowpack on-top of it. First, a fraction of the snowpack is converted into ice using the HBV-light model (fraction between 0.001-0.005 per day). Glacier melting is modelled using a Temperature degree factor and only occurs if the snow cover < 10 mm. :ivar GlacierFrac: Fraction of wflow cell covered by glaciers :ivar GlacierStore: Volume of the galcier in the cell in mm w.e. :ivar Snow: Snow pack on top of Glacier :ivar Temperature: Air temperature :ivar TT: Temperature threshold for ice melting :ivar Cfmax: Ice degree-day factor in mm/(°C/day) :ivar G_SIfrac: Fraction of the snow part turned into ice each timestep :ivar timestepsecs: Model timestep in seconds :ivar basetimestep: Model base timestep (86 400 seconds) :returns: Snow,Snow2Glacier,GlacierStore,GlacierMelt, """ #Fraction of the snow transformed into ice (HBV-light model) Snow2Glacier = G_SIfrac * Snow Snow2Glacier = pcr.ifthenelse( GlacierFrac > 0.0, Snow2Glacier, pcr.scalar(0.0) ) # Max conversion to 8mm/day Snow2Glacier = ( pcr.min(Snow2Glacier, 8.0 * (timestepsecs / basetimestep)) ) Snow = Snow - (Snow2Glacier * GlacierFrac) GlacierStore = GlacierStore + Snow2Glacier PotMelt = pcr.ifthenelse( Temperature > TT, Cfmax * (Temperature - TT), pcr.scalar(0.0) ) # Potential snow melt, based on temperature GlacierMelt = pcr.ifthenelse( Snow < 10.0, pcr.min(PotMelt, GlacierStore), pcr.cover(0.0) ) # actual Glacier melt GlacierStore = GlacierStore - GlacierMelt # dry snow content return Snow, Snow2Glacier, GlacierStore, GlacierMelt
def rainfall_interception_modrut( Precipitation, PotEvap, CanopyStorage, CanopyGapFraction, Cmax ): """ Interception according to a modified Rutter model. The model is solved explicitly and there is no drainage below Cmax. Returns: - NetInterception: P - TF - SF (may be different from the actual wet canopy evaporation) - ThroughFall: - StemFlow: - LeftOver: Amount of potential eveporation not used - Interception: Actual wet canopy evaporation in this thimestep - CanopyStorage: Canopy storage at the end of the timestep """ ########################################################################## # Interception according to a modified Rutter model with hourly timesteps# ########################################################################## p = CanopyGapFraction pt = 0.1 * p # Amount of P that falls on the canopy Pfrac = pcr.max((1 - p - pt), 0) * Precipitation # S cannot be larger than Cmax, no gravity drainage below that DD = pcr.ifthenelse(CanopyStorage > Cmax, CanopyStorage - Cmax, 0.0) CanopyStorage = CanopyStorage - DD # Add the precipitation that falls on the canopy to the store CanopyStorage = CanopyStorage + Pfrac # Now do the Evap, make sure the store does not get negative dC = -1 * pcr.min(CanopyStorage, PotEvap) CanopyStorage = CanopyStorage + dC LeftOver = PotEvap + dC # Amount of evap not used # Now drain the canopy storage again if needed... D = pcr.ifthenelse(CanopyStorage > Cmax, CanopyStorage - Cmax, 0.0) CanopyStorage = CanopyStorage - D # Calculate throughfall ThroughFall = DD + D + p * Precipitation StemFlow = Precipitation * pt # Calculate interception, this is NET Interception NetInterception = Precipitation - ThroughFall - StemFlow Interception = -dC return NetInterception, ThroughFall, StemFlow, LeftOver, Interception, CanopyStorage
def kinAlphaComposite(self,watStor,mask): #-given the total water storage and the mask specifying the occurrence of # floodplain conditions, retrns the Q-A relationship for the kinematic # wave and the associated parameters floodplainStorage= pcr.ifthen(mask,watStor) floodFrac, floodZ, dynamicWetA, dynamicAlphaQ= self.kinAlphaDynamic(floodplainStorage) staticWetA, staticAlphaQ= self.kinAlphaStatic(watStor) floodFrac= pcr.ifthenelse(mask,floodFrac,0.) floodZ= pcr.ifthenelse(mask,floodZ,0.) wetA= pcr.ifthenelse(mask,dynamicWetA,staticWetA) alphaQ= pcr.ifthenelse(mask,dynamicAlphaQ,staticAlphaQ) return floodFrac,floodZ,wetA,alphaQ
def volume_spread(ldd, hand, subcatch, volume, volume_thres=0., cell_surface=1., iterations=15, logging=logging, order=0): """ Estimate 2D flooding from a 1D simulation per subcatchment reach Input: ldd -- pcraster object direction, local drain directions hand -- pcraster object float32, elevation data normalised to nearest drain subcatch -- pcraster object ordinal, subcatchments with IDs volume -- pcraster object float32, scalar flood volume (i.e. m3 volume outside the river bank within subcatchment) volume_thres=0. -- scalar threshold, at least this amount of m3 of volume should be present in a catchment area_multiplier=1. -- in case the maps are not in m2, set a multiplier other than 1. to convert iterations=15 -- number of iterations to use Output: inundation -- pcraster object float32, scalar inundation estimate """ #initial values pcr.setglobaloption("unitcell") dem_min = pcr.areaminimum(hand, subcatch) # minimum elevation in subcatchments dem_norm = hand - dem_min # surface of each subcatchment surface = pcr.areaarea(subcatch) * pcr.areaaverage( cell_surface, subcatch) # area_multiplier error_abs = pcr.scalar(1e10) # initial error (very high) volume_catch = pcr.areatotal(volume, subcatch) depth_catch = volume_catch / surface # meters water disc averaged over subcatchment # ilt(depth_catch, 'depth_catch_{:02d}.map'.format(order)) # pcr.report(volume, 'volume_{:02d}.map'.format(order)) dem_max = pcr.ifthenelse(volume_catch > volume_thres, pcr.scalar(32.), pcr.scalar(0)) # bizarre high inundation depth dem_min = pcr.scalar(0.) for n in range(iterations): logging.debug('Iteration: {:02d}'.format(n + 1)) #####while np.logical_and(error_abs > error_thres, dem_min < dem_max): dem_av = (dem_min + dem_max) / 2 # compute value at dem_av average_depth_catch = pcr.areaaverage(pcr.max(dem_av - dem_norm, 0), subcatch) error = pcr.cover((depth_catch - average_depth_catch) / depth_catch, depth_catch * 0) dem_min = pcr.ifthenelse(error > 0, dem_av, dem_min) dem_max = pcr.ifthenelse(error <= 0, dem_av, dem_max) inundation = pcr.max(dem_av - dem_norm, 0) pcr.setglobaloption('unittrue') return inundation
def derive_HAND(dem, ldd, accuThreshold, rivers=None, basin=None, up_area=None, neg_HAND=None): """ Function derives Height-Above-Nearest-Drain. See http://www.sciencedirect.com/science/article/pii/S003442570800120X Input: dem -- pcraster object float32, elevation data ldd -- pcraster object direction, local drain directions accuThreshold -- upstream amount of cells as threshold for river delineation rivers=None -- you can provide a rivers layer here. Pixels that are identified as river should have a value > 0, other pixels a value of zero. basin=None -- set a boolean pcraster map where areas with True are estimated using the nearest drain in ldd distance and areas with False by means of the nearest friction distance. Friction distance estimated using the upstream area as weight (i.e. drains with a bigger upstream area have a lower friction) the spreadzone operator is used in this case. up_area=None -- provide the upstream area (if not assigned a guesstimate is prepared, assuming the LDD covers a full catchment area) neg_HAND=None -- if set to 1, HAND maps can have negative values when elevation outside of stream is lower than stream (for example when there are natural embankments) Output: hand -- pcraster bject float32, height, normalised to nearest stream dist -- distance to nearest stream measured in cell lengths according to D8 directions """ if rivers is None: # prepare stream from a strahler threshold stream = pcr.ifthenelse(pcr.accuflux(ldd, 1) >= accuThreshold, pcr.boolean(1), pcr.boolean(0)) else: # convert stream network to boolean stream = pcr.boolean(pcr.cover(rivers, 0)) # determine height in river (in DEM*100 unit as ordinal) height_river = pcr.ifthenelse(stream, pcr.ordinal(dem*100), 0) if basin is None: up_elevation = pcr.scalar(pcr.subcatchment(ldd, height_river)) else: # use basin to allocate areas outside basin to the nearest stream. Nearest is weighted by upstream area if up_area is None: up_area = pcr.accuflux(ldd, 1) up_area = pcr.ifthen(stream, up_area) # mask areas outside streams friction = 1./pcr.scalar(pcr.spreadzone(pcr.cover(pcr.ordinal(up_area), 0), 0, 0)) # if basin, use nearest river within subcatchment, if outside basin, use weighted-nearest river up_elevation = pcr.ifthenelse(basin, pcr.scalar(pcr.subcatchment(ldd, height_river)), pcr.scalar(pcr.spreadzone(height_river, 0, friction))) # replace areas outside of basin by a spread zone calculation. # make negative HANDS also possible if neg_HAND == 1: hand = (pcr.scalar(pcr.ordinal(dem*100))-up_elevation)/100 # convert back to float in DEM units else: hand = pcr.max(pcr.scalar(pcr.ordinal(dem*100))-up_elevation, 0)/100 # convert back to float in DEM units dist = pcr.ldddist(ldd, stream, 1) # compute horizontal distance estimate return hand, dist
def kinAlphaComposite(self, watStor, mask): #-given the total water storage and the mask specifying the occurrence of # floodplain conditions, retrns the Q-A relationship for the kinematic # wave and the associated parameters floodplainStorage = pcr.ifthen(mask, watStor) floodFrac, floodZ, dynamicWetA, dynamicAlphaQ = self.kinAlphaDynamic( floodplainStorage) staticWetA, staticAlphaQ = self.kinAlphaStatic(watStor) floodFrac = pcr.ifthenelse(mask, floodFrac, 0.) floodZ = pcr.ifthenelse(mask, floodZ, 0.) wetA = pcr.ifthenelse(mask, dynamicWetA, staticWetA) alphaQ = pcr.ifthenelse(mask, dynamicAlphaQ, staticAlphaQ) return floodFrac, floodZ, wetA, alphaQ
def testIfThenElse(self): pcraster.setclone("and_Expr1.map") exceptionThrown = False try: result = pcraster.ifthenelse(1.0 == 2.0, 3.0, 4.0) except RuntimeError as exception: message = str(exception) self.assertTrue(message.find("conversion function to pick a data type") != -1) exceptionThrown = True self.assertTrue(exceptionThrown) result = pcraster.ifthenelse(pcraster.boolean(1.0 == 2.0), \ pcraster.scalar(3.0), pcraster.scalar(4.0)) self.assertEqual(pcraster.cellvalue(result, 1)[0], 4.0)
def testIfThenElse(self): pcraster.setclone("and_Expr1.map") exceptionThrown = False try: result = pcraster.ifthenelse(1.0 == 2.0, 3.0, 4.0) except RuntimeError as exception: message = str(exception) self.assert_(message.find("conversion function to pick a data type") != -1) exceptionThrown = True self.assert_(exceptionThrown) result = pcraster.ifthenelse(pcraster.boolean(1.0 == 2.0), \ pcraster.scalar(3.0), pcraster.scalar(4.0)) self.assertEqual(pcraster.cellvalue(result, 1)[0], 4.0)
def dayLength(doy,lat): """ daylength fraction of day """ lat = lat * pcr.scalar(math.pi) / 180.0 M_PI_2 = pcr.spatial(pcr.scalar(math.pi / 2.0)) dec = pcr.sin( (6.224111 + 0.017202 * doy) * 180. / math.pi) dec = pcr.scalar(0.39785 * pcr.sin ((4.868961 + .017203 * doy + 0.033446 * pcr.sin (dec* 180 / math.pi)) * 180 / math.pi)) dec = pcr.scalar(pcr.asin(dec)) lat = pcr.ifthenelse(pcr.abs(lat) > M_PI_2, (M_PI_2 - pcr.scalar(0.01)) * pcr.ifthenelse(lat > 0, pcr.scalar(1.0), pcr.scalar(-1.0)) ,lat ) arg = pcr.tan(dec ) * pcr.tan(lat * 180.0 / math.pi ) * -1.0 h = pcr.scalar( pcr.acos(arg ) ) h = h / 180. * math.pi h = pcr.ifthenelse(arg > 1.0, 0.0,h) # /* sun stays below horizon */ h = pcr.ifthenelse(arg < -1.0 ,math.pi,h) # /* sun stays above horizon */ return (h / math.pi)
def volume_spread(ldd, hand, subcatch, volume, volume_thres=0., area_multiplier=1., iterations=15): """ Estimate 2D flooding from a 1D simulation per subcatchment reach Input: ldd -- pcraster object direction, local drain directions hand -- pcraster object float32, elevation data normalised to nearest drain subcatch -- pcraster object ordinal, subcatchments with IDs volume -- pcraster object float32, scalar flood volume (i.e. m3 volume outside the river bank within subcatchment) volume_thres=0. -- scalar threshold, at least this amount of m3 of volume should be present in a catchment area_multiplier=1. -- in case the maps are not in m2, set a multiplier other than 1. to convert iterations=15 -- number of iterations to use Output: inundation -- pcraster object float32, scalar inundation estimate """ #initial values pcr.setglobaloption("unittrue") dem_min = pcr.areaminimum(hand, subcatch) # minimum elevation in subcatchments # pcr.report(dem_min, 'dem_min.map') dem_norm = hand - dem_min # pcr.report(dem_norm, 'dem_norm.map') # surface of each subcatchment surface = pcr.areaarea(subcatch)*area_multiplier pcr.report(surface, 'surface.map') error_abs = pcr.scalar(1e10) # initial error (very high) volume_catch = pcr.areatotal(volume, subcatch) # pcr.report(volume_catch, 'volume_catch.map') depth_catch = volume_catch/surface pcr.report(depth_catch, 'depth_catch.map') dem_max = pcr.ifthenelse(volume_catch > volume_thres, pcr.scalar(32.), pcr.scalar(0)) # bizarre high inundation depth dem_min = pcr.scalar(0.) for n in range(iterations): print('Iteration: {:02d}'.format(n + 1)) #####while np.logical_and(error_abs > error_thres, dem_min < dem_max): dem_av = (dem_min + dem_max)/2 # pcr.report(dem_av, 'dem_av00.{:03d}'.format(n + 1)) # compute value at dem_av average_depth_catch = pcr.areaaverage(pcr.max(dem_av - dem_norm, 0), subcatch) # pcr.report(average_depth_catch, 'depth_c0.{:03d}'.format(n + 1)) error = pcr.cover((depth_catch-average_depth_catch)/depth_catch, depth_catch*0) # pcr.report(error, 'error000.{:03d}'.format(n + 1)) dem_min = pcr.ifthenelse(error > 0, dem_av, dem_min) dem_max = pcr.ifthenelse(error <= 0, dem_av, dem_max) # error_abs = np.abs(error) # TODO: not needed probably, remove inundation = pcr.max(dem_av - dem_norm, 0) return inundation
def getFWaterPotential(self): # calculates the reduction factor (0-1) for the stomatal conductance, i.e. stomatal # conductance in Penman will be the maximum stomatal conductance multiplied by # this reduction factor (see penman script) # the reduction factor is a linear function of soil moisture content, zero at # or below wilting point and one at or above field capacity, and a linear function inbetween fWaterPotentialTmp = (self.soilMoistureThick - self.wiltingPointThick) \ / self.maximumAvailableForVegetationUptakeThick fWaterPotentialTmpWilt = pcr.ifthenelse( self.soilMoistureThick < self.wiltingPointThick, pcr.scalar(0), fWaterPotentialTmp) self.fWaterPotential = pcr.ifthenelse( self.soilMoistureThick > self.limitingPointThick, pcr.scalar(1), fWaterPotentialTmpWilt) return self.fWaterPotential
def downscaleTemperature(self, currTimeStep, useFactor=False, maxCorrelationCriteria=-0.75, zeroCelciusInKelvin=273.15): tmpSlope = 1.000 * vos.netcdf2PCRobjClone(\ self.temperLapseRateNC, 'temperature',\ currTimeStep.month, useDoy = "Yes",\ cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) tmpSlope = pcr.min(0., tmpSlope) # must be negative tmpCriteria = vos.netcdf2PCRobjClone(\ self.temperatCorrelNC, 'temperature',\ currTimeStep.month, useDoy = "Yes",\ cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) tmpSlope = pcr.ifthenelse(tmpCriteria < maxCorrelationCriteria,\ tmpSlope, 0.0) tmpSlope = pcr.cover(tmpSlope, 0.0) if useFactor == True: temperatureInKelvin = self.temperature + zeroCelciusInKelvin factor = pcr.max(0.0, temperatureInKelvin + tmpSlope * self.anomalyDEM) factor = factor / \ pcr.areaaverage(factor, self.meteoDownscaleIds) factor = pcr.cover(factor, 1.0) self.temperature = factor * temperatureInKelvin - zeroCelciusInKelvin else: self.temperature = self.temperature + tmpSlope * self.anomalyDEM
def snaptomap(points, mmap): """ Snap the points in _points_ to nearest non missing values in _mmap_. Can be used to move gauge locations to the nearest rivers. Input: - points - map with points to move - mmap - map with points to move to Return: - map with shifted points """ points = pcr.cover(points, 0) # Create unique id map of mmap cells unq = pcr.nominal(pcr.cover(pcr.uniqueid(pcr.defined(mmap)), pcr.scalar(0.0))) # Now fill holes in mmap map with lues indicating the closes mmap cell. dist_cellid = pcr.scalar(pcr.spreadzone(unq, 0, 1)) # Get map with values at location in points with closes mmap cell dist_cellid = pcr.ifthenelse(points > 0, dist_cellid, 0) # Spread this out dist_fill = pcr.spreadzone(pcr.nominal(dist_cellid), 0, 1) # Find the new (moved) locations npt = pcr.uniqueid(pcr.boolean(pcr.ifthen(dist_fill == unq, unq))) # Now recreate the original value in the points maps ptcover = pcr.spreadzone(pcr.cover(points, 0), 0, 1) # Now get the org point value in the pt map nptorg = pcr.ifthen(npt > 0, ptcover) return nptorg
def downscalePrecipitation(self, currTimeStep, useFactor=True, minCorrelationCriteria=0.85): preSlope = 0.001 * vos.netcdf2PCRobjClone(\ self.precipLapseRateNC, 'precipitation',\ currTimeStep.month, useDoy = "Yes",\ cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) preSlope = pcr.cover(preSlope, 0.0) preSlope = pcr.max(0., preSlope) preCriteria = vos.netcdf2PCRobjClone(\ self.precipitCorrelNC, 'precipitation',\ currTimeStep.month, useDoy = "Yes",\ cloneMapFileName=self.cloneMap,\ LatitudeLongitude = True) preSlope = pcr.ifthenelse(preCriteria > minCorrelationCriteria,\ preSlope, 0.0) preSlope = pcr.cover(preSlope, 0.0) if useFactor == True: factor = pcr.max(0., self.precipitation + preSlope * self.anomalyDEM) factor = factor / \ pcr.areaaverage(factor, self.meteoDownscaleIds) factor = pcr.cover(factor, 1.0) self.precipitation = factor * self.precipitation else: self.precipitation = self.precipitation + preSlope * self.anomalyDEM self.precipitation = pcr.max(0.0, self.precipitation)
def _report(self, data, identifier, timestamp): """ Report function to store an attribue maps. In the traditional PCRaster the report function writes a map straight to a location on disk, with a directory structure and filename which is representative of the attribute and timestep at which the reporting takes place. This can cause some problems thougg. For example, a model reports 8 attributes, and the model has 24 timesteps. This will result in 192 map files, each of which (due to the PCRaster file format) is uncompressed, does not have detailed georeference information, does not contain overviews for quick zooming out, and is in the wrong projection (that of the model, rather than web mercator that we need). In GEMS we choose a slightly different approach and use GeoTiff as the storage mechanism. The variable self._report_layers contains the model outputs and has the following structure: { '<attribute_name>' : [ (<data_array>, <utc_timestamp>), (...), (...) ] } This way, accessing self._report_layers['snow_depth'] will return an array of tuples, of which each tuple contains a numpy array and a timestamp. To get a list of the reported attributes we can simply use list(self._report_layers) Every time an attribute is reported it is added to this variable, and at the end of the model run the _report_postprocess() is called, which turns each reported attribute into a separate geotiff file where the bands in the geotiff file correspond to one of the timesteps. Performing operations on these stacked geotiffs (such as creating overviews, requesting subsections, slices, or reprojecting) is much more efficient than on separate files. """ try: if identifier in self.reporting: logger.debug("Reporting map '%s' (datatype:%s timestep:%d timestamp:%s)"%(identifier, self.reporting[identifier]["datatype"], self.timestep, self.timestamp)) # Crop by the mask. Set all data outside mask to nodata value data = ifthenelse(self._mask, data, -9999) # Convert to numpy array. The numpy array will be added to stack # of maps, one for each timestep. data = pcr2numpy(data, -9999) # #Todo: implement some kind of clamp functionality here. if 'clamp' is set to true # on the symbolizer for this output attribute, set all values # above the max to the max value, and all below the min to the # min value. Allow overriding this in the model's report function like: # report(data,'map', clamp=True) and then use pcraster/numpy to clamp the data. # (rows,cols) = data.shape if identifier not in list(self._report_layers): self._report_layers.update({identifier:[]}) self._report_layers[identifier].append((data, timestamp)) else: logger.error("Don't know how to report '%s', please specify in the 'reporting' section of your model configuration."%(identifier)) except: logger.error("An exception occurred while trying to report '%s'"%(identifier)) return False else: logger.debug(" - Reporting map '%s' completed."%(identifier)) return True
def getValDivZero(x,y,y_lim=smallNumber,z_def= 0.): #-returns the result of a division that possibly involves a zero # denominator; in which case, a default value is substituted: # x/y= z in case y > y_lim, # x/y= z_def in case y <= y_lim, where y_lim -> 0. # z_def is set to zero if not otherwise specified return pcr.ifthenelse(y > y_lim,x/pcr.max(y_lim,y),z_def)
def satPressure(airT): """ calculates saturated vp from airt temperature Murray (1967) """ # airT - air temperature [degree C] */ satPressure = pcr.ifthenelse( airT >= 0.0, 0.61078 * pcr.exp(17.26939 * airT / (airT + 237.3)), 0.61078 * pcr.exp(21.87456 * airT / (airT + 265.5))) return satPressure
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 volume_spread(ldd, hand, subcatch, volume, volume_thres=0., cell_surface=1., iterations=15, logging=logging, order=0, neg_HAND=None): """ Estimate 2D flooding from a 1D simulation per subcatchment reach Input: ldd -- pcraster object direction, local drain directions hand -- pcraster object float32, elevation data normalised to nearest drain subcatch -- pcraster object ordinal, subcatchments with IDs volume -- pcraster object float32, scalar flood volume (i.e. m3 volume outside the river bank within subcatchment) volume_thres=0. -- scalar threshold, at least this amount of m3 of volume should be present in a catchment area_multiplier=1. -- in case the maps are not in m2, set a multiplier other than 1. to convert iterations=15 -- number of iterations to use neg_HAND -- if set to 1, HAND maps can have negative values when elevation outside of stream is lower than stream (for example when there are natural embankments) Output: inundation -- pcraster object float32, scalar inundation estimate """ #initial values pcr.setglobaloption("unitcell") dem_min = pcr.areaminimum(hand, subcatch) # minimum elevation in subcatchments dem_norm = hand - dem_min # surface of each subcatchment surface = pcr.areaarea(subcatch)*pcr.areaaverage(cell_surface, subcatch) # area_multiplier error_abs = pcr.scalar(1e10) # initial error (very high) volume_catch = pcr.areatotal(volume, subcatch) depth_catch = volume_catch/surface # meters water disc averaged over subcatchment # ilt(depth_catch, 'depth_catch_{:02d}.map'.format(order)) # pcr.report(volume, 'volume_{:02d}.map'.format(order)) if neg_HAND == 1: dem_max = pcr.ifthenelse(volume_catch > volume_thres, pcr.scalar(32.), pcr.scalar(-32.)) # bizarre high inundation depth☻ dem_min = pcr.scalar(-32.) else: dem_max = pcr.ifthenelse(volume_catch > volume_thres, pcr.scalar(32.), pcr.scalar(0.)) # bizarre high inundation depth☻ dem_min = pcr.scalar(0.) for n in range(iterations): logging.debug('Iteration: {:02d}'.format(n + 1)) #####while np.logical_and(error_abs > error_thres, dem_min < dem_max): dem_av = (dem_min + dem_max)/2 # compute value at dem_av average_depth_catch = pcr.areaaverage(pcr.max(dem_av - dem_norm, 0), subcatch) error = pcr.cover((depth_catch-average_depth_catch)/depth_catch, depth_catch*0) dem_min = pcr.ifthenelse(error > 0, dem_av, dem_min) dem_max = pcr.ifthenelse(error <= 0, dem_av, dem_max) inundation = pcr.max(dem_av - dem_norm, 0) pcr.setglobaloption('unittrue') return inundation
def agriZone_Ep(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on LP - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa - Code for ini-file: 2 """ JarvisCoefficients.calcEp(self, k) self.PotEvaporation = pcr.cover(pcr.ifthenelse(self.EpHour >= 0, self.EpHour, 0), 0) self.Qa = pcr.max(self.Pe - (self.samax[k] - self.Sa_t[k]), 0) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) self.SaN = pcr.min(self.Sa[k] / self.samax2, 1) self.SuN = self.Su[k] / self.sumax[k] self.Ea1 = pcr.max((self.PotEvaporation - self.Ei), 0) * pcr.min( self.Sa[k] / (self.samax[k] * self.LP[k]), 1 ) self.Fa1 = self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * e ** ( -self.decF[k] * self.SuN ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Fa = ( self.Fa1 + (self.Fa1 / pcr.ifthenelse(self.Fa1 + self.Ea1 > 0, self.Fa1 + self.Ea1, 1)) * self.Sa_diff ) self.Ea = ( self.Ea1 + (self.Ea1 / pcr.ifthenelse(self.Fa1 + self.Ea1 > 0, self.Fa1 + self.Ea1, 1)) * self.Sa_diff ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qa) - self.Ea - self.Fa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[k] = self.Pe - self.Ea - self.Qa - self.Fa - self.Sa[k] + self.Sa_t[k] self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa self.Fa_[k] = self.Fa
def derive_HAND(dem, ldd, accuThreshold, rivers=None, basin=None): """ Function derives Height-Above-Nearest-Drain. See http://www.sciencedirect.com/science/article/pii/S003442570800120X Input: dem -- pcraster object float32, elevation data ldd -- pcraster object direction, local drain directions accuThreshold -- upstream amount of cells as threshold for river delineation rivers=None -- you can provide a rivers layer here. Pixels that are identified as river should have a value > 0, other pixels a value of zero. basin=None -- set a boolean pcraster map where areas with True are estimated using the nearest drain in ldd distance and areas with False by means of the nearest friction distance. Friction distance estimated using the upstream area as weight (i.e. drains with a bigger upstream area have a lower friction) the spreadzone operator is used in this case. Output: hand -- pcraster bject float32, height, normalised to nearest stream dist -- distance to nearest stream measured in cell lengths according to D8 directions """ if rivers is None: stream = pcr.ifthenelse( pcr.accuflux(ldd, 1) >= accuThreshold, pcr.boolean(1), pcr.boolean(0) ) else: stream = pcr.boolean(pcr.cover(rivers, 0)) height_river = pcr.ifthenelse(stream, pcr.ordinal(dem * 100), 0) if basin is None: up_elevation = pcr.scalar(pcr.subcatchment(ldd, height_river)) else: drainage_surf = pcr.ifthen(rivers, pcr.accuflux(ldd, 1)) weight = 1.0 / pcr.scalar( pcr.spreadzone(pcr.cover(pcr.ordinal(drainage_surf), 0), 0, 0) ) up_elevation = pcr.ifthenelse( basin, pcr.scalar(pcr.subcatchment(ldd, height_river)), pcr.scalar(pcr.spreadzone(height_river, 0, weight)), ) # replace areas outside of basin by a spread zone calculation. hand = pcr.max(pcr.scalar(pcr.ordinal(dem * 100)) - up_elevation, 0) / 100 dist = pcr.ldddist(ldd, stream, 1) return hand, dist
def set_satDegUpp000005(self, observed_satDegUpp000005): # ratio between observation and model ratio_between_observation_and_model = pcr.ifthenelse(self.model.landSurface.satDegUpp000005> 0.0, observed_satDegUpp000005 / \ self.model.landSurface.satDegUpp000005, 0.0) # updating upper soil states for all lad cover types for coverType in self.model.landSurface.coverTypes: # correcting upper soil state (storUpp000005) self.model.landSurface.landCoverObj[coverType].storUpp000005 *= ratio_between_observation_and_model # if model value = 0.0, storUpp000005 is calculated based on storage capacity (model parameter) and observed saturation degree self.model.landSurface.landCoverObj[coverType].storUpp000005 = pcr.ifthenelse(self.model.landSurface.satDegUpp000005 > 0.0,\ self.model.landSurface.landCoverObj[coverType].storUpp000005,\ observed_satDegUpp000005 * self.model.landSurface.parameters.storCapUpp000005)
def create_city_map(ESA_CCI_land_use_map_filename, city_class_id): #Create cities map from land use map land_use_map = pcr.readmap(ESA_CCI_land_use_map_filename) cities = pcr.ifthenelse(land_use_map == city_class_id, pcr.boolean(1), pcr.boolean(0)) pcr.report( cities, os.path.join(os_output_folder, "cities_" + CASE_STUDY_NAME + ".map")) return
def testIfThenElse(self): pcraster.setclone("and_Expr1.map") exceptionThrown = False try: result = pcraster.ifthenelse(1.0 == 2.0, 3.0, 4.0) except RuntimeError, exception: message = str(exception) self.assert_(string.find(message, "conversion function to pick a data type") != -1) exceptionThrown = True
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 agriZone_Ep_Sa_beta(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on LP - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa --> incorporation of beta function - Fa is based on storage in Sa - Code for ini-file: 6 """ JarvisCoefficients.calcEp(self, k) self.PotEvaporation = pcr.cover( pcr.ifthenelse(self.EpHour >= 0, self.EpHour, 0), 0) self.samax2 = self.samax[k] * pcr.scalar(self.catchArea) self.Qaadd = pcr.max(self.Sa_t[k] + self.Pe - self.samax2, 0) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) self.SaN = pcr.min(pcr.max(self.Sa[k] / self.samax2, 0), 1) self.SuN = self.Su[k] / self.sumax[k] self.Ea1 = pcr.max((self.PotEvaporation - self.Ei), 0) * pcr.min( self.Sa[k] / (self.samax2 * self.LP[k]), 1) self.Qa1 = (self.Pe - self.Qaadd) * (1 - (1 - self.SaN)**self.beta[k]) self.Fa1 = pcr.ifthenelse( self.SaN > 0, self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * e**(-self.decF[k] * (1 - self.SaN)), 0, ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Qa1 - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Qa = (self.Qa1 + (self.Qa1 / pcr.ifthenelse(self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1)) * self.Sa_diff) self.Fa = (self.Fa1 + (self.Fa1 / pcr.ifthenelse(self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1)) * self.Sa_diff) self.Ea = (self.Ea1 + (self.Ea1 / pcr.ifthenelse(self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1)) * self.Sa_diff) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Ea - self.Fa - self.Qa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[k] = (self.Pe - self.Ea - self.Qa - self.Qaadd - self.Fa - self.Sa[k] + self.Sa_t[k]) self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa + self.Qaadd self.Fa_[k] = self.Fa
def remove_fxw(fixed_weirs, removal_area): """ Erase fixed weirs within the removal area. Line elements that get split by the removal are relabeled. ------------- Parameters fixed_weirs: Fixed weir DataFrame with Point geometry removal_area: Scalar map area, removal area indicated with -9999. Returns a fixed weir DataFrame. """ # add label details as columns labels = fixed_weirs.label.str.split(':', expand=True) labels.columns = ['label_nr', 'label_type'] fixed_weirs = pd.concat([fixed_weirs, labels], axis=1) # add reference value: 'selected' = 0:inside, 1:outside label_map = pcr.ifthenelse(pcr.defined(removal_area), pcr.scalar(0), pcr.scalar(1)) labeled_points = update_z(fixed_weirs.loc[:, list('abc')], label_map) fixed_weirs.loc[:, 'selected'] = labeled_points.z # 'parts' increases with 1 at every change from 1 to 0 and 0 to 1 in 'selected' fixed_weirs['parts'] = (1 + fixed_weirs.selected.diff().abs().cumsum()) fixed_weirs.fillna(0, inplace=True) # take the minimum value of 'parts' for each fixed weir and subtract # from the cumulative value to reset the counter per fixed weir nr_parts_cum = fixed_weirs.groupby(by='label').min().parts nr_parts_cum = nr_parts_cum.fillna(0).reset_index() fxw2 = pd.merge(fixed_weirs, nr_parts_cum, on='label', suffixes=['_l', '_r']) fxw2['parts'] = (1 + fxw2.parts_l - fxw2.parts_r) fxw2['new_label'] = fxw2.apply(lambda x: '{0}_{1}:{2}'.format(x.label_nr, str(int(x.parts)),\ str(x.label_type)),\ axis=1) # clean up points_per_part = fxw2.loc[:,['new_label', 'selected']]\ .groupby('new_label')\ .count().reset_index() points_per_part.columns = ['new_label', 'nr_points'] fxw3 = pd.merge(fxw2, points_per_part, on='new_label', suffixes=['_l', '_r']) fxw_out = fxw3[(fxw3.nr_points > 1) & (fxw3.selected == 1)] fxw_out.drop(['label', 'label_nr', 'label_type', 'selected',\ 'parts_l', 'parts_r', 'parts', 'nr_points'],\ axis = 1, inplace=True) fxw_out.rename(columns={'new_label': 'label'}, inplace=True) fxw_out.index = range(len(fxw_out)) # to overwrite duplicate indices return fxw_out
def estimate_iterations_kin_wave(Q, Beta, alpha, timestepsecs, dx, mv): celerity = pcr.ifthenelse(Q > 0.0, 1.0 / (alpha * Beta * Q**(Beta - 1)), 0.0) courant = (timestepsecs / dx) * celerity np_courant = pcr.pcr2numpy(courant, mv) np_courant[np_courant == mv] = np.nan it_kin = max(int(np.ceil(1.25 * (np.nanpercentile(np_courant, 95)))), 1) return it_kin
def initial(self): """ initial part of the evapo water module """ # ************************************************************ # ***** EVAPORATION # ************************************************************ self.var.EvaCumM3 = MaskInfo.instance().in_zero() # water use cumulated amount # water use substep amount settings = LisSettings.instance() option = settings.options binding = settings.binding maskinfo = MaskInfo.instance() if option['openwaterevapo']: LakeMask = loadmap('LakeMask', pcr=True) lmask = ifthenelse(LakeMask != 0, self.var.LddStructuresKinematic, 5) LddEva = lddrepair(lmask) lddC = compressArray(LddEva) inAr = decompress(np.arange(maskinfo.info.mapC[0], dtype="int32")) self.var.downEva = (compressArray(downstream( LddEva, inAr))).astype("int32") # each upstream pixel gets the id of the downstream pixel self.var.downEva[lddC == 5] = maskinfo.info.mapC[0] self.var.maxNoEva = int(loadmap('maxNoEva')) # all pits gets a high number # still to test if this works # ldd only inside lakes for calculating evaporation if option['varfractionwater']: self.var.diffmaxwater = loadmap( 'FracMaxWater') - self.var.WaterFraction # Fraction of maximum extend of water - fraction of water in lakes and rivers varWNo = [ 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 370 ] self.var.varW = [] # variable fraction of water self.var.varW1 = [] self.var.varW1.append(12) j = 0 for i in range(1, 367): if i >= varWNo[j + 1]: j += 1 self.var.varW1.append(j) for i in range(12): varWName = generateName(binding['WFractionMaps'], varWNo[i]) self.var.varW.append( loadLAI(binding['WFractionMaps'], varWName, i))
def getLakeOutflow(self, avgChannelDischarge, length_of_time_step=vos.secondsPerDay()): # waterHeight (m): temporary variable, a function of storage: minWaterHeight = ( 0.001 ) # (m) Rens used 0.001 m as the limit # this is to make sure there is always lake outflow, # but it will be still limited by available self.waterBodyStorage waterHeight = pcr.cover( pcr.max( minWaterHeight, (self.waterBodyStorage - pcr.cover(self.waterBodyCap, 0.0)) / self.waterBodyArea, ), 0.0, ) # weirWidth (m) : # - estimated from avgOutflow (m3/s) using the bankfull discharge formula # avgOutflow = self.avgOutflow avgOutflow = pcr.ifthenelse( avgOutflow > 0.0, avgOutflow, pcr.max(avgChannelDischarge, self.avgInflow, 0.001), ) # This is needed when new lakes/reservoirs introduced (its avgOutflow is still zero). avgOutflow = pcr.areamaximum(avgOutflow, self.waterBodyIds) # bankfullWidth = pcr.cover(pcr.scalar(4.8) * ((avgOutflow)**(0.5)), 0.0) weirWidthUsed = bankfullWidth weirWidthUsed = pcr.max( weirWidthUsed, self.minWeirWidth ) # TODO: minWeirWidth based on the GRanD database weirWidthUsed = pcr.cover( pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0.0, weirWidthUsed), 0.0) # avgInflow <= lakeOutflow (weirFormula) <= waterBodyStorage lakeOutflowInM3PerSec = pcr.max( self.weirFormula(waterHeight, weirWidthUsed), self.avgInflow) # unit: m3/s # estimate volume of water relased by lakes lakeOutflow = lakeOutflowInM3PerSec * length_of_time_step # unit: m3 lakeOutflow = pcr.min(self.waterBodyStorage, lakeOutflow) # lakeOutflow = pcr.ifthen( pcr.scalar(self.waterBodyIds) > 0.0, lakeOutflow) lakeOutflow = pcr.ifthen( pcr.scalar(self.waterBodyTyp) == 1, lakeOutflow) # TODO: Consider endorheic lake/basin. No outflow for endorheic lake/basin! return lakeOutflow
def rainfall_interception_gash(Cmax, EoverR, CanopyGapFraction, Precipitation, CanopyStorage, maxevap=9999): """ Interception according to the Gash model (For daily timesteps). """ # TODO: add other rainfall interception method (lui) # TODO: Include subdaily Gash model # TODO: add LAI variation in year # Hack for stemflow pt = 0.1 * CanopyGapFraction P_sat = pcr.max( pcr.scalar(0.0), pcr.cover( (-Cmax / EoverR) * pcr.ln(1.0 - (EoverR / (1.0 - CanopyGapFraction - pt))), pcr.scalar(0.0), ), ) # large storms P > P_sat largestorms = Precipitation > P_sat Iwet = pcr.ifthenelse( largestorms, ((1 - CanopyGapFraction - pt) * P_sat) - Cmax, Precipitation * (1 - CanopyGapFraction - pt), ) Isat = pcr.ifthenelse(largestorms, (EoverR) * (Precipitation - P_sat), 0.0) Idry = pcr.ifthenelse(largestorms, Cmax, 0.0) Itrunc = 0 StemFlow = pt * Precipitation ThroughFall = Precipitation - Iwet - Idry - Isat - Itrunc - StemFlow Interception = Iwet + Idry + Isat + Itrunc # Non corect for area without any Interception (say open water Cmax -- zero) CmaxZero = Cmax <= 0.0 ThroughFall = pcr.ifthenelse(CmaxZero, Precipitation, ThroughFall) Interception = pcr.ifthenelse(CmaxZero, pcr.scalar(0.0), Interception) StemFlow = pcr.ifthenelse(CmaxZero, pcr.scalar(0.0), StemFlow) # Now corect for maximum potential evap OverEstimate = pcr.ifthenelse(Interception > maxevap, Interception - maxevap, pcr.scalar(0.0)) Interception = pcr.min(Interception, maxevap) # Add surpluss to the thoughdfall ThroughFall = ThroughFall + OverEstimate return ThroughFall, Interception, StemFlow, CanopyStorage
def getLakeOutflow( self, avgChannelDischarge, length_of_time_step=vos.secondsPerDay() ): # waterHeight (m): temporary variable, a function of storage: minWaterHeight = ( 0.001 ) # (m) Rens used 0.001 m as the limit # this is to make sure there is always lake outflow, # but it will be still limited by available self.waterBodyStorage waterHeight = pcr.cover( pcr.max( minWaterHeight, (self.waterBodyStorage - pcr.cover(self.waterBodyCap, 0.0)) / self.waterBodyArea, ), 0.0, ) # weirWidth (m) : # - estimated from avgOutflow (m3/s) using the bankfull discharge formula # avgOutflow = self.avgOutflow avgOutflow = pcr.ifthenelse( avgOutflow > 0.0, avgOutflow, pcr.max(avgChannelDischarge, self.avgInflow, 0.001), ) # This is needed when new lakes/reservoirs introduced (its avgOutflow is still zero). avgOutflow = pcr.areamaximum(avgOutflow, self.waterBodyIds) # bankfullWidth = pcr.cover(pcr.scalar(4.8) * ((avgOutflow) ** (0.5)), 0.0) weirWidthUsed = bankfullWidth weirWidthUsed = pcr.max( weirWidthUsed, self.minWeirWidth ) # TODO: minWeirWidth based on the GRanD database weirWidthUsed = pcr.cover( pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0.0, weirWidthUsed), 0.0 ) # avgInflow <= lakeOutflow (weirFormula) <= waterBodyStorage lakeOutflowInM3PerSec = pcr.max( self.weirFormula(waterHeight, weirWidthUsed), self.avgInflow ) # unit: m3/s # estimate volume of water relased by lakes lakeOutflow = lakeOutflowInM3PerSec * length_of_time_step # unit: m3 lakeOutflow = pcr.min(self.waterBodyStorage, lakeOutflow) # lakeOutflow = pcr.ifthen(pcr.scalar(self.waterBodyIds) > 0.0, lakeOutflow) lakeOutflow = pcr.ifthen(pcr.scalar(self.waterBodyTyp) == 1, lakeOutflow) # TODO: Consider endorheic lake/basin. No outflow for endorheic lake/basin! return lakeOutflow
def rainfall_interception_gash( Cmax, EoverR, CanopyGapFraction, Precipitation, CanopyStorage, maxevap=9999 ): """ Interception according to the Gash model (For daily timesteps). """ # TODO: add other rainfall interception method (lui) # TODO: Include subdaily Gash model # TODO: add LAI variation in year # Hack for stemflow pt = 0.1 * CanopyGapFraction P_sat = pcr.max( pcr.scalar(0.0), pcr.cover( (-Cmax / EoverR) * pcr.ln(1.0 - (EoverR / (1.0 - CanopyGapFraction - pt))), pcr.scalar(0.0), ), ) # large storms P > P_sat largestorms = Precipitation > P_sat Iwet = pcr.ifthenelse( largestorms, ((1 - CanopyGapFraction - pt) * P_sat) - Cmax, Precipitation * (1 - CanopyGapFraction - pt), ) Isat = pcr.ifthenelse(largestorms, (EoverR) * (Precipitation - P_sat), 0.0) Idry = pcr.ifthenelse(largestorms, Cmax, 0.0) Itrunc = 0 StemFlow = pt * Precipitation ThroughFall = Precipitation - Iwet - Idry - Isat - Itrunc - StemFlow Interception = Iwet + Idry + Isat + Itrunc # Non corect for area without any Interception (say open water Cmax -- zero) CmaxZero = Cmax <= 0.0 ThroughFall = pcr.ifthenelse(CmaxZero, Precipitation, ThroughFall) Interception = pcr.ifthenelse(CmaxZero, pcr.scalar(0.0), Interception) StemFlow = pcr.ifthenelse(CmaxZero, pcr.scalar(0.0), StemFlow) # Now corect for maximum potential evap OverEstimate = pcr.ifthenelse( Interception > maxevap, Interception - maxevap, pcr.scalar(0.0) ) Interception = pcr.min(Interception, maxevap) # Add surpluss to the thoughdfall ThroughFall = ThroughFall + OverEstimate return ThroughFall, Interception, StemFlow, CanopyStorage
def pt_flow_in_river(ldd, river): """ Returns all points (True) that flow into the mak river (boolean map with river set to True) :param ldd: Drainage network :param river: Map of river (True River, False non-river) :return ifmap: map with infrlo points into the river (True) :return ctach: catchment of each of the inflow points """ dspts = pcr.downstream(ldd, pcr.cover(river, 0)) dspts = pcr.ifthenelse(pcr.cover(river, 0) == 1, 0, dspts) catch = pcr.subcatchment(ldd, pcr.nominal(pcr.uniqueid(dspts))) return dspts, catch
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)
time_index_in_netcdf_file = i_year - str_year + 1 value_from_the_hydrological_year_1 = vos.netcdf2PCRobjClone(input_files['file_name']["hydrological_year_1"][var], \ varDict.netcdf_short_name[var], time_index_in_netcdf_file,\ useDoy = "Yes", cloneMapFileName = clone_map_file,\ LatitudeLongitude = True,\ specificFillValue = None) value_from_the_hydrological_year_2 = vos.netcdf2PCRobjClone(input_files['file_name']["hydrological_year_2"][var], \ varDict.netcdf_short_name[var], time_index_in_netcdf_file,\ useDoy = "Yes", cloneMapFileName = clone_map_file,\ LatitudeLongitude = True,\ specificFillValue = None) # merging two hydrological years value_for_this_year = pcr.ifthenelse(pcr.scalar(hydro_year_type) == 1, value_from_the_hydrological_year_1, \ value_from_the_hydrological_year_2) value_for_this_year = pcr.cover(value_for_this_year, 0.0) if landmask_only: value_for_this_year = pcr.ifthen(landmask, value_for_this_year) # report to a netcdf file ncFileName = output_files[var]['file_name'] msg = "Saving to the netcdf file: " + str(ncFileName) logger.info(msg) time_stamp_used = datetime.datetime(i_year, 12, 31, 0) netcdf_report.data2NetCDF(ncFileName, varDict.netcdf_short_name[var], pcr.pcr2numpy(value_for_this_year, vos.MV), time_stamp_used)
specificFillValue = None) maximum_discharge = pcr.areamaximum(\ pcr.cover(maximum_discharge, 0.0), basin_map) # - find the month with maximum discharge maximum_month = pcr.spatial(pcr.scalar(1.0)) for i_month in range(1, 12 + 1): # read the climatology discharge time series discharge_for_this_month = vos.netcdf2PCRobjClone(input_files['climatologyDischargeMonthAvg'], \ "discharge", i_month,\ useDoy = "Yes", cloneMapFileName = clone_map_file,\ LatitudeLongitude = True,\ specificFillValue = None) # upscale it to the basin scale discharge_for_this_month = pcr.areamaximum(discharge_for_this_month, basin_map) maximum_month = pcr.ifthenelse(discharge_for_this_month == maximum_discharge, pcr.scalar(i_month), maximum_month) pcr.report(maximum_month, "maximum_month.map") # defining the hydrological year type msg = "Defining the type of hydrological year:" logger.info(msg) hydrological_year_type = pcr.spatial(pcr.nominal(1)) hydrological_year_type = pcr.ifthenelse(maximum_month == 9, pcr.nominal(2), hydrological_year_type) hydrological_year_type = pcr.ifthenelse(maximum_month == 10, pcr.nominal(2), hydrological_year_type) hydrological_year_type = pcr.ifthenelse(maximum_month == 11, pcr.nominal(2), hydrological_year_type) hydrological_year_type = pcr.cover(hydrological_year_type, pcr.nominal(1)) hydrological_year_type = pcr.ifthen(landmask, hydrological_year_type) pcr.report(hydrological_year_type, "hydrological_year_type.map") #~ pcr.aguila(hydrological_year_type)
def getQAtBasinMouths(discharge, basinMouth): temp = pcr.ifthenelse(basinMouth != 0 , discharge * secondsPerDay(),0.) pcr.report(temp,"temp.map") return (getMapTotal(temp) / 1e9)
# loop the file list to get the correct file for pcraster_file in input_pcraster_files: if historical_results: if os.path.basename(pcraster_file).startswith(return_period): selected_pcraster_file = pcraster_file map_for_this_return_period = pcr.readmap(selected_pcraster_file) else: if return_period in pcraster_file: selected_pcraster_file = pcraster_file map_for_this_return_period = pcr.readmap(selected_pcraster_file) print selected_pcraster_file map_for_this_return_period = pcr.cover(map_for_this_return_period, 0.0) if i_return_period > 0: check_map = pcr.ifthenelse(map_for_this_return_period >= previous_map, pcr.scalar(0.0), pcr.scalar(-1.0)) minimum_value, maximum_value, average_value = vos.getMinMaxMean(check_map) msg = "" msg += "\n" msg += "\n" msg += "Checkting that the values in the file %s are equal to or bigger than the file %s : Min %f Max %f Mean %f" %(selected_pcraster_file, previous_file, minimum_value, maximum_value, average_value) msg += "\n" msg += "\n" print(msg) previous_map = map_for_this_return_period previous_file = selected_pcraster_file
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 agriZone_Ep_Sa_beta_Fvar(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on LP - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa --> incorporation of beta function - Fa is based on storage in Sa - Code for ini-file: 8 """ JarvisCoefficients.calcEp(self, k) self.PotEvaporation = self.EpHour self.samax2 = self.samax[k] * pcr.scalar(self.catchArea) self.Qaadd = pcr.max(self.Sa_t[k] + self.Pe - self.samax2, 0) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) self.SaN = pcr.min(self.Sa[k] / self.samax2, 1) self.SuN = self.Su[k] / self.sumax[k] self.Ea1 = pcr.max((self.PotEvaporation - self.Ei), 0) * pcr.min( self.Sa[k] / (self.samax2 * self.LP[k]), 1 ) self.Qa1 = (self.Pe - self.Qaadd) * (1 - (1 - self.SaN) ** self.beta[k]) self.Fa1 = self.cropG * pcr.ifthenelse( self.SaN > 0, self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * e ** (-self.decF[k] * (1 - self.SaN)), 0, ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Qa1 - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Qa = ( self.Qa1 + ( self.Qa1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Fa = ( self.Fa1 + ( self.Fa1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Ea = ( self.Ea1 + ( self.Ea1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Ea - self.Fa - self.Qa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[k] = ( self.Pe - self.Ea - self.Qa - self.Qaadd - self.Fa - self.Sa[k] + self.Sa_t[k] ) self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa + self.Qaadd self.Fa_[k] = self.Fa
logger.info(msg) landmask_30sec = pcr.defined(pcr.readmap(landmask_30sec_file)) landmask_used = pcr.ifthen(landmask_30sec, landmask_30sec) # boolean maps to mask out permanent water bodies (lakes and reservoirs): reservoirs_30sec_file = "/projects/0/aqueduct/users/edwinsut/data/reservoirs_and_lakes_30sec/grand_reservoirs_v1_1.boolean.map" msg = "Set the (high resolution) reservoirs based on the file: " + str(reservoirs_30sec_file) logger.info(msg) reservoirs_30sec = pcr.cover(pcr.readmap(reservoirs_30sec_file), pcr.boolean(0.0)) lakes_30sec_file = "/projects/0/aqueduct/users/edwinsut/data/reservoirs_and_lakes_30sec/glwd1_lakes.boolean.map" msg = "Set the (high resolution) lakes based on the file: " + str(lakes_30sec_file) logger.info(msg) lakes_30sec = pcr.cover(pcr.readmap(lakes_30sec_file), pcr.boolean(0.0)) # # cells that do not belong lakes and reservoirs non_permanent_water_bodies = pcr.ifthenelse(reservoirs_30sec, pcr.boolean(0.0), pcr.boolean(1.0)) non_permanent_water_bodies = pcr.ifthen(non_permanent_water_bodies, non_permanent_water_bodies) non_permanent_water_bodies = pcr.ifthenelse( lakes_30sec, pcr.boolean(0.0), non_permanent_water_bodies) non_permanent_water_bodies = pcr.ifthen(non_permanent_water_bodies, non_permanent_water_bodies) #~ pcr.aguila(non_permanent_water_bodies) # Convert pcraster files to a netcdt file: msg = "Convert pcraster maps to a netcdf file." logger.info(msg) # netcdf general setup: netcdf_setup = {} netcdf_setup['format'] = "NETCDF4" netcdf_setup['zlib'] = False netcdf_setup['institution'] = "Department of Physical Geography, Utrecht University" netcdf_setup['title' ] = "PCR-GLOBWB 2 output (post-processed for the Aqueduct Flood Analyzer): Flood Inundation Depth (above surface level)."