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 dynamic(self): """ *Required* This is where all the time dependent functions are executed. Time dependent output should also be saved here. """ self.wf_updateparameters() # read the temperature map for each step (see parameters()) self.wf_multparameters() # needed so parameters can be altered in the inifile under [variable_change_once] # snow routine snowfall = self.precipitation snowfall = pcr.scalar(self.temperature < 3.0) * snowfall self.snow = self.snow + snowfall melt = (self.meltf * self.temperature) melt = pcr.scalar(self.temperature > 3.0) * melt melt = pcr.max(0.0, pcr.min(self.snow, melt)) self.snow = self.snow - melt self.precipitation = self.precipitation - snowfall + melt # soil storage self.PotenEvap = self.PET * self.cropf Peff = self.precipitation - self.PotenEvap Aw_1 = self.Available_water # Soil wetting below capacity self.whc = pcr.max(0.0, self.whc) self.below_cap = (Aw_1 + pcr.max(0.0, Peff)) <= self.whc self.available_water_below_cap = (self.Available_water * pcr.scalar(self.below_cap)) + (pcr.max(0.0, Peff) * pcr.scalar(self.below_cap)) # Soil wetting above capacity self.above_cap = (Aw_1 + Peff) > self.whc self.excess = (Aw_1 * pcr.scalar(self.above_cap)) + (Peff * pcr.scalar(self.above_cap)) - (self.whc * pcr.scalar(self.above_cap)) self.available_water_above_cap = (self.whc * pcr.scalar(self.above_cap)) self.Available_water = self.available_water_below_cap + self.available_water_above_cap # soil drying self.drying = Peff <= 0.0 self.exp_content = (Peff)/(self.whc) self.available_water_drying = (Aw_1 * pcr.scalar(self.drying)) * pcr.exp(self.exp_content) # soil that is not drying self.not_drying = Peff > 0.0 self.Available_water = self.Available_water * pcr.scalar(self.not_drying) self.Available_water = self.Available_water + self.available_water_drying self.excess = self.excess * pcr.scalar(self.not_drying) # seepage to groundwater self.runoff = self.togwf * self.excess self.togw = self.excess - self.runoff # adding water to groundwater and taking water from groundwater self.Ground_water = self.Ground_water + self.togw self.sloflo = (self.Ground_water/self.C) self.Ground_water = self.Ground_water - self.sloflo # adding water from groundwater to runoff self.runoff = self.runoff + self.sloflo
def sCurve(X, a=0.0, b=1.0, c=1.0): """ sCurve function: Input: - X input map - C determines the steepness or "stepwiseness" of the curve. The higher C the sharper the function. A negative C reverses the function. - b determines the amplitude of the curve - a determines the centre level (default = 0) Output: - result """ try: s = 1.0 / (b + pcr.exp(-c * (X - a))) except: s = 1.0 / (b + pcr.exp(-c * (X - a))) return s
def dynamic(self): logger.info("Step 4: Monte Carlo simulation") # draw a random value (uniform for the entire map) z = pcr.mapnormal() #~ self.report(z,"z") # constraints, in order to make sure that random values are in the table of "lookup_table_average_thickness" z = pcr.max(-5.0, z) z = pcr.min(5.0, z) # assign average thickness (also uniform for the entire map) based on z self.Davg = pcr.lookupscalar(self.lookup_table_average_thickness, z) # self.report(self.Davg, "davg") self.lnDavg = pcr.ln(self.Davg) # sedimentary basin thickness (varying over cells and samples) lnD = self.F * (self.lnCV * self.lnDavg) + self.lnDavg # set the minimum depth (must be bigger than zero) minimum_depth = 0.005 lnD = pcr.max(pcr.ln(minimum_depth), lnD) # extrapolation lnD = pcr.cover(lnD, \ pcr.windowaverage(lnD, 1.50*vos.getMapAttributes(self.clone_map_file,"cellsize"))) lnD = pcr.cover(lnD, \ pcr.windowaverage(pcr.cover(lnD, pcr.ln(minimum_depth)), 3.00*vos.getMapAttributes(self.clone_map_file,"cellsize"))) lnD = pcr.cover(lnD, \ pcr.windowaverage(pcr.cover(lnD, pcr.ln(minimum_depth)), 0.50)) # smoothing per quarter arc degree lnD = pcr.windowaverage(lnD, 0.25) # thickness in meter self.D = pcr.exp(lnD) #~ # smoothing bottom elevation #~ dem_bottom = pcr.windowaverage(self.dem_average - self.D, 0.50) #~ # thickness in meter #~ self.D = pcr.max(0.0, self.dem_average - dem_bottom) #~ # smoothing #~ self.D = pcr.windowaverage(self.D, 1.50*vos.getMapAttributes(self.clone_map_file,"cellsize")) # accuracy until cm only self.D = pcr.rounddown(self.D * 100.) / 100. self.report(self.D, "damc")
def updateWeight(self): print('#### UPDATEWEIGHTING') print('filter period', self.filterPeriod()) print('filter timestep ', self._d_filterTimesteps[self.filterPeriod() - 1]) print('lijst ', self._d_filterTimesteps) print('filter sample ', self.currentSampleNumber()) modelledData = self.readmap('Rqs') observations = self.readDeterministic('observations/Rqs') #observations=pcr.ifthen(pit(self.ldd) != 0,syntheticData) measurementErrorSD = 3.0 * observations + 1.0 sum = pcr.maptotal(((modelledData - observations)**2) / (2.0 * (measurementErrorSD**2))) weight = pcr.exp(-sum) weightFloatingPoint, valid = pcr.cellvalue(weight, 1) return weightFloatingPoint
def correction_per_aquifer(self, id): id = float(id); print id # identify aquifer mask aquifer_landmask = pcr.ifthen(self.margat_aquifer_map == pcr.nominal(id), pcr.boolean(1)) # obtain the logarithmic value of Margat value exp_margat_thick = pcr.cellvalue(\ pcr.mapmaximum(\ pcr.ifthen(aquifer_landmask, pcr.ln(self.margat_aquifer_thickness))), 1)[0] # obtain the logarithmic values of 'estimated thickness' exp_approx_thick = pcr.ifthen(aquifer_landmask, pcr.ln(self.approx_thick)) exp_approx_thick_array = pcr.pcr2numpy(exp_approx_thick, vos.MV) exp_approx_thick_array = exp_approx_thick_array[exp_approx_thick_array <> vos.MV] exp_approx_thick_array = exp_approx_thick_array[exp_approx_thick_array < 1000000.] # identify percentile exp_approx_minim = np.percentile(exp_approx_thick_array, 2.5); exp_approx_maxim = np.percentile(exp_approx_thick_array, 97.5); # correcting exp_approx_thick_correct = ( exp_approx_thick - exp_approx_minim ) / \ ( exp_approx_maxim - exp_approx_minim ) exp_approx_thick_correct = pcr.max(0.0, exp_approx_thick_correct ) exp_approx_thick_correct *= pcr.max(0.0,\ ( exp_margat_thick - exp_approx_minim ) ) exp_approx_thick_correct += pcr.min(exp_approx_minim, exp_approx_thick) # maximum thickness exp_approx_thick_correct = pcr.min(exp_margat_thick, exp_approx_thick_correct) # corrected thickness correct_thickness = pcr.exp(exp_approx_thick_correct) return correct_thickness
def createInstancesInitial(self): import generalfunctions if readDistributionOfParametersFromDisk: path = '/home/derek/tmp/' maximumInterceptionCapacityPerLAI = pcr.scalar( path + pcrfw.generateNameS('RPic', self.currentSampleNumber()) + '.map') ksat = pcr.scalar( path + pcrfw.generateNameS('RPks', self.currentSampleNumber()) + '.map') regolithThicknessHomogeneous = pcr.scalar( path + pcrfw.generateNameS('RPrt', self.currentSampleNumber()) + '.map') saturatedConductivityMetrePerDay = pcr.scalar( path + pcrfw.generateNameS('RPsc', self.currentSampleNumber()) + '.map') multiplierMaxStomatalConductance = pcr.scalar( path + pcrfw.generateNameS('RPmm', self.currentSampleNumber()) + '.map') else: maximumInterceptionCapacityPerLAI = generalfunctions.areauniformBounds( 0.0001, 0.0005, pcr.nominal(1), pcr.scalar(cfg.maximumInterceptionCapacityValue), createRealizations) ksat = generalfunctions.areauniformBounds( 0.025, 0.05, pcr.nominal(1), pcr.scalar(cfg.ksatValue), createRealizations) regolithThicknessHomogeneous = generalfunctions.areauniformBounds( 1.0, 3.5, cfg.areas, pcr.scalar(cfg.regolithThicknessHomogeneousValue), createRealizations) saturatedConductivityMetrePerDay = generalfunctions.mapuniformBounds( 25.0, 40.0, pcr.scalar(cfg.saturatedConductivityMetrePerDayValue), createRealizations) multiplierMaxStomatalConductance = generalfunctions.mapuniformBounds( 0.8, 1.1, pcr.scalar(cfg.multiplierMaxStomatalConductanceValue), createRealizations) if swapCatchments: regolithThicknessHomogeneous = generalfunctions.swapValuesOfTwoRegions( cfg.areas, regolithThicknessHomogeneous, True) self.d_randomparameters = randomparameters.RandomParameters( timeStepsToReportRqs, setOfVariablesToReport, maximumInterceptionCapacityPerLAI, ksat, regolithThicknessHomogeneous, saturatedConductivityMetrePerDay, multiplierMaxStomatalConductance) # class for exchange variables in initial and dynamic # introduced to make filtering possible self.d_exchangevariables = exchangevariables.ExchangeVariables( timeStepsToReportSome, setOfVariablesToReport, ) ################ # interception # ################ self.ldd = cfg.lddMap initialInterceptionStore = pcr.scalar(0.000001) leafAreaIndex = pcr.scalar(cfg.leafAreaIndexValue) if swapCatchments: leafAreaIndex = generalfunctions.swapValuesOfTwoRegions( cfg.areas, leafAreaIndex, True) gapFraction = pcr.exp( -0.5 * leafAreaIndex) # equation 40 in Brolsma et al 2010a maximumInterceptionStore = maximumInterceptionCapacityPerLAI * leafAreaIndex self.d_interceptionuptomaxstore = interceptionuptomaxstore.InterceptionUpToMaxStore( self.ldd, initialInterceptionStore, maximumInterceptionStore, gapFraction, self.timeStepDurationHours, timeStepsToReportSome, setOfVariablesToReport) ################# # surface store # ################# initialSurfaceStore = pcr.scalar(0.0) maxSurfaceStore = pcr.scalar(cfg.maxSurfaceStoreValue) self.d_surfaceStore = surfacestore.SurfaceStore( initialSurfaceStore, maxSurfaceStore, self.timeStepDurationHours, timeStepsToReportSome, setOfVariablesToReport) ################ # infiltration # ################ # N initialMoistureContentFraction taken from 1st July # DK # we do not use rts and Gs as input to calculate initial moisture fraction to avoid # problems when the initial regolith thickness is calibrated (it might be thinner than # initialMoistureThick -> problems!) # instead, we use initial moisture content fraction as input, read from disk, it is just calculated # by pcrcalc 'mergeInitialMoistureContentFraction=Gs000008.761/rts00008.761' # note that I also changed the name for the initial soil moisture as a fraction initialSoilMoistureFractionFromDisk = pcr.scalar( cfg.initialSoilMoistureFractionFromDiskValue) if swapCatchments: initialSoilMoistureFractionFromDisk = generalfunctions.swapValuesOfTwoRegions( cfg.areas, initialSoilMoistureFractionFromDisk, True) # initial soil moisture as a fraction should not be above soil porosity as a fraction, just a check soilPorosityFraction = pcr.scalar(cfg.soilPorosityFractionValue) if swapCatchments: soilPorosityFraction = generalfunctions.swapValuesOfTwoRegions( cfg.areas, soilPorosityFraction, True) initialSoilMoistureFraction = pcr.min( soilPorosityFraction, initialSoilMoistureFractionFromDisk) hf = pcr.scalar(-0.0000001) self.d_infiltrationgreenandampt = infiltrationgreenandampt.InfiltrationGreenAndAmpt( soilPorosityFraction, initialSoilMoistureFraction, ksat, hf, self.timeStepDurationHours, timeStepsToReportSome, setOfVariablesToReport) #################### # subsurface water # #################### demOfBedrockTopography = self.dem stream = pcr.boolean(cfg.streamValue) theSlope = pcr.slope(self.dem) regolithThickness = pcr.ifthenelse(stream, 0.01, regolithThicknessHomogeneous) self.multiplierWiltingPoint = pcr.scalar(1.0) limitingPointFraction = pcr.scalar(cfg.limitingPointFractionValue) if swapCatchments: limitingPointFraction = generalfunctions.swapValuesOfTwoRegions( cfg.areas, limitingPointFraction, True) mergeWiltingPointFractionFS = pcr.scalar( cfg.mergeWiltingPointFractionFSValue) if swapCatchments: mergeWiltingPointFractionFS = generalfunctions.swapValuesOfTwoRegions( cfg.areas, mergeWiltingPointFractionFS, True) wiltingPointFractionNotChecked = mergeWiltingPointFractionFS * self.multiplierWiltingPoint wiltingPointFraction = pcr.min(wiltingPointFractionNotChecked, limitingPointFraction) fieldCapacityFraction = pcr.scalar(cfg.fieldCapacityFractionValue) if swapCatchments: fieldCapacityFraction = generalfunctions.swapValuesOfTwoRegions( cfg.areas, fieldCapacityFraction, True) self.d_subsurfaceWaterOneLayer = subsurfacewateronelayer.SubsurfaceWaterOneLayer( self.ldd, demOfBedrockTopography, regolithThickness, initialSoilMoistureFraction, soilPorosityFraction, wiltingPointFraction, fieldCapacityFraction, limitingPointFraction, saturatedConductivityMetrePerDay, self.timeStepDurationHours, timeStepsToReportSome, setOfVariablesToReport) ########## # runoff # ########## self.d_runoffAccuthreshold = runoffaccuthreshold.RunoffAccuthreshold( self.ldd, self.timeStepDurationHours, timeStepsToReportRqs, setOfVariablesToReport) ###################### # evapotranspiration # ###################### albedo = pcr.scalar(cfg.albedoValue) if swapCatchments: albedo = generalfunctions.swapValuesOfTwoRegions( cfg.areas, albedo, True) maxStomatalConductance = pcr.scalar( cfg.maxStomatalConductanceValue) * multiplierMaxStomatalConductance if swapCatchments: maxStomatalConductance = generalfunctions.swapValuesOfTwoRegions( cfg.areas, maxStomatalConductance, True) vegetationHeight = pcr.scalar(cfg.vegetationHeightValue) if swapCatchments: vegetationHeight = generalfunctions.swapValuesOfTwoRegions( cfg.areas, vegetationHeight, True) self.d_evapotranspirationPenman = evapotranspirationpenman.EvapotranspirationPenman( self.timeStepDurationHours, albedo, maxStomatalConductance, vegetationHeight, leafAreaIndex, timeStepsToReportSome, setOfVariablesToReport)
def agriZone_hourlyEp_Sa_beta_frostSamax(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on LP - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa --> incorporation of beta function - Fa is based on storage in Sa - Fa is decreased in case of frozen soil - Code for ini-file: 11 """ # JarvisCoefficients.calcEp(self,k) # self.PotEvaporation = self.EpHour self.FrDur[k] = pcr.min(self.FrDur[k] + (self.Temperature) * self.dayDeg[k], 0) self.Ft = pcr.min( pcr.max( self.FrDur[k] / (self.FrDur1[k] - self.FrDur0[k]) - self.FrDur0[k] / (self.FrDur1[k] - self.FrDur0[k]), 0.1, ), 1, ) self.samax2 = self.samax[k] * pcr.scalar(self.catchArea) * self.Ft self.Qaadd = pcr.max(self.Sa_t[k] + self.Pe - self.samax2, 0) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) self.SaN = pcr.min(self.Sa[k] / self.samax2, 1) self.SuN = self.Su[k] / self.sumax[k] self.Ea1 = pcr.max((self.PotEvaporation - self.Ei), 0) * pcr.min( self.Sa[k] / (self.samax2 * self.LP[k]), 1 ) self.Qa1 = (self.Pe - self.Qaadd) * (1 - (1 - self.SaN) ** self.beta[k]) self.Fa1 = pcr.ifthenelse( self.SaN > 0, self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * pcr.exp((-self.decF[k] * (1 - self.SaN))), 0, ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Qa1 - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Qa = ( self.Qa1 + ( self.Qa1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Fa = ( self.Fa1 + ( self.Fa1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Ea = ( self.Ea1 + ( self.Ea1 / pcr.ifthenelse( self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1 ) ) * self.Sa_diff ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Ea - self.Fa - self.Qa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[k] = ( self.Pe - self.Ea - self.Qa - self.Qaadd - self.Fa - self.Sa[k] + self.Sa_t[k] ) self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa + self.Qaadd self.Fa_[k] = self.Fa self.Ft_[k] = self.Ft
def agriZone_hourlyEp_Sa_beta_frostSamax(self, k): """ - Potential evaporation is decreased by energy used for interception evaporation - Formula for evaporation based on LP - Outgoing fluxes are determined based on (value in previous timestep + inflow) and if this leads to negative storage, the outgoing fluxes are corrected to rato --> Eu is no longer taken into account for this correction - Qa u is determined from overflow from Sa --> incorporation of beta function - Fa is based on storage in Sa - Fa is decreased in case of frozen soil - Code for ini-file: 11 """ # JarvisCoefficients.calcEp(self,k) # self.PotEvaporation = self.EpHour self.FrDur[k] = pcr.min( self.FrDur[k] + (self.Temperature) * self.dayDeg[k], 0) self.Ft = pcr.min( pcr.max( self.FrDur[k] / (self.FrDur1[k] - self.FrDur0[k]) - self.FrDur0[k] / (self.FrDur1[k] - self.FrDur0[k]), 0.1, ), 1, ) self.samax2 = self.samax[k] * pcr.scalar(self.catchArea) * self.Ft self.Qaadd = pcr.max(self.Sa_t[k] + self.Pe - self.samax2, 0) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) self.SaN = pcr.min(self.Sa[k] / self.samax2, 1) self.SuN = self.Su[k] / self.sumax[k] self.Ea1 = pcr.max((self.PotEvaporation - self.Ei), 0) * pcr.min( self.Sa[k] / (self.samax2 * self.LP[k]), 1) self.Qa1 = (self.Pe - self.Qaadd) * (1 - (1 - self.SaN)**self.beta[k]) self.Fa1 = pcr.ifthenelse( self.SaN > 0, self.Fmin[k] + (self.Fmax[k] - self.Fmin[k]) * pcr.exp( (-self.decF[k] * (1 - self.SaN))), 0, ) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Qa1 - self.Fa1 - self.Ea1 self.Sa_diff = pcr.ifthenelse(self.Sa[k] < 0, self.Sa[k], 0) self.Qa = (self.Qa1 + (self.Qa1 / pcr.ifthenelse(self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1)) * self.Sa_diff) self.Fa = (self.Fa1 + (self.Fa1 / pcr.ifthenelse(self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1)) * self.Sa_diff) self.Ea = (self.Ea1 + (self.Ea1 / pcr.ifthenelse(self.Fa1 + self.Ea1 + self.Qa1 > 0, self.Fa1 + self.Ea1 + self.Qa1, 1)) * self.Sa_diff) self.Sa[k] = self.Sa_t[k] + (self.Pe - self.Qaadd) - self.Ea - self.Fa - self.Qa self.Sa[k] = pcr.ifthenelse(self.Sa[k] < 0, 0, self.Sa[k]) self.Sa_diff2 = pcr.ifthen(self.Sa[k] < 0, self.Sa[k]) self.wbSa_[k] = (self.Pe - self.Ea - self.Qa - self.Qaadd - self.Fa - self.Sa[k] + self.Sa_t[k]) self.Ea_[k] = self.Ea self.Qa_[k] = self.Qa + self.Qaadd self.Fa_[k] = self.Fa self.Ft_[k] = self.Ft
def __init__(self, clone_map_file,\ input_thickness_netcdf_file,\ input_thickness_var_name ,\ margat_aquifers,\ tmp_directory, landmask = None, arcdegree = True): object.__init__(self) # aquifer table from Margat and van der Gun self.margat_aquifers = margat_aquifers # clone map self.clone_map_file = clone_map_file self.clone_map_attr = vos.getMapAttributesALL(self.clone_map_file) if arcdegree == True: self.clone_map_attr['cellsize'] = round(self.clone_map_attr['cellsize'] * 360000.)/360000. xmin = self.clone_map_attr['xUL'] xmax = xmin + self.clone_map_attr['cols'] * self.clone_map_attr['cellsize'] ymax = self.clone_map_attr['yUL'] ymin = ymax - self.clone_map_attr['rows'] * self.clone_map_attr['cellsize'] pcr.setclone(self.clone_map_file) # temporary directory self.tmp_directory = tmp_directory # thickness approximation (unit: m, file in netcdf with variable name = average self.approx_thick = vos.netcdf2PCRobjCloneWithoutTime(input_thickness_netcdf_file,\ input_thickness_var_name,\ self.clone_map_file) # set minimum value to 0.1 mm self.approx_thick = pcr.max(0.0001, self.approx_thick) # rasterize the shape file # - # save current directory and move to temporary directory current_dir = str(os.getcwd()+"/") os.chdir(str(self.tmp_directory)) # cmd_line = 'gdal_rasterize -a MARGAT ' # layer name = MARGAT cmd_line += '-te '+str(xmin)+' '+str(ymin)+' '+str(xmax)+' '+str(ymax)+ ' ' cmd_line += '-tr '+str(self.clone_map_attr['cellsize'])+' '+str(self.clone_map_attr['cellsize'])+' ' cmd_line += str(margat_aquifers['shapefile'])+' ' cmd_line += 'tmp.tif' print(cmd_line); os.system(cmd_line) # # make it nomial cmd_line = 'pcrcalc tmp.map = "nominal(tmp.tif)"' print(cmd_line); os.system(cmd_line) # # make sure that the clone map is correct cmd_line = 'mapattr -c '+str(self.clone_map_file)+' tmp.map' print(cmd_line); os.system(cmd_line) # # read the map self.margat_aquifer_map = pcr.nominal(pcr.readmap("tmp.map")) # # clean temporary directory and return to the original directory vos.clean_tmp_dir(self.tmp_directory) os.chdir(current_dir) # extend the extent of each aquifer self.margat_aquifer_map = pcr.cover(self.margat_aquifer_map, pcr.windowmajority(self.margat_aquifer_map, 1.25)) # assign aquifer thickness, unit: m (lookuptable operation) self.margat_aquifer_thickness = pcr.lookupscalar(margat_aquifers['txt_table'], self.margat_aquifer_map) self.margat_aquifer_thickness = pcr.ifthen(self.margat_aquifer_thickness > 0., \ self.margat_aquifer_thickness) #~ pcr.report(self.margat_aquifer_thickness,"thick.map"); os.system("aguila thick.map") # aquifer map self.margat_aquifer_map = pcr.ifthen(self.margat_aquifer_thickness > 0., self.margat_aquifer_map) # looping per aquifer: cirrecting or rescaling aquifer_ids = np.unique(pcr.pcr2numpy(pcr.scalar(self.margat_aquifer_map), vos.MV)) aquifer_ids = aquifer_ids[aquifer_ids > 0] aquifer_ids = aquifer_ids[aquifer_ids < 10000] self.rescaled_thickness = None for id in aquifer_ids: rescaled_thickness = self.correction_per_aquifer(id) try: self.rescaled_thickness = pcr.cover(self.rescaled_thickness, rescaled_thickness) except: self.rescaled_thickness = rescaled_thickness # integrating ln_aquifer_thickness = self.mapFilling( pcr.ln(self.rescaled_thickness), pcr.ln(self.approx_thick) ) self.aquifer_thickness = pcr.exp(ln_aquifer_thickness) #~ pcr.report(self.aquifer_thickness,"thick.map"); os.system("aguila thick.map") # cropping only in the landmask region if landmask == None: landmask = self.clone_map_file self.landmask = pcr.defined(vos.readPCRmapClone(landmask,self.clone_map_file,self.tmp_directory)) #~ pcr.report(self.landmask,"test.map"); os.system("aguila test.map") self.aquifer_thickness = pcr.ifthen(self.landmask, self.aquifer_thickness)
def tanh(x): #-returns the hyperbolic tangent of a PCRaster map as (exp(2x)-1)/(exp(2x)+1) return (pcr.exp(2*x)-1.)/(pcr.exp(2*x)+1.)
def integralLogisticFunction(self, x): #-returns a tupple of two values holding the integral of the logistic functions # of (x) and (-x) logInt = pcr.ln(pcr.exp(-x) + 1) return logInt, x + logInt
def dynamic(self): """ *Required* This is where all the time dependent functions are executed. Time dependent output should also be saved here. """ # print 'useETPdata' , self.UseETPdata # Put the W3RA here. Stuff from W3RA_timestep_model.m # read meteo from file self.logger.debug("Running for: " + str(self.currentdatetime)) self.PRECIP = pcr.cover( self.wf_readmap(self.PRECIP_mapstack, 0.0), pcr.scalar(0.0) ) # mm if self.UseETPdata == 1: self.TDAY = pcr.cover( self.wf_readmap(self.TDAY_mapstack, 10.0), pcr.scalar(10.0) ) # T in degC self.EPOT = pcr.cover( self.wf_readmap(self.EPOT_mapstack, 0.0), pcr.scalar(0.0) ) # mm self.WINDSPEED = pcr.cover( self.wf_readmap(self.WINDSPEED_mapstack, default=1.0), pcr.scalar(1.0) ) self.AIRPRESS = pcr.cover( self.wf_readmap(self.AIRPRESS_mapstack, default=980.0), pcr.scalar(980.0), ) # print "Using climatology for wind, air pressure and albedo." elif self.UseETPdata == 0: self.TMIN = pcr.cover( self.wf_readmap(self.TMIN_mapstack, 10.0), pcr.scalar(10.0) ) # T in degC self.TMAX = pcr.cover( self.wf_readmap(self.TMAX_mapstack, 10.0), pcr.scalar(10.0) ) # T in degC self.RAD = pcr.cover( self.wf_readmap(self.RAD_mapstack, 10.0), pcr.scalar(10.0) ) # W m-2 s-1 self.WINDSPEED = pcr.cover( self.wf_readmap(self.WINDSPEED_mapstack, 10.0), pcr.scalar(10.0) ) # ms-1 self.AIRPRESS = pcr.cover( self.wf_readmap(self.AIRPRESS_mapstack, 10.0), pcr.scalar(10.0) ) # Pa self.ALBEDO = pcr.cover( self.wf_readmapClimatology(self.ALBEDO_mapstack, default=0.1), pcr.scalar(0.1), ) self.wf_multparameters() doy = self.currentdatetime.timetuple().tm_yday # conversion daylength pcr.setglobaloption("radians") m = pcr.scalar(1) - pcr.tan( (self.latitude * pcr.scalar(math.pi) / pcr.scalar(180)) ) * pcr.tan( ( (pcr.scalar(23.439) * pcr.scalar(math.pi) / pcr.scalar(180)) * pcr.cos( pcr.scalar(2) * pcr.scalar(math.pi) * (doy + pcr.scalar(9)) / pcr.scalar(365.25) ) ) ) self.fday = pcr.min( pcr.max( pcr.scalar(0.02), pcr.scalar( pcr.acos( pcr.scalar(1) - pcr.min(pcr.max(pcr.scalar(0), m), pcr.scalar(2)) ) ) / pcr.scalar(math.pi), ), pcr.scalar(1), ) # fraction daylength # Assign forcing and estimate effective meteorological variables Pg = self.PRECIP # mm if self.UseETPdata == 1: Ta = self.TDAY # T in degC T24 = self.TDAY # T in degC elif self.UseETPdata == 0: Rg = pcr.max( self.RAD, pcr.scalar(0.0001) ) # already in W m-2 s-1; set minimum of 0.01 to avoid numerical problems Ta = self.TMIN + pcr.scalar(0.75) * (self.TMAX - self.TMIN) # T in degC T24 = self.TMIN + pcr.scalar(0.5) * (self.TMAX - self.TMIN) # T in degC pex = pcr.min( pcr.scalar(17.27) * (self.TMIN) / (pcr.scalar(237.3) + self.TMIN), pcr.scalar(10), ) # T in degC pe = pcr.min( pcr.scalar(610.8) * (pcr.exp(pex)), pcr.scalar(10000.0) ) # Mean actual vapour pressure, from dewpoint temperature # rescale factor because windspeed climatology is at 2m WindFactor = 1.0 # u2 = pcr.scalar(WindFactor)*self.WINDSPEED*(pcr.scalar(1)-(pcr.scalar(1)-self.fday)*scalar(0.25))/self.fday self.u2 = ( pcr.scalar(WindFactor) * self.WINDSPEED * (pcr.scalar(1) - (pcr.scalar(1) - self.fday) * pcr.scalar(0.25)) / self.fday ) pair = self.AIRPRESS # already in Pa # diagnostic equations self.LAI1 = self.SLA1 * self.Mleaf1 # (5.3) self.LAI2 = self.SLA2 * self.Mleaf2 # (5.3) fveg1 = pcr.max(1 - pcr.exp(-self.LAI1 / self.LAIref1), 0.000001) # (5.3) fveg2 = pcr.max(1 - pcr.exp(-self.LAI2 / self.LAIref2), 0.000001) # Vc = pcr.max(0,EVI-0.07)/fveg fsoil1 = 1 - fveg1 fsoil2 = 1 - fveg2 w01 = self.S01 / self.S0FC1 # (2.1) w02 = self.S02 / self.S0FC2 ws1 = self.Ss1 / self.SsFC1 # (2.1) ws2 = self.Ss2 / self.SsFC2 wd1 = self.Sd1 / self.SdFC1 # (2.1) wd2 = self.Sd2 / self.SdFC2 # (2.1) TotSnow1 = self.FreeWater1 + self.DrySnow1 TotSnow2 = self.FreeWater2 + self.DrySnow2 wSnow1 = self.FreeWater1 / (TotSnow1 + 1e-5) wSnow2 = self.FreeWater2 / (TotSnow2 + 1e-5) # Spatialise catchment fractions Sgfree = pcr.max(self.Sg, 0.0) # JS: Not sure if this is translated properly.... # for i=1:par.Nhru fwater1 = pcr.min(0.005, (0.007 * self.Sr ** 0.75)) fwater2 = pcr.min(0.005, (0.007 * self.Sr ** 0.75)) fsat1 = pcr.min( 1.0, pcr.max(pcr.min(0.005, 0.007 * self.Sr ** 0.75), Sgfree / self.Sgref) ) fsat2 = pcr.min( 1.0, pcr.max(pcr.min(0.005, 0.007 * self.Sr ** 0.75), Sgfree / self.Sgref) ) Sghru1 = self.Sg Sghru2 = self.Sg # CALCULATION OF PET # Conversions and coefficients (3.1) pesx = pcr.min( (pcr.scalar(17.27) * Ta / (pcr.scalar(237.3) + Ta)), pcr.scalar(10) ) pes = pcr.min( pcr.scalar((pcr.scalar(610.8)) * pcr.exp(pesx)), pcr.scalar(10000) ) # saturated vapour pressure # fRH = pe/pes # relative air humidity -------------- check cRE = 0.03449 + 4.27e-5 * Ta # Caero = self.fday*0.176*(1+Ta/209.1)*(pair-0.417*pe)*(1-fRH) -------------- check # keps = 1.4e-3*((Ta/187)**2+Ta/107+1)*(6.36*pair+pe)/pes ga1 = self.ku2_1 * self.u2 ga2 = self.ku2_2 * self.u2 if self.UseETPdata == 1: self.E01 = pcr.max(self.EPOT, 0) self.E02 = pcr.max(self.EPOT, 0) keps = ( 0.655e-3 * pair / pes ) # See Appendix A3 (http://www.clw.csiro.au/publications/waterforahealthycountry/2010/wfhc-aus-water-resources-assessment-system.pdf) -------------------------------- check! elif self.UseETPdata == 0: # Aerodynamic conductance (3.7) ns_alb = self.ALBEDO Rgeff = Rg / self.fday # shortwave radiation balance (3.2) # alb_veg = 0.452*Vc # alb_soil = alb_wet+(alb_dry-alb_wet)*exp(-w0/w0ref_alb) # new equations for snow albedo alb_snow1 = 0.65 - 0.2 * wSnow1 # assumed; ideally some lit research needed alb_snow2 = 0.65 - 0.2 * wSnow2 fsnow1 = pcr.min( 1.0, 0.05 * TotSnow1 ) # assumed; ideally some lit research needed fsnow2 = pcr.min(1.0, 0.05 * TotSnow2) # alb = fveg*alb_veg+(fsoil-fsnow)*alb_soil +fsnow*alb_snow # alb = albedo alb1 = (1 - fsnow1) * ns_alb + fsnow1 * alb_snow1 alb2 = (1 - fsnow2) * ns_alb + fsnow2 * alb_snow2 RSn1 = (1 - alb1) * Rgeff RSn2 = (1 - alb2) * Rgeff # long wave radiation balance (3.3 to 3.5) StefBolz = 5.67e-8 Tkelv = Ta + 273.16 self.RLin = (0.65 * (pe / Tkelv) ** 0.14) * StefBolz * Tkelv ** 4 # (3.3) RLout = StefBolz * Tkelv ** 4.0 # (3.4) self.RLn = self.RLin - RLout self.fGR1 = self.Gfrac_max1 * (1 - pcr.exp(-fsoil1 / self.fvegref_G1)) self.fGR2 = self.Gfrac_max2 * ( 1 - pcr.exp(-fsoil2 / self.fvegref_G2) ) # (3.5) self.Rneff1 = (RSn1 + self.RLn) * (1 - self.fGR1) self.Rneff2 = (RSn2 + self.RLn) * (1 - self.fGR2) fRH = pe / pes # relative air humidity Caero = ( self.fday * 0.176 * (1 + Ta / 209.1) * (pair - 0.417 * pe) * (1 - fRH) ) # -------------- check keps = 1.4e-3 * ((Ta / 187) ** 2 + Ta / 107 + 1) * (6.36 * pair + pe) / pes # Potential evaporation kalpha1 = 1 + Caero * ga1 / self.Rneff1 kalpha2 = 1 + Caero * ga2 / self.Rneff2 self.E01 = cRE * (1 / (1 + keps)) * kalpha1 * self.Rneff1 * self.fday self.E02 = cRE * (1 / (1 + keps)) * kalpha2 * self.Rneff2 * self.fday self.E01 = pcr.max(self.E01, 0) self.E02 = pcr.max(self.E02, 0) # CALCULATION OF ET FLUXES AND ROOT WATER UPTAKE # Root water uptake constraint (4.4) Usmax1 = pcr.max( 0, self.Us01 * pcr.min(1, ws1 / self.wslimU1) ) ##0-waarden omdat ws1 bevat 0-waarden (zie regel 116) Usmax2 = pcr.max( 0, self.Us02 * pcr.min(1, ws2 / self.wslimU2) ) ##0-waarden omdat ws2 bevat 0-waarden (zie regel 117) Udmax1 = pcr.max( 0, self.Ud01 * pcr.min(1, wd1 / self.wdlimU1) ) ##0-waarden omdat wd1 bevat 0-waarden (zie regel 118) Udmax2 = pcr.max( 0, self.Ud02 * pcr.min(1, wd2 / self.wdlimU2) ) ##0-waarden omdat wd2 bevat 0-waarden (zie regel 119) # U0max = pcr.max(0, Us0*min(1,w0/wslimU)) U0max1 = pcr.scalar(0) U0max2 = pcr.scalar(0) Utot1 = pcr.max(Usmax1, pcr.max(Udmax1, U0max1)) Utot2 = pcr.max(Usmax2, pcr.max(Udmax2, U0max2)) # Maximum transpiration (4.3) Gsmax1 = self.cGsmax1 * self.Vc1 gs1 = fveg1 * Gsmax1 ft1 = 1 / (1 + (keps / (1 + keps)) * ga1 / gs1) Etmax1 = ft1 * self.E01 Gsmax2 = self.cGsmax2 * self.Vc2 gs2 = fveg2 * Gsmax2 ft2 = 1 / (1 + (keps / (1 + keps)) * ga2 / gs2) Etmax2 = ft2 * self.E02 # Actual transpiration (4.1) Et1 = pcr.min(Utot1, Etmax1) Et2 = pcr.min(Utot2, Etmax2) # # Root water uptake distribution (2.3) U01 = pcr.max( pcr.min((U0max1 / (U0max1 + Usmax1 + Udmax1)) * Et1, self.S01 - 1e-2), 0 ) Us1 = pcr.max( pcr.min((Usmax1 / (U0max1 + Usmax1 + Udmax1)) * Et1, self.Ss1 - 1e-2), 0 ) Ud1 = pcr.max( pcr.min((Udmax1 / (U0max1 + Usmax1 + Udmax1)) * Et1, self.Sd1 - 1e-2), 0 ) Et1 = U01 + Us1 + Ud1 # to ensure mass balance U02 = pcr.max( pcr.min((U0max2 / (U0max2 + Usmax2 + Udmax2)) * Et2, self.S02 - 1e-2), 0 ) Us2 = pcr.max( pcr.min((Usmax2 / (U0max2 + Usmax2 + Udmax2)) * Et2, self.Ss2 - 1e-2), 0 ) Ud2 = pcr.max( pcr.min((Udmax2 / (U0max2 + Usmax2 + Udmax2)) * Et2, self.Sd2 - 1e-2), 0 ) Et2 = U02 + Us2 + Ud2 # Soil evaporation (4.5) self.S01 = pcr.max(0, self.S01 - U01) self.S02 = pcr.max(0, self.S02 - U02) w01 = self.S01 / self.S0FC1 # (2.1) w02 = self.S02 / self.S0FC2 # (2.1) fsoilE1 = self.FsoilEmax1 * pcr.min(1, w01 / self.w0limE1) fsoilE2 = self.FsoilEmax2 * pcr.min(1, w02 / self.w0limE2) Es1 = pcr.max( 0, pcr.min(((1 - fsat1) * fsoilE1 * (self.E01 - Et1)), self.S01 - 1e-2) ) Es2 = pcr.max( 0, pcr.min(((1 - fsat2) * fsoilE2 * (self.E02 - Et2)), self.S02 - 1e-2) ) # Groundwater evaporation (4.6) Eg1 = pcr.min((fsat1 - fwater1) * self.FsoilEmax1 * (self.E01 - Et1), Sghru1) Eg2 = pcr.min((fsat2 - fwater2) * self.FsoilEmax2 * (self.E02 - Et2), Sghru2) # Open water evaporation (4.7) Er1 = pcr.min(fwater1 * self.FwaterE1 * pcr.max(0, self.E01 - Et1), self.Sr) Er2 = pcr.min(fwater2 * self.FwaterE2 * pcr.max(0, self.E02 - Et2), self.Sr) # Rainfall interception evaporation (4.2) Sveg1 = self.S_sls1 * self.LAI1 fER1 = self.ER_frac_ref1 * fveg1 Pwet1 = -pcr.ln(1 - fER1 / fveg1) * Sveg1 / fER1 Ei1 = pcr.scalar(Pg < Pwet1) * fveg1 * Pg + pcr.scalar(Pg >= Pwet1) * ( fveg1 * Pwet1 + fER1 * (Pg - Pwet1) ) Sveg2 = self.S_sls2 * self.LAI2 fER2 = self.ER_frac_ref2 * fveg2 Pwet2 = -pcr.ln(1 - fER2 / fveg2) * Sveg2 / fER2 Ei2 = pcr.scalar(Pg < Pwet2) * fveg2 * Pg + pcr.scalar(Pg >= Pwet2) * ( fveg2 * Pwet2 + fER2 * (Pg - Pwet2) ) self.EACT1 = (Et1 + Es1 + Eg1 + Er1 + Ei1) * self.Fhru1 self.EACT2 = (Et2 + Es2 + Eg2 + Er2 + Ei2) * self.Fhru2 self.EACT = self.EACT1 + self.EACT2 # HBV snow routine # Matlab: function [FreeWater,DrySnow,InSoil]=snow_submodel(Precipitation,Temperature,FreeWater,DrySnow) # derived from HBV-96 shared by Jaap Schellekens (Deltares) in May 2011 # original in PCraster, adapted to Matlab by Albert van Dijk # HBV snow routine Pn1 = Pg - Ei1 Pn2 = Pg - Ei2 Precipitation1 = Pn1 Precipitation2 = Pn2 # Snow routine parameters # parameters # TODO: Check this, not sure if this works....... x = pcr.scalar(Pg) Cfmax1 = 0.6 * 3.75653 * pcr.scalar(x >= 0) Cfmax2 = 3.75653 * pcr.scalar(x >= 0) TT1 = -1.41934 * pcr.scalar( x >= 0 ) # critical temperature for snowmelt and refreezing TT2 = -1.41934 * pcr.scalar(x >= 0) TTI1 = 1.00000 * pcr.scalar( x >= 0 ) # defines interval in which precipitation falls as rainfall and snowfall TTI2 = 1.00000 * pcr.scalar(x >= 0) CFR1 = 0.05000 * pcr.scalar( x >= 0 ) # refreezing efficiency constant in refreezing of freewater in snow CFR2 = 0.05000 * pcr.scalar(x >= 0) WHC1 = 0.10000 * pcr.scalar(x >= 0) WHC2 = 0.10000 * pcr.scalar(x >= 0) # Partitioning into fractions rain and snow Temperature = T24 # Dimmie, let op: tijdelijke regel!! RainFrac1 = pcr.max(0, pcr.min((Temperature - (TT1 - TTI1 / 2)) / TTI1, 1)) RainFrac2 = pcr.max(0, pcr.min((Temperature - (TT2 - TTI2 / 2)) / TTI2, 1)) SnowFrac1 = 1 - RainFrac1 # fraction of precipitation which falls as snow SnowFrac2 = 1 - RainFrac2 # Snowfall/melt calculations SnowFall1 = SnowFrac1 * Precipitation1 # snowfall depth SnowFall2 = SnowFrac2 * Precipitation2 RainFall1 = RainFrac1 * Precipitation1 # rainfall depth RainFall2 = RainFrac2 * Precipitation2 PotSnowMelt1 = Cfmax1 * pcr.max( 0, Temperature - TT1 ) # Potential snow melt, based on temperature PotSnowMelt2 = Cfmax2 * pcr.max(0, Temperature - TT2) PotRefreezing1 = ( Cfmax1 * CFR1 * pcr.max(TT1 - Temperature, 0) ) # Potential refreezing, based on temperature PotRefreezing2 = Cfmax2 * CFR2 * pcr.max(TT2 - Temperature, 0) Refreezing1 = pcr.min(PotRefreezing1, self.FreeWater1) # actual refreezing Refreezing2 = pcr.min(PotRefreezing2, self.FreeWater2) SnowMelt1 = pcr.min(PotSnowMelt1, self.DrySnow1) # actual snow melt SnowMelt2 = pcr.min(PotSnowMelt2, self.DrySnow2) self.DrySnow1 = ( self.DrySnow1 + SnowFall1 + Refreezing1 - SnowMelt1 ) # dry snow content self.DrySnow2 = self.DrySnow2 + SnowFall2 + Refreezing2 - SnowMelt2 self.FreeWater1 = self.FreeWater1 - Refreezing1 # free water content in snow self.FreeWater2 = self.FreeWater2 - Refreezing2 MaxFreeWater1 = self.DrySnow1 * WHC1 MaxFreeWater2 = self.DrySnow2 * WHC2 self.FreeWater1 = self.FreeWater1 + SnowMelt1 + RainFall1 self.FreeWater2 = self.FreeWater2 + SnowMelt2 + RainFall2 InSoil1 = pcr.max( self.FreeWater1 - MaxFreeWater1, 0 ) # abundant water in snow pack which goes into soil InSoil2 = pcr.max(self.FreeWater2 - MaxFreeWater2, 0) self.FreeWater1 = self.FreeWater1 - InSoil1 self.FreeWater2 = self.FreeWater2 - InSoil2 # End of Snow Module # CALCULATION OF WATER BALANCES # surface water fluxes (2.2) NetInSoil1 = pcr.max(0, (InSoil1 - self.InitLoss1)) NetInSoil2 = pcr.max(0, (InSoil2 - self.InitLoss2)) Rhof1 = (1 - fsat1) * (NetInSoil1 / (NetInSoil1 + self.PrefR1)) * NetInSoil1 Rhof2 = (1 - fsat2) * (NetInSoil2 / (NetInSoil2 + self.PrefR2)) * NetInSoil2 Rsof1 = fsat1 * NetInSoil1 Rsof2 = fsat2 * NetInSoil2 QR1 = Rhof1 + Rsof1 QR2 = Rhof2 + Rsof2 I1 = InSoil1 - QR1 I2 = InSoil2 - QR2 # SOIL WATER BALANCES (2.1 & 2.4) # Topsoil water balance (S0) self.S01 = self.S01 + I1 - Es1 - U01 self.S02 = self.S02 + I2 - Es2 - U02 SzFC1 = self.S0FC1 SzFC2 = self.S0FC2 Sz1 = self.S01 Sz2 = self.S02 wz1 = pcr.max(1e-2, Sz1) / SzFC1 wz2 = pcr.max(1e-2, Sz2) / SzFC2 self.TMP = SzFC1 # TODO: Check if this works fD1 = pcr.scalar(wz1 > 1) * pcr.max(self.FdrainFC1, 1 - 1 / wz1) + pcr.scalar( wz1 <= 1 ) * self.FdrainFC1 * pcr.exp(self.beta1 * pcr.scalar(wz1 - 1)) fD2 = pcr.scalar(wz2 > 1) * pcr.max(self.FdrainFC2, 1 - 1 / wz2) + pcr.scalar( wz2 <= 1 ) * self.FdrainFC2 * pcr.exp(self.beta2 * pcr.scalar(wz2 - 1)) Dz1 = pcr.max(0, pcr.min(fD1 * Sz1, Sz1 - 1e-2)) Dz2 = pcr.max(0, pcr.min(fD2 * Sz2, Sz2 - 1e-2)) D01 = Dz1 D02 = Dz2 self.S01 = self.S01 - D01 self.S02 = self.S02 - D02 # Shallow root zone water balance (Ss) self.Ss1 = self.Ss1 + D01 - Us1 self.Ss2 = self.Ss2 + D02 - Us2 SzFC1 = self.SsFC1 SzFC2 = self.SsFC2 Sz1 = self.Ss1 Sz2 = self.Ss2 wz1 = pcr.max(1e-2, Sz1) / SzFC1 wz2 = pcr.max(1e-2, Sz2) / SzFC2 fD1 = pcr.scalar(wz1 > 1) * pcr.max(self.FdrainFC1, 1 - 1 / wz1) + pcr.scalar( wz1 <= 1 ) * self.FdrainFC1 * pcr.exp(self.beta1 * pcr.scalar(wz1 - 1)) fD2 = pcr.scalar(wz2 > 1) * pcr.max(self.FdrainFC2, 1 - 1 / wz2) + pcr.scalar( wz2 <= 1 ) * self.FdrainFC2 * pcr.exp(self.beta2 * pcr.scalar(wz2 - 1)) Dz1 = pcr.max(0, pcr.min(fD1 * Sz1, Sz1 - 1e-2)) Dz2 = pcr.max(0, pcr.min(fD2 * Sz2, Sz2 - 1e-2)) Ds1 = Dz1 Ds2 = Dz2 self.Ss1 = self.Ss1 - Ds1 self.Ss2 = self.Ss2 - Ds2 # Deep root zone water balance (Sd) (2.6) self.Sd1 = self.Sd1 + Ds1 - Ud1 self.Sd2 = self.Sd2 + Ds2 - Ud2 SzFC1 = self.SdFC1 SzFC2 = self.SdFC2 Sz1 = self.Sd1 Sz2 = self.Sd2 wz1 = pcr.max(1e-2, Sz1) / SzFC1 wz2 = pcr.max(1e-2, Sz2) / SzFC2 fD1 = pcr.scalar(wz1 > 1) * pcr.max(self.FdrainFC1, 1 - 1 / wz1) + pcr.scalar( wz1 <= 1 ) * self.FdrainFC1 * pcr.exp(self.beta1 * pcr.scalar(wz1 - 1)) fD2 = pcr.scalar(wz2 > 1) * pcr.max(self.FdrainFC2, 1 - 1 / wz2) + pcr.scalar( wz2 <= 1 ) * self.FdrainFC2 * pcr.exp(self.beta2 * pcr.scalar(wz2 - 1)) Dz1 = pcr.max(0, pcr.min(fD1 * Sz1, Sz1 - 1e-2)) Dz2 = pcr.max(0, pcr.min(fD2 * Sz2, Sz2 - 1e-2)) Dd1 = Dz1 Dd2 = Dz2 self.Sd1 = self.Sd1 - Dd1 self.Sd2 = self.Sd2 - Dd2 Y1 = pcr.min( self.Fgw_conn1 * pcr.max(0, self.wdlimU1 * self.SdFC1 - self.Sd1), Sghru1 - Eg1, ) Y2 = pcr.min( self.Fgw_conn2 * pcr.max(0, self.wdlimU2 * self.SdFC2 - self.Sd2), Sghru2 - Eg2, ) # Y = Fgw_conn.*max(0,wdlimU.*SdFC-Sd); #nog matlab script self.Sd1 = self.Sd1 + Y1 self.Sd2 = self.Sd2 + Y2 # CATCHMENT WATER BALANCE # Groundwater store water balance (Sg) (2.5) NetGf = (self.Fhru1 * (Dd1 - Eg1 - Y1)) + (self.Fhru2 * (Dd2 - Eg2 - Y2)) self.Sg = self.Sg + NetGf Sgfree = pcr.max(self.Sg, 0) Qg = pcr.min(Sgfree, (1 - pcr.exp(-self.K_gw)) * Sgfree) self.Sg = self.Sg - Qg # Surface water store water balance (Sr) (2.7) self.Sr = self.Sr + (self.Fhru1 * (QR1 - Er1)) + (self.Fhru2 * (QR2 - Er2)) + Qg self.Qtot = pcr.min(self.Sr, (1 - pcr.exp(-self.K_rout)) * self.Sr) self.Sr = self.Sr - self.Qtot # VEGETATION ADJUSTMENT (5) fveq1 = ( (1 / pcr.max((self.E01 / Utot1) - 1, 1e-3)) * (keps / (1 + keps)) * (ga1 / Gsmax1) ) fveq2 = ( (1 / pcr.max((self.E02 / Utot2) - 1, 1e-3)) * (keps / (1 + keps)) * (ga2 / Gsmax2) ) fvmax1 = 1 - pcr.exp(-self.LAImax1 / self.LAIref1) fvmax2 = 1 - pcr.exp(-self.LAImax2 / self.LAIref2) fveq1 = pcr.min(fveq1, fvmax1) fveq2 = pcr.min(fveq2, fvmax2) dMleaf1 = -pcr.ln(1 - fveq1) * self.LAIref1 / self.SLA1 - self.Mleaf1 dMleaf2 = -pcr.ln(1 - fveq2) * self.LAIref2 / self.SLA2 - self.Mleaf2 # Mleafnet1 = dMleaf1 * (dMleaf1/self.Tgrow1) + dMleaf1 * dMleaf1/self.Tsenc1 # Mleafnet2 = dMleaf2 * (dMleaf1/self.Tgrow2) + dMleaf2 * dMleaf2/self.Tsenc2 Mleafnet1 = ( pcr.scalar(dMleaf1 > 0) * (dMleaf1 / self.Tgrow1) + pcr.scalar(dMleaf1 < 0) * dMleaf1 / self.Tsenc1 ) Mleafnet2 = ( pcr.scalar(dMleaf2 > 0) * (dMleaf2 / self.Tgrow2) + pcr.scalar(dMleaf2 < 0) * dMleaf2 / self.Tsenc2 ) self.Mleaf1 = self.Mleaf1 + Mleafnet1 self.Mleaf2 = self.Mleaf2 + Mleafnet2 self.LAI1 = self.SLA1 * self.Mleaf1 # (5.3) self.LAI2 = self.SLA2 * self.Mleaf2 # Updating diagnostics self.LAI1 = self.SLA1 * self.Mleaf1 # (5.3) self.LAI2 = self.SLA2 * self.Mleaf2 fveg1 = 1 - pcr.exp(-self.LAI1 / self.LAIref1) # (5.3) fveg2 = 1 - pcr.exp(-self.LAI2 / self.LAIref2) fsoil1 = 1 - fveg1 fsoil2 = 1 - fveg2 w01 = self.S01 / self.S0FC1 # (2.1) w02 = self.S02 / self.S0FC2 ws1 = self.Ss1 / self.SsFC1 # (2.1) ws2 = self.Ss2 / self.SsFC2 wd1 = self.Sd1 / self.SdFC1 # (2.1) wd2 = self.Sd2 / self.SdFC2
def dynamic(self): """ *Required* This is where all the time dependent functions are executed. Time dependent output should also be saved here. """ self.wf_updateparameters() # Get the date as a Python datetime object and as the day of the year (DOY): used for cropping calendar and daylength. self.date = datetime.utcfromtimestamp( self.wf_supplyStartTime()) + dt.timedelta(self.currentTimeStep() - 1) self.enddate = datetime.utcfromtimestamp(self.wf_supplyEndTime( )) # wf_supplyEndTime() in wflow_dyamicframework? todo DOY = self.wf_supplyJulianDOY() # Some Boolean PCRaster variables to check if crop is still developing, in terms of thermal time/phenology: TSUM_not_Finished = self.TSUM <= self.TTSUM DVS_not_Finished = self.DVS <= 2.01 Not_Finished = TSUM_not_Finished # Start calculating the accumulated preciptation from a certain date on (defined by RainSumStart_Month, RainSumStart_Day), # to judge when there's enough water for rice crop establishment. if (self.date.month == self.RainSumStart_Month and self.date.day == self.RainSumStart_Day): Calc_RainSum = True self.PSUM += self.RAIN + TINY else: Calc_RainSum = False # Check whether the precipitation sum is positive WeveGotRain = self.PSUM > 0.0 # Check whether the precipitation sum is still below the threshhold for crop establishment (and hence calculation of the sum should still proceed): NotEnoughRainYet = self.PSUM <= self.RainSumReq EnoughRain = self.PSUM >= self.RainSumReq KeepAddingRain = WeveGotRain & NotEnoughRainYet # The first season is defined here as starting on November 1. The 2md and 3rd season are following the 1st with break periods of <self.Pausedays> days, # to account for the time that the farmer needs for rice harvesting and crop establishment. # self.Season += pcr.ifthenelse(Calc_RainSum, self.ricemask, 0.) FirstSeason = self.Season == 1 SecondSeason = self.Season == 2 ThirdSeason = self.Season == 3 self.Season += pcr.ifthenelse( EnoughRain, self.ricemask, 0.0) # beware, this variable is also modified in another equation # Add rain when the precipitation sum is positive but still below the threshold for crop establishment, reset to 0. when this is no longer the case. self.PSUM = (self.PSUM + pcr.ifthenelse(KeepAddingRain, self.RAIN, 0.0) ) * pcr.ifthenelse(KeepAddingRain, pcr.scalar(1.0), 0.0) # Initializing crop harvest: # If a fixed planting and a fixed harvest date are forced for the whole catchment: if self.CropStartDOY > -1: if self.HarvestDAP > 0: HarvNow = DOY >= (self.CropStartDOY + self.HarvestDAP) print( "Warning: harvest date read from ini file, not from Crop Profile map..." ) elif self.HarvestDAP == 0: HarvNow = Not_Finished == False print( "Harvest date not specified; crop harvest at crop maturity" ) else: print( "Crop harvest not initialized, found strange values in ini file... CTRL + C to exit..." ) time.sleep(100) CropHarvNow = HarvNow & self.ricemask_BOOL # Initializing crop growth, optionally from a single start day (CropStartDOY in the ini file), # but normally from a crop profile forcing variable. StartNow = DOY == self.CropStartDOY CropStartNow = StartNow & self.ricemask_BOOL CropStartNow_scalar = pcr.scalar(CropStartNow) Started = self.STARTED > 0 CropStarted = Started & self.ricemask_BOOL self.STARTED = (self.STARTED + CropStartNow_scalar + pcr.scalar(CropStarted)) * pcr.ifthenelse( CropHarvNow, pcr.scalar(0.0), 1.0) print( "Warning: using start date from ini file, not read from Crop Profile..." ) elif self.CropStartDOY == -1: if self.AutoStartStop == False: Started = self.STARTED > 0.0 if self.HarvestDAP == 0: crpprfl_eq_zero = self.CRPST == 0.0 CropHarvNow = Started & crpprfl_eq_zero & self.ricemask_BOOL elif self.HarvestDAP > 0: HarvNow = self.STARTED == self.HarvestDAP CropHarvNow = HarvNow & self.ricemask_BOOL print("Start date read from Crop Profile...") # Two auxilliary variables: CRPST_gt_0 = self.CRPST > 0.0 CRPST_eq_STARTED = self.CRPST == self.STARTED CropStartNow = CRPST_gt_0 & CRPST_eq_STARTED & self.ricemask_BOOL CropStarted = Started & self.ricemask_BOOL self.STARTED = (self.STARTED + self.CRPST) * pcr.ifthenelse( CropHarvNow, pcr.scalar(0.0), 1.0) # - pcr.ifthenelse(CropHarvNow, self.STARTED, 0.) elif self.AutoStartStop == True: if self.HarvestDAP == 0: HarvNow = (Not_Finished == False) | Calc_RainSum CropHarvNow = HarvNow & self.ricemask_BOOL elif self.HarvestDAP > 0: HarvNow = self.STARTED == self.HarvestDAP CropHarvNow = (HarvNow & self.ricemask_BOOL) | Calc_RainSum # Two auxilliary variables: Time2Plant1stCrop = self.PSUM >= self.RainSumReq StdMin1 = self.STARTED == -1 CropStartNow_Season1 = Time2Plant1stCrop & self.ricemask_BOOL CropStartNow_Season2 = StdMin1 & self.ricemask_BOOL CropStartNow = CropStartNow_Season1 | CropStartNow_Season2 CropStartNow_scalar = pcr.scalar(CropStartNow) if self.Sim3rdSeason == False: HarvSeason1_temp = FirstSeason & CropHarvNow HarvSeasonOne = HarvSeason1_temp & self.ricemask_BOOL HarvSeason2_temp = SecondSeason & CropHarvNow HarvSeasonTwo = HarvSeason2_temp & self.ricemask_BOOL self.Season = ( self.Season + pcr.ifthenelse(HarvSeasonOne, self.ricemask, 0.0) - pcr.ifthenelse(HarvSeasonTwo, self.ricemask * 2.0, 0.0) ) # beware, this variable is also modified in another equation Started = self.STARTED > 0 CropStarted = Started & self.ricemask_BOOL SeasonOneHarvd = self.STARTED < 0 SeasonOneHarvd_Scalar = pcr.scalar(SeasonOneHarvd) PrepareField_temp = pcr.scalar(self.Pausedays) PrepareField = pcr.ifthenelse(FirstSeason, PrepareField_temp, 0.0) self.STARTED = ( (self.STARTED + CropStartNow_scalar + pcr.scalar(CropStarted)) * pcr.ifthenelse(CropHarvNow, pcr.scalar(0.0), 1.0) - pcr.ifthenelse(HarvSeasonOne, PrepareField, 0.0) + SeasonOneHarvd_Scalar) elif self.Sim3rdSeason == True: HarvSeason12_temp = FirstSeason | SecondSeason HarvSeasonOneTwo = HarvSeason12_temp & CropHarvNow HarvSeasonThree = (ThirdSeason & CropHarvNow) | ( ThirdSeason & Calc_RainSum) self.Season = ( self.Season + pcr.ifthenelse(HarvSeasonOneTwo, pcr.scalar(1.0), 0.0) - pcr.ifthenelse(HarvSeasonThree, pcr.scalar(3.0), 0.0) ) # beware, this variable is also modified in another equation Started = self.STARTED > 0 CropStarted = Started & self.ricemask_BOOL Season12Harvd = self.STARTED < 0 Season12Harvd_Scalar = pcr.scalar(Season12Harvd) PrepareField_temp = pcr.scalar(self.Pausedays) FirstorSecondSeason = FirstSeason | SecondSeason PrepareField = pcr.ifthenelse(FirstorSecondSeason, PrepareField_temp, 0.0) self.STARTED = ( (self.STARTED + CropStartNow_scalar + pcr.scalar(CropStarted)) * pcr.ifthenelse(CropHarvNow, pcr.scalar(0.0), 1.0) - pcr.ifthenelse(HarvSeasonOneTwo, PrepareField, 0.0) + Season12Harvd_Scalar) else: print(self.Sim3rdSeason) time.sleep(10) else: print( "Strange value of variable AutoStartStop found... ctrl + c to exit..." ) time.sleep(100) else: print( "Strange (negative?) value of variable CropStartDOY found... ctrl + c to exit..." ) time.sleep(100) if self.WATERLIMITED == "True": TRANRF = self.Transpiration / NOTNUL_pcr(self.PotTrans) WAWP = WCWP * self.ROOTD_mm Enough_water = pcr.ifthenelse( CropStartNow, True, self.WA > WAWP) # timestep delay...! todo else: print("Warning, run without water effects on crop growth...") TRANRF = pcr.scalar(1.0) Enough_water = True # self.T = (self.TMIN + self.TMAX)/2. # for testing with Wageningen weather files only - sdv # Calculate thermal time (for TSUM and DVS): Warm_Enough = self.T >= self.TBASE DegreeDay = self.T - self.TBASE DTEFF = pcr.ifthenelse(Warm_Enough, DegreeDay, 0.0) # Check if leaves are present: Leaves_Present = self.LAI > 0.0 # Check whether certain critical moments, external circumstances or crop growth stages occur that influence crop growth and development: BeforeAnthesis = self.TSUM < self.TSUMAN UntilAnthesis = self.TSUM <= self.TSUMAN AtAndAfterAnthesis = self.TSUM >= self.TSUMAN AfterAnthesis = self.TSUM > self.TSUMAN Roots_Dying = self.DVS >= self.DVSDR Vegetative = CropStarted & UntilAnthesis Generative = CropStarted & AfterAnthesis EarlyStages = self.DVS < 0.2 LaterStages = self.DVS >= 0.2 SmallLeaves = self.LAI < 0.75 BiggerLeaves = self.LAI >= 0.75 Juvenile = EarlyStages & SmallLeaves Adult = LaterStages | BiggerLeaves # Calculate daylength (assumed similar throughout the catchment area -> scalar, no array), based on latitude and Day Of Year (DOY) DAYL = astro_py(DOY, self.LAT) # Calculate the specific leaf area (m2 (leaf) gā1 (leaf)) by interpolation of development stage in SLAF, multiplication with self.SLACF SLA = self.SLAC * self.SLACF.lookup_linear(self.DVS) # Obtain the fractions (-) of daily dry matter production allocated (in absence of water shortage) to, respectively, root growth (FRTWET), leaf growth (FLVT), # growth of stems (FSTT) and growth of storage organs (FSO, i.e. rice grains), as a function of development stage (DVS), by interpolation. FRTWET = self.FRTTB.lookup_linear(self.DVS) FLVT = self.FLVTB.lookup_linear(self.DVS) FSTT = self.FSTTB.lookup_linear(self.DVS) FSOT = self.FSOTB.lookup_linear(self.DVS) RDRTMP = self.RDRTB.lookup_linear(self.DVS) # Many growth processes can only occur when EMERG = TRUE; this is the case when crop phenological development has started, soil water content is above # permanent wilting point, the crop has leaves and is not yet harvested or growth has otherwise been terminated: EMERG = CropStarted & Enough_water & Leaves_Present & Not_Finished # Determine the influence of astronomical daylength on crop development (via thermal time - TSUM) by interpolation in the PHOTTB table PHOTT = self.PHOTTB.lookup_linear(DAYL) # Daylength only potentially has a modifying influence on crop development (via thermal time, TSUM) before anthesis: PHOTPF = pcr.ifthenelse(BeforeAnthesis, PHOTT, pcr.scalar(1.0)) # Influence (if any) of daylength results in a modified daily change in thermal time (TSUM). RTSUMP = DTEFF * PHOTPF # TSUM (state): at crop establishment TSUMI is added (the TSUM that was accumulated in the nursery in the case of transplanted rice); # during crop growth, the daily rate of change RTSUMP is added if EMERG = TRUE. Upon crop harvest, TSUM is reset (i.e. multiplied with 0.). self.TSUM = (self.TSUM + pcr.ifthenelse(CropStartNow, pcr.scalar(self.TSUMI), 0.0) + pcr.ifthenelse(EMERG, RTSUMP, 0.0)) * pcr.ifthenelse( CropHarvNow, pcr.scalar(0.0), 1.0) # Calculation of DVS (state). # In LINTUL1 and LINTUL2, TSUM directly steered all processes influenced by crop phenological development. # However, Shibu et al. (2010) derived some code from ORYZA_2000 (Bouman et al., 2001), including the use DVS instead of TSUM. # Hence in LINTUL3, some processes are still controlled directly by TSUM and some are controlled by its derived variable DVS # ā a somewhat confusing situation that offers scope for future improvement. # After anthesis DVS proceeds at a different rate (DVS_gen) than before (DVS_veg). Throughout crop development DVS is calculated as the DVS_veg + DVS_gen. DVS_veg = (self.TSUM / self.TSUMAN * pcr.ifthenelse(CropHarvNow, pcr.scalar(0.0), 1.0)) DVS_gen = (1.0 + (self.TSUM - self.TSUMAN) / self.TSUMMT) * pcr.ifthenelse( CropHarvNow, pcr.scalar(0.0), 1.0) self.DVS = pcr.ifthenelse(Vegetative, DVS_veg, 0.0) + pcr.ifthenelse( Generative, DVS_gen, 0.0) # Root depth growth can occur as long as the maximum rooting depth has not yet been achieved: CanGrowDownward = self.ROOTD_mm <= self.ROOTDM_mm # Root growth occurs before anthesis if there is crop growth (EMERG = TRUE), enough water (already in EMERG - todo) and the maximum rooting depth has not yet been reached. RootGrowth = Enough_water & BeforeAnthesis & EMERG & CanGrowDownward # If root growth occurs, it occurs at a fixed pace (mm/day): RROOTD_mm = pcr.ifthenelse(RootGrowth, self.RRDMAX_mm, pcr.scalar(0.0)) # Rooting depth (state): at crop establishment ROOTDI_mm is added (the rooting depth at transplanting); during crop growth, the daily rate of change # self.ROOTDI_mm is added. Upon crop harvest, rooting depth is reset (i.e. multiplied with 0.). self.ROOTD_mm = ( self.ROOTD_mm + pcr.ifthenelse(CropStartNow, self.ROOTDI_mm, pcr.scalar(0.0)) + RROOTD_mm) * pcr.ifthenelse(CropHarvNow, pcr.scalar(0.0), 1.0) # By depth growth, roots explore deeper layers of soil that contain previously untapped water supplies, the assumption is. # In the case of irrigated rice, it seems reasonable to assume that those layers are saturated with water (WCST = volumetric soil water content at saturation). # The volume of additional water that becomes available to the crop is then equal to EXPLOR: EXPLOR = RROOTD_mm * WCST ############################################################################################################# # Water Limitation: effects on partitioning # If TRANRF falls below 0.5, root growth is accelerated: FRTMOD = pcr.max(1.0, 1.0 / (TRANRF + 0.5)) FRT = FRTWET * FRTMOD # ... and shoot growth (i.e. growth of all aboveground parts) diminshed: FSHMOD = (1.0 - FRT) / (1 - FRT / FRTMOD) FLV = FLVT * FSHMOD FST = FSTT * FSHMOD FSO = FSOT * FSHMOD # Daily intercepted Photosynthetically Active Radiation (PAR), according to (Lambert-)Beer's law. # The factor 0.5 accounts for the fact that about 50% (in terms of energy) of the frequency spectrum of incident solar radiation # can be utilized for photosynthesis by green plants. PARINT = pcr.ifthenelse( Not_Finished, 0.5 * self.IRRAD * 0.001 * (1.0 - pcr.exp(-self.K * self.LAI)), 0.0, ) # The total growth rate is proportional to the intercepted PAR with a fixed Light Use Efficiency (LUE) - the core of the LINTUL apporach. GTOTAL = self.LUE * PARINT * TRANRF # Leaf dying due to ageing occurs from anthesis on (actually that is already arranged in the interpolation table - double!), with a relative death rate RDRTMP: RDRDV = pcr.ifthenelse(AtAndAfterAnthesis, RDRTMP, pcr.scalar(0.0)) # Leaf dying due to mutual shading occurs when LAI > LAICR: RDRSH = pcr.max(0.0, self.RDRSHM * (self.LAI - self.LAICR) / self.LAICR) # The largest of the two effects determines the relative death rate of foliage: RDR = pcr.max(RDRDV, RDRSH) # Impact of leaf dying on leaf weight - N limitation stuff not (yet) implemented N_Limitation = NNI < 1.0 DLVNS = pcr.ifthenelse( CropStarted, pcr.scalar(1.0), 0.0) * pcr.ifthenelse( N_Limitation, self.WLVG * self.RDRNS * (1.0 - NNI), 0.0) DLVS = self.WLVG * RDR DLV = (DLVS + DLVNS) * pcr.scalar(Not_Finished) RWLVG = pcr.ifthenelse(EMERG, GTOTAL * FLV - DLV, pcr.scalar(0.0)) self.WLVG = (self.WLVG + pcr.ifthenelse(CropStartNow, self.WLVGI, pcr.scalar(0.0)) + RWLVG) * (1.0 - pcr.scalar(CropHarvNow)) self.WLVD = (self.WLVD + DLV) * (1.0 - pcr.scalar(CropHarvNow)) # Growth of leaves in terms of mass (GLV) and in terms of LAI (GLAI). GLV = FLV * GTOTAL Adt_or_Harv = pcr.pcror(Adult, CropHarvNow) Juv_or_Harv = pcr.pcror(Juvenile, CropHarvNow) NoLeavesYet = self.LAI == 0.0 LetsGo = pcr.pcrand(Enough_water, CropStartNow) LetsGro = pcr.pcrand(NoLeavesYet, LetsGo) GLAI = (pcr.ifthenelse(Adt_or_Harv, SLA * GLV, pcr.scalar(0.0)) + pcr.ifthenelse( Juv_or_Harv, self.LAI * (pcr.exp(self.RGRL * DTEFF * DELT) - 1.0) / DELT * TRANRF * pcr.exp(-self.LAI * (1.0 - NNI)), 0.0, ) + pcr.ifthenelse(LetsGro, self.LAII / DELT, pcr.scalar(0.0))) # (Abs.) impact of leaf dying on LAI # Daily decrease in LAI due to dying of leaves (if any), due to aging and/or mutual shading: DLAIS = self.LAI * RDR # Daily decrease in LAI due to nitrogen shortage (presently non-functional): DLAINS = pcr.ifthenelse(CropStarted, pcr.scalar(1.0), 0.0) * pcr.ifthenelse( N_Limitation, DLVNS * SLA, 0.0) # Total daily decrease in LAI due to leaf death (aging, mutual shading, N shortage): DLAI = (DLAIS + DLAINS) * pcr.scalar(Not_Finished) # The initial LAI (LAII, transplanted rice) is added to GLAI at crop establishment, not in below state equation as done by Shibu et al. (2010). self.LAI = (self.LAI + GLAI - DLAI) * pcr.ifthenelse( CropHarvNow, pcr.scalar(0.0), 1.0) # Daily death rate of roots: if self.DVS >= self.DVSDR, a fraction self.DRRT of the roots is dying every day: DRRT = pcr.ifthenelse(Roots_Dying, self.WRT * self.RDRRT, pcr.scalar(0.0)) # Net daily change in root weight: if there is crop growth, this is equal to daily weight increase (GTOTAL * FRT) minus the daily decrease due to dying roots (if any). RWRT = pcr.ifthenelse(EMERG, GTOTAL * FRT - DRRT, pcr.scalar(0.0)) # Calculation of the root weight (state): when the crop is planted, the initial root weight WRTLI is added. After that, the net (daily) rate of change RWRT is added. # Upon crop harvest, RWRT is reset (i.e. multiplied with 0.) self.WRT = (self.WRT + pcr.ifthenelse(CropStartNow, self.WRTLI, pcr.scalar(0.0)) + RWRT) * (1.0 - pcr.scalar(CropHarvNow)) # WDRT (state) is the total quantity of leaves that has died (mostly relevant for mass balance checking purposes). Simply calculated by adding the daily dying roots (if any); # (the variable is reset upon crop harvest). self.WDRT = (self.WDRT + DRRT) * (1.0 - pcr.scalar(CropHarvNow)) # Daily change in the weight of the storage organs (rice grains) is fraction FSO of the total growth rate (GTOTAL). FSO is determined by phenology and moisture stress # (which can modify the root/shoort ratio) RWSO = pcr.ifthenelse(EMERG, GTOTAL * FSO, pcr.scalar(0.0)) # Weight of storage organs (state) is simply calculated by accumulating the daily growth RWSO. At crop harvest, it is reset to 0. self.WSO = (self.WSO + pcr.ifthenelse(CropStartNow, self.WSOI, pcr.scalar(0.0)) + RWSO) * (1.0 - pcr.scalar(CropHarvNow)) # WSO in tons/ha: WSOTHA = self.WSO / 100.0 # Daily change in the weight of the stems is a fraction FST of the total growth rate (GTOTAL). FST is determined by phenology and moisture stress RWST = pcr.ifthenelse(EMERG, GTOTAL * FST, pcr.scalar(0.0)) # Weight of storage organs (state) is simply calculated by accumulating the daily growth RWSO. At crop harvest, it is reset to 0. self.WST = (self.WST + pcr.ifthenelse(CropHarvNow, self.WSTI, pcr.scalar(0.0)) + RWST) * (1.0 - pcr.scalar(CropHarvNow)) # An additional state, handy for testing purposes: self.Test += 1.0
def integralLogisticFunction(self,x): #-returns a tupple of two values holding the integral of the logistic functions # of (x) and (-x) logInt=pcr.ln(pcr.exp(-x)+1) return logInt,x+logInt