Index: doc/release_notes.rst =================================================================== diff -u -rac27b04cf4c19855fe8533b46ed1417ede3499f9 -rc11e200673ad9c59a065f3ff6324474e049dfba5 --- doc/release_notes.rst (.../release_notes.rst) (revision ac27b04cf4c19855fe8533b46ed1417ede3499f9) +++ doc/release_notes.rst (.../release_notes.rst) (revision c11e200673ad9c59a065f3ff6324474e049dfba5) @@ -1,13 +1,16 @@ Release notes ============= -Version 1.0 RC6 ---------------- +Trunc +----- unsupported interim release + added HBV type lower zone to wflow\_sbm. Use MaxPercolation > 0 to use this zone. MaxLeakege > 0 will send water outside of the model + Test version of the wflow_W3RA model ++ The default formulation of lateral flow is not the original SBM formulation that + gives much lower lateral flows than the one from the Cqflow model. Use origTopogLateral=0 to fro + old models! Other options are 1 (default), and 2: use effective Ksat at freatic level Version 1.0 RC5 Index: wflow-py/setup.py =================================================================== diff -u -raeb030a6cde309d5711928010286dbbdeb129ccd -rc11e200673ad9c59a065f3ff6324474e049dfba5 --- wflow-py/setup.py (.../setup.py) (revision aeb030a6cde309d5711928010286dbbdeb129ccd) +++ wflow-py/setup.py (.../setup.py) (revision c11e200673ad9c59a065f3ff6324474e049dfba5) @@ -14,5 +14,3 @@ scripts=['Scripts/pcr2netcdf.py','Scripts/tss2xml.py','wflow/wflow_extract.py','wflow/wflow_sceleton.py','wflow/wflow_gr4.py','wflow/plottss.py','wflow/wflow_wave.py','wflow/wflow_cqf.py','wflow/wflow_floodmap.py','wflow/wflow_upscale.py','wflow/wflow_fit.py','wflow/wflow_adapt.py','wflow/wflow_delwaq.py','Scripts/wflow_prepare_step1.py','Scripts/wflow_prepare_step2.py','wflow/wflow_sbm.py','wflow/wflow_hbv.py','wflow/wflow_W3RA.py','wflow/wflow_upscale.py'], description='the wflow hydrological models (part of OpenStreams)', ) - - Index: wflow-py/wflow/wf_DynamicFramework.py =================================================================== diff -u -r0bc74763b7bce34de02b39cee5d98d0cd85e936c -rc11e200673ad9c59a065f3ff6324474e049dfba5 --- wflow-py/wflow/wf_DynamicFramework.py (.../wf_DynamicFramework.py) (revision 0bc74763b7bce34de02b39cee5d98d0cd85e936c) +++ wflow-py/wflow/wf_DynamicFramework.py (.../wf_DynamicFramework.py) (revision c11e200673ad9c59a065f3ff6324474e049dfba5) @@ -950,32 +950,32 @@ return retval.flatten().tolist() else: self.logger.warn(mapname + " is not defined in the usermodel, returning empty list") - return [] + return [] - def wf_supplyMapAsNumpy(self,mapname): """ Returns a numpy array (matrix) for the specified map and the current timestep. If the maps is not dynamic the current staus of the map is - returns which may be undefined for maps that are filled with data + returns which may be undefined for maps that are filled with data at the end of a run Missing value is -999 - - Input: + + Input: - mapname (string) - - Output: + + Output: - numpy array """ if hasattr(self._userModel(), mapname): - exec "retval = pcr2numpy(self._userModel()." + mapname + ",-999)" + #exec "retval = pcr2numpy(self._userModel()." + mapname + ",-999)" + pcrmap = getattr(self._userModel(),mapname) + retval = pcr_as_numpy(pcrmap) if self.APIDebug: self.logger.debug("wf_supplyMapAsNumpy returning: " + mapname) - else: + else: self.logger.warn(mapname + " is not defined in the usermodel, returning empty list") return [] - - + return retval @@ -1126,9 +1126,26 @@ self.logger.debug("wf_supplyVariableUnits from framework: " + str(ret)) return ret - + def wf_supplyEndTime(self): + """ + gets the end time of the model run + :return: current time as seconds since epoch + """ + seconds_since_epoch = time.mktime(self.datetime_firststep.timetuple()) * 1000 + + return seconds_since_epoch + (self._d_lastTimestep - self._d_firstTimestep) * self._userModel().timestepsecs + + def wf_supplyStartTime(self): + """ + gets the start time of the model run + :return: current time as seconds since epoch + """ + seconds_since_epoch = time.mktime(self.datetime_firststep.timetuple()) * 1000 + + return seconds_since_epoch + def wf_supplyCurrentTime(self): """ gets the current time in seconds after the start of the run Index: wflow-py/wflow/wflow_bmi.py =================================================================== diff -u -r9971ec9df2d1b8af04eb510c21c80ccd3d0c94ce -rc11e200673ad9c59a065f3ff6324474e049dfba5 --- wflow-py/wflow/wflow_bmi.py (.../wflow_bmi.py) (revision 9971ec9df2d1b8af04eb510c21c80ccd3d0c94ce) +++ wflow-py/wflow/wflow_bmi.py (.../wflow_bmi.py) (revision c11e200673ad9c59a065f3ff6324474e049dfba5) @@ -1,26 +1,38 @@ __author__ = 'schelle' +import os -from abc import abstractmethod -from abc import ABCMeta +#TODO: Put link to runinfo file in ini ( to get date time) +#TODO: Rework framework to get rid of max timesteps shit +class wflowbmi(object): -class IBmi(object): - __metaclass__ = ABCMeta - - @abstractmethod def initialize(self, configfile=None): """ - Initialize and load the Fortran library (and model, if applicable). - The Fortran library is loaded and ctypes is used to annotate functions - inside the library. The Fortran library's initialization is called. - Normally a path to an ``*.ini`` model file is passed to the - :meth:`__init__`. If so, that model is loaded. Note that - :meth:`_load_model` changes the working directory to that of the model. + Assumptions for now: + - the configfile wih be a full path + - we define the case from the basedir of the configfile """ - pass + wflow_cloneMap = 'wflow_subcatch.map' + datadir = os.path.basename(os.path.dirname(configfile)) + inifile = os.path.basename(configfile) + runid = "run_default" + # The current pcraster framework needs a max number of timesteps :-( + # This cannot be avoided at the moment (needs a rework) + # set to 10000 for now + maxNrSteps = 10000 + if "_sbm" in configfile: + import wflow_sbm as wf + elif "hbv" in configfile: + import wflow_sbm as wf - @abstractmethod + myModel = wf.WflowModel(wflow_cloneMap, datadir, runid,inifile) + myModel.timestepsecs = timeStepInSeconds + self.dynModel = wf.wf_DynamicFramework(myModel, maxNrSteps, firstTimestep = 1) + self.dynModel.createRunId(NoOverWrite=0) + self.dynModel._runInitial() + self.dynModel._runResume() + def finalize(self): """ Shutdown the library and clean up the model. @@ -30,82 +42,88 @@ """ pass - @abstractmethod + def update(self, dt): """ Return type string, compatible with numpy. """ pass - @abstractmethod + def get_var_count(self): """ Return number of variables """ - pass + return self.dynModel.wf_supplyVariableCount() - @abstractmethod + def get_var_name(self, i): """ Return variable name """ - pass + #TODO: Add mapping here??? + return self.dynModel.wf_supplyVariableNames(i) - @abstractmethod + def get_var_type(self, name): """ Return type string, compatible with numpy. """ - return self.get_var(name).dtype + npmap = self.dynModel.wf_supplyMapAsNumpy(name) - @abstractmethod + return npmap.dtype + + def get_var_rank(self, name): """ Return array rank or 0 for scalar. """ - return len(self.get_var(name).shape) + npmap = self.dynModel.wf_supplyMapAsNumpy(name) - @abstractmethod + return len(npmap.shape) + + def get_var_shape(self, name): """ Return shape of the array. """ - return self.get_var(name).shape + npmap = self.dynModel.wf_supplyMapAsNumpy(name) - @abstractmethod + return npmap.shape + + def get_start_time(self): """ returns start time """ - pass + return self.dynModel.wf_supplyStartTime() - @abstractmethod + def get_end_time(self): """ returns end time of simulation """ - pass + return self.dynModel.wf_supplyEndTime() - @abstractmethod + def get_current_time(self): """ returns current time of simulation """ - pass + return self.dynModel.wf_supplyCurrentTime() - @abstractmethod def get_var(self, name): """ Return an nd array from model library """ pass - @abstractmethod + def set_var(self, name, var): """Set the variable name with the values of var""" pass - @abstractmethod + def set_var_slice(self, name, start, count, var): """ Overwrite the values in variable name with data @@ -125,7 +143,7 @@ tmp[slices] self.set_var(name, name, tmp) - @abstractmethod + def set_var_index(self, name, index, var): """ Overwrite the values in variable "name" with data @@ -140,14 +158,14 @@ tmp.flat[index] = var self.set_var(name, name, tmp) - @abstractmethod + def inq_compound(self, name): """ Return the number of fields of a compound type. """ pass - @abstractmethod + def inq_compound_field(self, name, index): """ Lookup the type,rank and shape of a compound field Index: wflow-py/wflow/wflow_sbm.py =================================================================== diff -u -r734067e6692e9b162f6ea4433559248b5f0a021f -rc11e200673ad9c59a065f3ff6324474e049dfba5 --- wflow-py/wflow/wflow_sbm.py (.../wflow_sbm.py) (revision 734067e6692e9b162f6ea4433559248b5f0a021f) +++ wflow-py/wflow/wflow_sbm.py (.../wflow_sbm.py) (revision c11e200673ad9c59a065f3ff6324474e049dfba5) @@ -233,10 +233,10 @@ def __init__(self, cloneMap, Dir, RunDir, configfile): DynamicModel.__init__(self) + self.caseName = os.path.abspath(Dir) self.clonemappath = Dir + "/staticmaps/" + cloneMap setclone(self.clonemappath) self.runId = RunDir - self.caseName = Dir self.Dir = Dir + "/" self.configfile = configfile self.SaveDir = self.Dir + "/" + self.runId + "/" @@ -401,7 +401,7 @@ if self.origTopogLateral: self.logger.info("Applying the original topog_sbm lateral transfer formulation") else: - self.logger.info("Applying the wflow lateral transfer formulation") + self.logger.warn("Using the original (depreciated!!) wflow lateral transfer formulation") self.sCatch = int(configget(self.config, "model", "sCatch", "0")) self.intbl = configget(self.config, "model", "intbl", "intbl") @@ -1086,14 +1086,8 @@ # now the actual transfer to the saturated store.. self.Transfer = min(self.UStoreDepth, ifthenelse(self.SaturationDeficit <= 0.00001, 0.0, Ksat * self.UStoreDepth / (self.SaturationDeficit + 1))) - zi_foreward = max(0.0, self.FirstZoneThickness - (self.FirstZoneDepth + self.Transfer) / ( - self.thetaS - self.thetaR)) - Ksat_foreward = self.FirstZoneKsatVer * exp(-self.f * zi_foreward) - self.Transfer = min(self.UStoreDepth, ifthenelse(self.SaturationDeficit <= 0.00001, 0.0, - (Ksat +Ksat_foreward) * 0.5 * self.UStoreDepth / (self.SaturationDeficit + 1))) - MaxCapFlux = max(0.0, min(Ksat, self.ActEvapUStore, UStoreCapacity, self.FirstZoneDepth)) # No capilary flux is roots are in water, max flux if very near to water, lower flux if distance is large CapFluxScale = ifthenelse(self.zi > self.RootingDepth, @@ -1142,7 +1136,11 @@ if self.origTopogLateral: Lateral = self.FirstZoneKsatVer * tan(self.waterSlope) * exp(-self.SaturationDeficit / self.M) else: - Lateral = self.FirstZoneKsatVer * self.waterSlope * exp(-self.SaturationDeficit / self.M) + if self.origTopogLateral == 1: + Lateral = self.FirstZoneKsatVer * self.waterSlope * exp(-self.SaturationDeficit / self.M) + else: + Lateral = Ksat * self.waterSlope + MaxHor = max(0.0, min(Lateral, self.FirstZoneDepth)) self.FirstZoneFlux = accucapacityflux(waterLdd, self.FirstZoneDepth, MaxHor) self.FirstZoneDepth = accucapacitystate(waterLdd, self.FirstZoneDepth, MaxHor) @@ -1154,7 +1152,10 @@ if self.origTopogLateral: Lateral = self.FirstZoneKsatVer * tan(self.waterSlope) * exp(-self.SaturationDeficit / self.M) else: - Lateral = self.FirstZoneKsatVer * self.waterSlope * exp(-self.SaturationDeficit / self.M) + if self.origTopogLateral == 1: + Lateral = self.FirstZoneKsatVer * self.waterSlope * exp(-self.SaturationDeficit / self.M) + else: + Lateral = Ksat * self.waterSlope MaxHor = max(0.0, min(Lateral, self.FirstZoneDepth)) #MaxHor = self.ZeroMap