Index: openda_bmi/bmi.py =================================================================== diff -u -r67ee1270099b7f14962d5cf00571d3c3af3a851c -rc351ce983c1eb3ce64a24cb1340d01faa69d3612 --- openda_bmi/bmi.py (.../bmi.py) (revision 67ee1270099b7f14962d5cf00571d3c3af3a851c) +++ openda_bmi/bmi.py (.../bmi.py) (revision c351ce983c1eb3ce64a24cb1340d01faa69d3612) @@ -1,26 +1,14 @@ #! /usr/bin/env python """ -2015-03-06 -Deltares -Arno Kockx +To create this file the original version of the CSDMS BMI Python Language Binding +from https://github.com/csdms/bmi-python/blob/master/bmi/bmi.py was extended with a number of extra functions +so that it can be used in OpenDA. All extra functions are contained in the class EBmi. -To create this file the original version of the CSDMS BMI Python Language Binding (file bmi.py) from https://github.com/csdms/bmi-python/blob/master/bmi/bmi.py was extended so that it can be used in OpenDA. - -The following changes were made: -1. All grid information functions have been merged into the Bmi class, so that different variables within the same model can have different grids. -2. Added function get_grid_type to get the grid type for a given variable. -3. Added function save_state to ask the model to save its state to disk. -4. Added comments. Where the original version of the CSDMS BMI Python Language Binding was ambiguous, the information from http://csdms.colorado.edu/wiki/BMI_Description and common sense were used to fill in most of the gaps. +Also added additional comments. Where the original version of the CSDMS BMI Python Language Binding was ambiguous, the +information from http://csdms.colorado.edu/wiki/BMI_Description and common sense were used to fill in most of the gaps. """ -""" -2018-2-10 -Netherlands eScience Center -Gijs van den Oord -Modifications to comply with modern csdms BMI. -""" - from abc import ABCMeta, abstractmethod @@ -29,17 +17,20 @@ Enumeration with grid types. """ - UNIFORM = "uniform_rectilinear" - RECTILINEAR = "rectilinear" - STRUCTURED = "structured_quadrilateral" - UNSTRUCTURED = "unstructured" + UNKNOWN = 0 + UNIFORM = 1 + RECTILINEAR = 2 + STRUCTURED = 3 + UNSTRUCTURED = 4 -class Bmi(object, metaclass=ABCMeta): +class Bmi(object): """ Interface (abstract base class) for a model that implements the CSDMS BMI (Basic Model Interface). """ + __metaclass__ = ABCMeta + """ Model Control Functions """ @@ -82,17 +73,6 @@ raise NotImplementedError @abstractmethod - 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. - - Input parameters: - File destination_directory: the directory in which the state files should be written. - """ - raise NotImplementedError - - @abstractmethod def finalize(self): """ Finalize the model. @@ -182,22 +162,11 @@ String long_var_name: identifier of a variable in the model. Return value: - Integer: total number of bytes taken by the return array of get_var(long_var_name) + ???: ??? """ raise NotImplementedError @abstractmethod - def get_var_itemsize(self, long_var_name): - """ - Input parameters: - String long_var_name: identifier of a variable in the model. - - Return value: - Integer: size, in bytes, of each item of the variable - """ - raise NotImplementedError - - @abstractmethod def get_start_time(self): """ Return value: @@ -248,8 +217,8 @@ String long_var_name: identifier of a variable in the model. Return value: - Flat numpy array of values in the data type returned by the function get_var_type: all values of the given - variable. + Numpy array of values in the data type returned by the function get_var_type: all values of the given variable. + For a 2D grid these values must be in row major order, starting with the bottom row. """ raise NotImplementedError @@ -258,10 +227,13 @@ """ Input parameters: String long_var_name: identifier of a variable in the model. - Lists of integers inds: denotes the indices within the flattened list that get_value(long_var_name) returns. + 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, + e.g. [[0, 0, 0], [0, 0, 1], [0, 15, 19], [0, 15, 20], [0, 15, 21]] indicates 5 elements in a 3D grid. + For a grid the indices start counting at the grid origin, i.e. the lower left corner for a 2D grid. + Return value: - Numpy array of values in the data type returned by the function get_var_type: one value for each of the - indicated elements. + Numpy array of values in the data type returned by the function get_var_type: one value for each of the indicated elements. """ raise NotImplementedError @@ -270,7 +242,8 @@ """ Input parameters: String long_var_name: identifier of a variable in the model. - Numpy array of values src: all values to set for the given variable. + Numpy array of values in the data type returned by the function get_var_type src: all values to set for the given variable. + For a 2D grid these values must be in row major order, starting with the bottom row. """ raise NotImplementedError @@ -279,8 +252,11 @@ """ Input parameters: String long_var_name: identifier of a variable in the model. - Lists of integers inds: denotes the indices within the flattened list that get_value(long_var_name) returns. - Numpy array of values src: one value to set for each of the indicated elements. + 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, + e.g. [[0, 0], [0, 1], [15, 19], [15, 20], [15, 21]] indicates 5 elements in a 2D grid. + For a grid the indices start counting at the grid origin, i.e. the lower left corner for a 2D grid. + Numpy array of values in the data type returned by the function get_var_type src: one value to set for each of the indicated elements. """ raise NotImplementedError @@ -289,219 +265,186 @@ """ @abstractmethod - def get_var_grid(self, long_var_name): + def get_grid_type(self, long_var_name): """ Input parameters: String long_var_name: identifier of a variable in the model. Return value: - Integer: identifier for the grid on which the variable is defined. + BmiGridType type of the grid geometry of the given variable. """ raise NotImplementedError @abstractmethod - def get_grid_type(self, grid_id): + def get_grid_shape(self, long_var_name): """ - Input parameters: - Integer grid_id: identifier of a grid in the model. - - Return value: - string type of the grid geometry of the given variable. - """ - raise NotImplementedError - - @abstractmethod - def get_grid_rank(self, grid_id): - """ - Input parameters: - Integer grid_id: identifier of a grid in the model. - - Return value: - Integer: number of values returned by get_grid_shape(grid_id) - """ - raise NotImplementedError - - @abstractmethod - def get_grid_shape(self, grid_id): - """ Only return something for variables with a uniform, rectilinear or structured grid. Otherwise raise ValueError. Input parameters: - Integer grid_id: identifier of a grid in the model. + String long_var_name: identifier of a variable in the model. Return value: - List of integers: the sizes of the dimensions of the given variable, e.g. [500, 400] for a 2D grid with 500x400 - grid cells. + List of integers: the sizes of the dimensions of the given variable, e.g. [400, 500] for a 2D grid with 400 rows and 500 columns. + The dimensions are ordered [y, x] or [z, y, x]. """ raise NotImplementedError @abstractmethod - def get_grid_size(self, grid_id): + def get_grid_spacing(self, long_var_name): """ - Input parameters: - Integer grid_id: identifier of a grid in the model. - Integer: The total size (nr of cells) of the grid with identifier grid_id - """ - raise NotImplementedError - - @abstractmethod - def get_grid_spacing(self, grid_id): - """ Only return something for variables with a uniform grid. Otherwise raise ValueError. Input parameters: - Integer grid_id: identifier of a grid in the model. + String long_var_name: identifier of a variable in the model. Return value: - Double: in case of a uniform equidistant grid, returns the distance between two neighboring cell centres. + List of doubles: the size of a grid cell for each of the dimensions of the given variable, e.g. [cellHeight, cellWidth] for a 2D grid. + The dimensions are ordered [y, x] or [z, y, x]. """ raise NotImplementedError @abstractmethod - def get_grid_origin(self, grid_id): + def get_grid_origin(self, long_var_name): """ Only return something for variables with a uniform grid. Otherwise raise ValueError. Input parameters: - Integer grid_id: identifier of a grid in the model. + String long_var_name: identifier of a variable in the model. Return value: - List of doubles: components of the location of the lower-left corner of the grid + 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. + The dimensions are ordered [y, x] or [z, y, x]. """ raise NotImplementedError @abstractmethod - def get_grid_x(self, grid_id): + def get_grid_x(self, long_var_name): """ - Only return something for variables with a rectilinear, structured or unstructured grid. Otherwise raise - ValueError. + Only return something for variables with a rectilinear, structured or unstructured grid. Otherwise raise ValueError. Input parameters: 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. + 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. """ raise NotImplementedError @abstractmethod - def get_grid_y(self, grid_id): + def get_grid_y(self, long_var_name): """ - Only return something for variables with a rectilinear, structured or unstructured grid. Otherwise raise - ValueError. + Only return something for variables with a rectilinear, structured or unstructured grid. Otherwise raise ValueError. Input parameters: String long_var_name: identifier of a variable in the model. Return value: - 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. + 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. For a rectilinear grid: y coordinate of row center for each row. """ raise NotImplementedError @abstractmethod - def get_grid_z(self, grid_id): + def get_grid_z(self, long_var_name): """ - Only return something for variables with a rectilinear, structured or unstructured grid. Otherwise raise - ValueError. + Only return something for variables with a rectilinear, structured or unstructured grid. Otherwise raise ValueError. Input parameters: String long_var_name: identifier of a variable in the model. Return value: - 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. + 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. For a rectilinear grid: z coordinate of layer center for each layer. """ raise NotImplementedError @abstractmethod - def get_grid_connectivity(self, grid_id): + def get_grid_connectivity(self, long_var_name): """ Only return something for variables with an unstructured grid. Otherwise raise ValueError. Input parameters: String long_var_name: identifier of a variable in the model. Return value: - List of integers, defining the cell corners for each cell. + ??? """ raise NotImplementedError @abstractmethod - def get_grid_offset(self, grid_id): + def get_grid_offset(self, long_var_name): """ Only return something for variables with an unstructured grid. Otherwise raise ValueError. Input parameters: String long_var_name: identifier of a variable in the model. Return value: - List of integers, defining the grid offset for each cell. + ??? """ raise NotImplementedError - - + + class EBmi(Bmi): + @abstractmethod def initialize_config(self, config_file): """ First step of two-phase initialize. In this step only the configuration is read in. This allows a user to then change settings and parameters before fully initializing the model """ raise NotImplementedError - + @abstractmethod def initialize_model(self, source_directory): """ Second step of two-phase initialize. In this step the model is setup, and will now allow reading/setting values. """ raise NotImplementedError - + @abstractmethod def set_start_time(self, start_time): """ Set the start time of the model. Can usually only be called after initialize_config and before initialize_model. Expects a value in the time units of the model """ raise NotImplementedError - + @abstractmethod def set_end_time(self, end_time): """ Set the end time of the model. Can usually only be called after initialize_config and before initialize_model. Expects a value in the time units of the model. """ raise NotImplementedError - + @abstractmethod def get_attribute_names(self): """ Gets a list of all supported attributes for this model. Attributes can be considered the meta-data of a model, for instance author, version, model specific settings, etc. """ raise NotImplementedError - + @abstractmethod def get_attribute_value(self, attribute_name): """ Gets the value of a certain attribute for this model. Attributes can be considered the meta-data of a model, for instance author, version, model specific settings, etc. """ raise NotImplementedError - + @abstractmethod def set_attribute_value(self, attribute_name, attribute_value): """ Sets the value of a certain attribute for this model. Usually only string values are allowed. Attributes can be considered the meta-data of a model, for instance author, version, model specific settings, etc. """ raise NotImplementedError - + @abstractmethod def save_state(self, destination_directory): """ @@ -512,7 +455,7 @@ File destination_directory: the directory in which the state files should be written. """ raise NotImplementedError - + @abstractmethod def load_state(self, source_directory): """