Index: wflow-py/wflow/wf_DynamicFramework.py =================================================================== diff -u -re87d7796f64996fb91a9c4c74fc9c7d211631264 -r2ac03e7c86b0d833f53b45ca40863aef72230ef5 --- wflow-py/wflow/wf_DynamicFramework.py (.../wf_DynamicFramework.py) (revision e87d7796f64996fb91a9c4c74fc9c7d211631264) +++ wflow-py/wflow/wf_DynamicFramework.py (.../wf_DynamicFramework.py) (revision 2ac03e7c86b0d833f53b45ca40863aef72230ef5) @@ -1138,6 +1138,66 @@ return retval + def wf_supplyMapXAsNumpy(self): + """ + + :return x-coordinates of the current clone map: + + Missing value is -999 + """ + + x = xcoordinate(boolean(spatial(1.0))) + retval = pcr_as_numpy(x) + + return retval + + + def wf_supplyMapYAsNumpy(self): + """ + + :return y-coordinates of the current clone map: + + Missing value is -999 + """ + + y = ycoordinate(boolean(spatial(1.0))) + retval = pcr_as_numpy(y) + + return retval + + + def wf_supplyMapZAsNumpy(self): + """ + + :return z-coordinates of the current clone map: + + Assumes an Altitude map is present, otherwise return empty numpy + Missing value is -999 + """ + + if hasattr(self._userModel(), 'Altitude'): + retval = getattr(self._userModel(), 'Altitude') + + return pcr2numpy(retval,-999) + else: + self.logger.warn("Altitude is not defined in the usermodel, returning empty list") + return [] + + def wf_supplyMapOrigin(self): + """ + + :return: lower left corner of the map as X, Y + + """ + a = boolean(1) + + Y = self.wf_supplyMapYAsNumpy() + X = self.wf_supplyMapXAsNumpy() + + return numpy.array([X.flatten.min(),Y.flatten.min()]) + + + def wf_supplyMapAsPcrMap(self,mapname): """ Returns a pcrmap for the specified map and the current Index: wflow-py/wflow/wflow_bmi.py =================================================================== diff -u -r81fa8786648bec668ca187e7e78214f57e79ad0a -r2ac03e7c86b0d833f53b45ca40863aef72230ef5 --- wflow-py/wflow/wflow_bmi.py (.../wflow_bmi.py) (revision 81fa8786648bec668ca187e7e78214f57e79ad0a) +++ wflow-py/wflow/wflow_bmi.py (.../wflow_bmi.py) (revision 2ac03e7c86b0d833f53b45ca40863aef72230ef5) @@ -10,7 +10,16 @@ #TODO: Rework framework to get rid of max timesteps shit class wflowbmi_ligth(object): + """ + Deltares specific ligth version of the BMI. Uaed for internal model linkage + """ + def __init__(self): + """ + + :return: + """ + def initialize(self, configfile=None,loglevel=logging.DEBUG): """ Assumptions for now: @@ -29,15 +38,19 @@ # .. todo:: # Get name of module from ini file name + maxNrSteps = 10000 if "wflow_sbm.ini" in configfile: import wflow_sbm as wf + self.name = "wflow_sbm" elif "wflow_hbv.ini" in configfile: import wflow_sbm as wf + self.name = "wflow_hbv" elif "wflow_routing.ini" in configfile: import wflow_routing as wf + self.name = "wflow_routing" else: - raise NotImplementedError + raise ValueError myModel = wf.WflowModel(wflow_cloneMap, datadir, runid, inifile) @@ -219,12 +232,35 @@ class wflowbmi_csdms(bmi.Bmi): + """ + csdms BMI implementation for pcraster/python models + """ + def __init__(self): + """ + Initialises the object + + :return nothing: + """ + + self.currenttimestep = 0 + self.name = "undefined" + self.myModel = None + self.dynModel = None + + def initialize(self, filename,loglevel=logging.DEBUG): """ + Initialise the model. Shoudl be call before any other method. + + :var filename: full path to the wflow ini file + :var loglevel: optional loglevel (default == DEBUG) + Assumptions for now: - - the configfile wih be a full path - - we define the case from the basedir of the configfile + + - the configfile wih be a full path + - we define the case from the basedir of the configfile + """ retval = 0 self.currenttimestep = 1 @@ -239,15 +275,20 @@ # Get name of module from ini file name maxNrSteps = 10000 + if "wflow_sbm.ini" in filename: import wflow_sbm as wf + self.name = "wflow_sbm" elif "wflow_hbv.ini" in filename: import wflow_sbm as wf + self.name = "wflow_hbv" elif "wflow_routing.ini" in filename: import wflow_routing as wf + self.name = "wflow_routing" else: - raise NotImplementedError + raise ValueError + self.myModel = wf.WflowModel(wflow_cloneMap, datadir, runid, inifile) self.dynModel = wf.wf_DynamicFramework(self.myModel, maxNrSteps, firstTimestep = 1) @@ -258,40 +299,42 @@ def update(self): """ - Propagate the model to the next timestep + Propagate the model to the next model timestep """ self.dynModel._runDynamic(self.currenttimestep, self.currenttimestep) self.currenttimestep = self.currenttimestep + 1 - def update_until(self, time): """ - Update the model until the given time. + Update the model until the given time. Can only go foreward in time. :var double time: time in the units and epoch returned by the function get_time_units. """ - timespan = time - self.get_current_time() + curtime = self.get_current_time() + + if curtime > time: + print("Time before current time.") + raise ValueError + timespan = time - curtime + nrsteps = int(timespan/self.dynModel.timestepsecs) self.dynModel._runDynamic(self.currenttimestep, self.currenttimestep + nrsteps -1) self.currenttimestep = self.currenttimestep + nrsteps def update_frac(self, time_frac): """ - No idea what to do with this one..... - - Input parameters: - double time_frac: ??? + Not implemented. Raises a NotImplementedError """ raise NotImplementedError - def save_state(self, destination_directory): """ Ask the model to write its complete internal current state to one or more state files in the given directory. - Afterwards the given directory should only contain the state files and nothing else. + Afterwards the given directory will only contain the state files and nothing else. + Sates are save in the models' native format. :var destination_directory: the directory in which the state files should be written. """ @@ -302,23 +345,19 @@ """ Shutdown the library and clean up the model. Uses the default (model configured) state location to also save states. - """ self.dynModel._runSuspend() self.dynModel._wf_shutdown() - def get_component_name(self): """ - :return identifier of the model: + :return: identifier of the model based on the name of the ini file """ + return self.name - return self.dynModel.name - - def get_input_var_names(self): """ - :return List of String objects: identifiers of all input variables of the model: + :return: List of String objects: identifiers of all input variables of the model: """ namesroles = self.dynModel.wf_supplyVariableNamesAndRoles() @@ -336,7 +375,9 @@ def get_output_var_names(self): """ - :return List of String objects: identifiers of all output variables of the model: + Returns the list of model output variables + + :return: List of String objects: identifiers of all output variables of the model: """ namesroles = self.dynModel.wf_supplyVariableNamesAndRoles() @@ -355,7 +396,9 @@ def get_var_type(self, long_var_name): """ - :return type string, compatible with numpy: + Gets the variable type as a numpy type string + + :return: variable type string, compatible with numpy: """ npmap = self.dynModel.wf_supplyMapAsNumpy(long_var_name) @@ -364,29 +407,35 @@ def get_var_rank(self, long_var_name): """ + Gets the number of dimensions for a variable + :var String long_var_name: identifier of a variable in the model: - :return array rank or 0 for scalar (number of dimensions): + :return: array rank or 0 for scalar (number of dimensions): """ npmap = self.dynModel.wf_supplyMapAsNumpy(long_var_name) return len(npmap.shape) def get_var_size(self, long_var_name): """ + Gets the number of elements in a variable (rows * cols) + :var String long_var_name: identifier of a variable in the model: - :return total number of values contained in the given variable (number of elements in map): + :return: total number of values contained in the given variable (number of elements in map) """ npmap = self.dynModel.wf_supplyMapAsNumpy(long_var_name) return npmap.size def get_var_nbytes(self, long_var_name): """ + Gets the number of bytes occupied in memory for a given variable. + :var String long_var_name: identifier of a variable in the model: - :return total number of bytes contained in the given variable (number of elements * bytes per element): + :return: total number of bytes contained in the given variable (number of elements * bytes per element) """ npmap = self.dynModel.wf_supplyMapAsNumpy(long_var_name) @@ -395,58 +444,73 @@ def get_start_time(self): """ - :return start time in the units and epoch returned by the function get_time_units: + Gets the start time of the model. + + :return: start time in the units and epoch returned by the function get_time_units """ return self.dynModel.wf_supplyStartTime() def get_current_time(self): """ - :return current time of simulation n the units and epoch returned by the function get_time_units: + Get the current time since the epoch of the model + + :return: current time of simulation n the units and epoch returned by the function get_time_units """ return self.dynModel.wf_supplyCurrentTime() def get_end_time(self): """ - :return end time of simulation n the units and epoch returned by the function get_time_units: + Get the end time of the model run in units since the epoch + + :return: end time of simulation n the units and epoch returned by the function get_time_units """ return self.dynModel.wf_supplyEndTime() def get_time_step(self): """ - :return duration of one time step of the model in the units returned by the function get_time_units: + Get the model time steps in units since the epoch + + :return: duration of one time step of the model in the units returned by the function get_time_units """ return self.dynModel.timestepsecs def get_time_units(self): """ - :return: time units as a CF convention string + Return the time units of the model as a string + + :return: Return a string formatted using the UDUNITS standard from Unidata. (http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/build/cf-conventions.html#time-coordinate) """ return self.dynModel.wf_supplyEpoch() def get_value(self, long_var_name): """ - :var long_var_name name of the variable - :return an np array of long_var_name + Get the value(s) of a variable as a numpy array + + :var long_var_name: name of the variable + :return: a np array of long_var_name """ return self.dynModel.wf_supplyMapAsNumpy(long_var_name) - def get_value_at_indices(self, long_var_name, inds): """ + Get a numpy array of the values at the given indices + :var long_var_name: identifier of a variable in the model: - :var List of list each tuple contains one index for each dimension of the given variable, i.e. each tuple indicates one element in the multi-dimensional variable array: + :var inds: List of list each tuple contains one index for each dimension of the given variable, i.e. each tuple indicates one element in the multi-dimensional variable array: - :return numpy array of values in the data type returned by the function get_var_type. + :return: numpy array of values in the data type returned by the function get_var_type. """ npmap = self.dynModel.wf_supplyMapAsNumpy(long_var_name) return npmap[inds] def set_value_at_indices(self, long_var_name, inds, src): """ + Set the values in a variable using a numpy array of the values given indices + :var long_var_name: identifier of a variable in the model: :var inds: List of Lists of integers inds each nested List contains one index for each dimension of the given variable, i.e. each nested List indicates one element in the multi-dimensional variable array, @@ -458,156 +522,118 @@ npmap[inds] = src self.dynModel.wf_setValuesAsNumpy(long_var_name,npmap) - def get_grid_type(self, long_var_name): """ + Get the grid type according to the enumeration in BmiGridType + :var String long_var_name: identifier of a variable in the model. - :return BmiGridType type of the grid geometry of the given variable: + :return: BmiGridType type of the grid geometry of the given variable. """ - ret=BmiGridType() return ret.UNIFORM - def get_grid_shape(self, long_var_name): """ - Only return something for variables with a uniform, rectilinear or structured grid. Otherwise raise ValueError. + Return the shape of the grid. Only return something for variables with a uniform, rectilinear or structured grid. Otherwise raise ValueError. :var long_var_name: identifier of a variable in the model. - :return List of integers: the sizes of the dimensions of the given variable, e.g. [500, 400] for a 2D grid with 500x400 grid cells. + :return: List of integers: the sizes of the dimensions of the given variable, e.g. [500, 400] for a 2D grid with 500x400 grid cells. """ - dim = self.dynModel.wf_supplyGridDim() #[ Xul, Yul, xsize, ysize, rows, cols] return dim[4,5] - def get_grid_spacing(self, long_var_name): + def get_grid_spacing(self, long_var_name): """ Only return something for variables with a uniform grid. Otherwise raise ValueError. :var long_var_name: identifier of a variable in the model. - :return the size of a grid cell for each of the dimensions of the given variable, e.g. [width, height]: for a 2D grid cell. + :return: The size of a grid cell for each of the dimensions of the given variable, e.g. [width, height]: for a 2D grid cell. """ raise NotImplementedError - def get_grid_origin(self, long_var_name): """ - Only return something for variables with a uniform grid. Otherwise raise ValueError. + gets the origin of the model grid. - Input parameters: - String long_var_name: identifier of a variable in the model. + :var String long_var_name: identifier of a variable in the model. - Return value: - List of doubles: the coordinate of the grid origin for each of the dimensions of the given variable. For a 2D grid this must be the lower left corner of the grid. + :return: X, Y: ,the lower left corner of the grid. """ - raise NotImplementedError + return self.dynModel.wf_supplyGridDim() + + def get_grid_x(self, long_var_name): """ - Only return something for variables with a rectilinear, structured or unstructured grid. Otherwise raise ValueError. + Give X coordinates of point in the model grid - Input parameters: - String long_var_name: identifier of a variable in the model. + :var String long_var_name: identifier of a variable in the model. - Return value: - Numpy array of doubles: x coordinate of grid cell center for each grid cell, in the same order as the values returned by function get_value. - For a rectilinear grid: x coordinate of column center for each column. + :return: Numpy array of doubles: x coordinate of grid cell center for each grid cell, in the same order as the values returned by function get_value. """ - return self.myModel.pcr2numpy(self.myModel.xcoordinate(1),0.0) + return self.dynModel.wf_supplyMapXAsNumpy() def get_grid_y(self, long_var_name): """ - Only return something for variables with a rectilinear, structured or unstructured grid. Otherwise raise ValueError. + Give Y coordinates of point in the model grid - Input parameters: - String long_var_name: identifier of a variable in the model. + :var String long_var_name: identifier of a variable in the model. - Return value: - Numpy array of doubles: x coordinate of grid cell center for each grid cell, in the same order as the values returned by function get_value. - For a rectilinear grid: x coordinate of column center for each column. + :return: Numpy array of doubles: y coordinate of grid cell center for each grid cell, in the same order as the values returned by function get_value. + """ - return self.myModel.pcr2numpy(self.myModel.ycoordinate(1),0.0) + return self.dynModel.wf_supplyMapYAsNumpy() - - def get_var_count(self): + def get_grid_z(self, long_var_name): """ - Return number of variables - """ - return self.dynModel.wf_supplyVariableCount() + Give Z coordinates of point in the model grid + :var String long_var_name: identifier of a variable in the model. - def get_var_name(self, i): + :return: Numpy array of doubles: z coordinate of grid cell center for each grid cell, in the same order as the values returned by function get_value. """ - Return variable name + return self.dynModel.wf_supplyMapZAsNumpy() + + def get_var_units(self, long_var_name): """ + Not implemented yet - names = self.dynModel.wf_supplyVariableNames() - return names[i] + :var long_var_name: identifier of a variable in the model. + :return: String: unit of the values of the given variable. Return a string formatted + using the UDUNITS standard from Unidata. + """ + raise NotImplementedError - def get_var_shape(self, name): + def set_value(self, long_var_name, src): """ - Return shape of the array. - """ - npmap = self.dynModel.wf_supplyMapAsNumpy(name) + Set the values(s) in a map using a numpy array as source - return npmap.shape - - - - - - def set_var(self, name, var): + :var long_var_name: identifier of a variable in the model. + :var src: all values to set for the given variable. """ - Set the variable name with the values of var - Assume var is a numpy array - """ #TODO: check the numpy type - self.dynModel.wf_setValuesAsNumpy(name, var) + self.dynModel.wf_setValuesAsNumpy(long_var_name, src) - - def set_var_slice(self, name, start, count, var): + def get_grid_connectivity(self, long_var_name): """ - Overwrite the values in variable name with data - from var, in the range (start:start+count). - Start, count can be integers for rank 1, and can be - tuples of integers for higher ranks. - For some implementations it can be equivalent and more efficient to do: - `get_var(name)[start[0]:start[0]+count[0], ..., start[n]:start[n]+count[n]] = var` + Not applicable, raises NotImplementedError """ - tmp = self.get_var(name).copy() - try: - # if we have start and count as a number we can do this - tmp[start:(start+count)] = var - except: - # otherwise we have to loop over all dimensions - slices = [np.s_[i:(i+n)] for i,n in zip(start, count)] - tmp[slices] - self.set_var(name, name, tmp) + raise NotImplementedError - def set_var_index(self, name, index, var): + def get_grid_offset(self, long_var_name): """ - Overwrite the values in variable "name" with data - from var, at the flattened (C-contiguous style) - indices. Indices is a vector of 0-based - integers, of the same length as the vector var. - For some implementations it can be equivalent - and more efficient to do: - `get_var(name).flat[index] = var` + Not applicable raises NotImplementedError """ - tmp = self.get_var(name).copy() - tmp.flat[index] = var - self.set_var(name, name, tmp) + raise NotImplementedError - - class BmiGridType(object): UNKNOWN = 0 UNIFORM = 1