diff --git a/examples/README.md b/examples/README.md index 54ecb2f..1a75257 100644 --- a/examples/README.md +++ b/examples/README.md @@ -93,8 +93,8 @@ be found at the beginning of each file. * [put_varn_int.py](./put_varn_int.py) + This example shows how to use a single call of `Variable` method - `put_var()` to to write a sequence of requests with arbitrary array indices - and lengths. + `put_varn()` to write a sequence of subarray requests to a variable with + arbitrary array indices and lengths. * [transpose.py](./transpose.py) + This example shows how to use `Variable` method `put_var()` to write six 3D diff --git a/examples/get_vara.py b/examples/get_vara.py index 84d5094..d78e159 100644 --- a/examples/get_vara.py +++ b/examples/get_vara.py @@ -95,8 +95,8 @@ def pnetcdf_io(filename, file_format): if rank == 0 and verbose: print("variable attribute \"str_att_name\" of type text =", str_att) - # Get the length of the variable's attribute named "float_att_name" - float_att = v.get_att("float_att_name") + # Get the variable's attribute named "float_att_name" + float_att = v.get_att("float_att_name") # set access pattern for reading subarray local_ny = global_ny diff --git a/examples/put_varn_int.py b/examples/put_varn_int.py index 7193e98..5a082de 100644 --- a/examples/put_varn_int.py +++ b/examples/put_varn_int.py @@ -4,8 +4,8 @@ # """ -This example shows how to use a single call of `Variable` method `put_var()` to -to write a sequence of requests with arbitrary array indices and lengths. +This example shows how to use a single call of `Variable` method `put_varn()` +to to write a sequence of requests with arbitrary array indices and lengths. To run: % mpiexec -n num_process python3 put_varn_int.py [test_file_name] @@ -153,7 +153,7 @@ def pnetcdf_io(file_name, file_format): w_buf = np.full(w_len, rank, dtype=np.int32) # set the buffer pointers to different offsets to the I/O buffe - v.put_var_all(w_buf, start = starts, count = counts, num = num_reqs) + v.put_varn_all(w_buf, num = num_reqs, starts = starts, counts = counts) # close the file f.close() @@ -187,7 +187,7 @@ def pnetcdf_io(file_name, file_format): filename = args.dir if verbose and rank == 0: - print("{}: example of writing multiple variables in a call".format(os.path.basename(__file__))) + print("{}: example of writing multiple subarrays of a variable in a call".format(os.path.basename(__file__))) try: pnetcdf_io(filename, file_format) diff --git a/src/pnetcdf/_File.pyx b/src/pnetcdf/_File.pyx index c1f4e28..928f007 100644 --- a/src/pnetcdf/_File.pyx +++ b/src/pnetcdf/_File.pyx @@ -499,12 +499,11 @@ cdef class File: Operational mode: This method must be called while the file is in define mode. """ - - cdef nc_type xtype xtype=-99 _set_att(self, NC_GLOBAL, name, value, xtype=xtype) + def get_att(self,name,encoding='utf-8'): """ get_att(self,name,encoding='utf-8') @@ -519,14 +518,14 @@ cdef class File: :param name: Name of the attribute. :type name: str - :rtype: str + :rtype: str or numpy.ndarray - Operational mode: This method must be called while the file is in define mode. + Operational mode: This method can be called while the file is in either + define or data mode (collective or independent). """ - - return _get_att(self, NC_GLOBAL, name, encoding=encoding) + def __delattr__(self,name): # if it's a netCDF attribute, remove it if name not in _private_atts: diff --git a/src/pnetcdf/_Variable.pyx b/src/pnetcdf/_Variable.pyx index 5944ac5..4b3c680 100644 --- a/src/pnetcdf/_Variable.pyx +++ b/src/pnetcdf/_Variable.pyx @@ -34,14 +34,11 @@ ctypedef MPI.Datatype Datatype cdef class Variable: """ A PnetCDF variable is used to read and write netCDF data. They are - analogous to numpy array objects. See ``Variable.__init__`` for more + analogous to numpy array objects. See :meth:`Variable.__init__` for more details. - .. note:: ``Variable`` instances should be created using the - `File.def_var` method of a `File` instance, not using this class constructor - directly. - - + .. note:: ``Variable`` instances should be created using the `File.def_var` + method of a `File` instance, not using this class constructor directly. """ def __init__(self, file, name, datatype, dimensions=(), **kwargs): @@ -53,8 +50,9 @@ cdef class Variable: :param varname: Name of the new variable. :type varname: str - :param datatype: The datatype of the new variable. Can be specified by providing a NC module constant, - or numpy dtype object, or a string that describes a numpy dtype object. Supported specifiers are: + :param datatype: The datatype of the new variable. Can be specified by + providing a NC module constant, or numpy dtype object, or a string + that describes a numpy dtype object. Supported specifiers are: - ``pnetcdf.NC_CHAR`` or ``S1`` for 1-character string - ``pnetcdf.NC_BYTE`` or ``i1`` for 1-byte integer @@ -67,24 +65,26 @@ cdef class Variable: - ``pnetcdf.NC_UBYTE`` or ``u1`` for unsigned 1-byte integer - ``pnetcdf.NC_USHORT`` or ``u2`` for unsigned 2-byte integer - - ``pnetcdf.NC_UINT`` or ``u4`` for unsigned 4-byte intege + - ``pnetcdf.NC_UINT`` or ``u4`` for unsigned 4-byte integer - ``pnetcdf.NC_INT64`` or ``i8`` for signed 8-byte integer - ``pnetcdf.NC_UINT64`` or ``u8`` for unsigned 8-byte integer :type datatype: int - :param dimensions: [Optional] The dimensions of the new variable. Can be either dimension names - or dimension class instances. Default is an empty tuple which means the variable is - a scalar (and therefore has no dimensions). + :param dimensions: [Optional] + The dimensions of the new variable. Can be either dimension names + or dimension class instances. Default is an empty tuple which means + the variable is a scalar (and therefore has no dimensions). :type dimensions: tuple of str or :class:`pnetcdf.Dimension` instances - :param fill_value: The fill value of the new variable. Accepted values are: + :param fill_value: + The fill value of the new variable. Accepted values are: - ``None``: use the default fill value for the given datatype - ``False``: fill mode is turned off - If specified with other value, the default netCDF `_FillValue` (the - value that the variable gets filled with before any data is written to it) - is replaced with this value. + value that the variable gets filled with before any data is + written to it) is replaced with this value. :return: The created variable :rtype: :class:`pnetcdf.Variable` @@ -104,7 +104,7 @@ cdef class Variable: self._file = file _file_id = self._file_id #TODO: decide whether we need to check xtype at python-level - if isinstance(datatype, str): # conver to numpy datatype object + if isinstance(datatype, str): # convert to numpy datatype object datatype = np.dtype(datatype) if isinstance(datatype, np.dtype): if datatype.str[1:] in _supportedtypes: @@ -209,7 +209,7 @@ cdef class Variable: return '\n'.join(ncdump) def _getdims(self): - # Private method to get variables's dimension names + # Private method to get variable's dimension names cdef int ierr, numdims, n, nn cdef char namstring[NC_MAX_NAME+1] cdef int *dimids @@ -251,7 +251,7 @@ cdef class Variable: raise AttributeError("name cannot be altered") property datatype: - """Return the mapped numpy data type of the vairable netCDF datatype """ + """Return the mapped numpy data type of the variable netCDF datatype """ def __get__(self): return self.dtype @@ -273,7 +273,7 @@ cdef class Variable: return int(np.prod(self.shape)) property dimensions: - """Get variables's dimension names""" + """Get variable's dimension names""" def __get__(self): return self._getdims() def __set__(self,value): @@ -287,29 +287,32 @@ cdef class Variable: :rtype: :class:`pnetcdf.File` """ return self._file + def ncattrs(self): """ ncattrs(self) - Return netCDF attribute names for this variable in a list. + :return: all attribute names of this variable in a list. :rtype: list """ return _get_att_names(self._file_id, self._varid) + def put_att(self,name,value): """ put_att(self,name,value) - Set an attribute for this variable using name,value pair. Especially - useful when you need to set a netCDF attribute with the - with the same name as one of the reserved python attributes. + Method to add or change a variable attribute. If this attribute is new, + or if the space required to store the attribute is greater than before, + the netCDF file must be in define mode. - :param name: Name of the new attribute. + :param name: Name of the attribute. :type name: str - :param value: Value of the new attribute. - :type value: str, int, float or list of int and float + :param value: Value of the attribute. + :type value: str or numpy.ndarray - Operational mode: This method must be called while the associated netCDF file is in define mode. + Operational mode: This method must be called while the associated + netCDF file is in define mode. """ cdef nc_type xtype xtype=-99 @@ -320,18 +323,19 @@ cdef class Variable: """ get_att(self,name,encoding='utf-8') - Retrieve a netCDF variable attribute. Useful when you need to get a netCDF attribute with the same - name as one of the reserved python attributes. - - option kwarg `encoding` can be used to specify the - character encoding of a string attribute (default is `utf-8`). + Retrieve an attribute of a netCDF variable. :param name: Name of the attribute. :type name: str - :rtype: str + :param encoding: specify the character encoding of a string attribute + (default is `utf-8`). + :type name: str + + :rtype: str or numpy.ndarray - Operational mode: This method must be called while the associated netCDF file is in define mode. + Operational mode: This method can be called while the file is in either + define or data mode (collective or independent). """ return _get_att(self._file, self._varid, name, encoding=encoding) @@ -339,14 +343,13 @@ cdef class Variable: """ del_att(self,name,value) - Delete a netCDF variable attribute. Useful when you need to delete a - netCDF attribute with the same name as one of the reserved python - attributes. + Delete a netCDF variable attribute. :param name: Name of the attribute :type name: str - Operational mode: This method must be called while the associated netCDF file is in define mode. + Operational mode: This method must be called while the associated + netCDF file is in define mode. """ cdef char *attname bytestr = _strencode(name) @@ -418,9 +421,9 @@ cdef class Variable: :param oldname: Old name of the attribute. :type oldname: str - Operational mode: If the new name is longer than the original name, the associated netCDF file must be in define mode. - Otherwise, the netCDF file can be in either define or data mode. - + Operational mode: If the new name is longer than the original name, the + associated netCDF file must be in define mode. Otherwise, the netCDF + file can be in either define or data mode. """ cdef char *oldnamec cdef char *newnamec @@ -437,10 +440,9 @@ cdef class Variable: """ get_dims(self) - return a tuple of ``Dimension`` instances associated with this - `Variable`. + :return: a tuple of ``Dimension`` instances associated with this + variable. :rtype: tuple of ``Dimension`` - """ return tuple(self._file.dimensions[dim] for dim in self.dimensions) @@ -448,14 +450,17 @@ cdef class Variable: """ def_fill(self, int no_fill, fill_value = None) - Sets the fill mode parameters for a variable. + Sets the fill mode for a variable. This API must be called while the + file is in the define mode, and after the variable is defined. - :param no_fill: Set no_fill mode for a variable. 1 for on (not to fill) and 0 for off (to fill). + :param no_fill: Set no_fill mode for a variable. 1 for on (not to fill) + and 0 for off (to fill). :type no_fill: int - :param fill_value: Sets the customized fill value. Must be the same type as the variable. This will - be written to a _FillValue attribute, created for this purpose. If None, this argument will be ignored - and the default fill value is used. + :param fill_value: Sets the customized fill value. Must be the same + type as the variable. This will be written to a `_FillValue` + attribute, created for this purpose. If None, this argument will be + ignored and the default fill value is used. :type fill_value: any """ @@ -477,14 +482,15 @@ cdef class Variable: """ inq_fill(self) - Returns te fill mode settings of this variable. A tuple of two values representing no_fill mode and fill value. + Returns the fill mode settings of this variable. A tuple of two values + representing no_fill mode and fill value. + + :return: A tuple of two values which are ``no_fill`` mode and + ``fill_value``, respectively. - :return: A tuple of two value which are ``no_fill`` mode and ``fill_value``. - ``no_fill``: will be 1 if no_fill mode is set (else 0). - ``fill_value``: the fill value for this variable. - :rtype: tuple - """ cdef int ierr, no_fill @@ -500,11 +506,11 @@ cdef class Variable: """ fill_rec(self, int rec_no) - Fills a record of a record variable with predefined or user-defined fill values. + Fills a record of a record variable with predefined or user-defined + fill values. :param rec_no: the index of the record to be filled :type rec_no: int - """ cdef int recno, ierr recno = rec_no @@ -520,11 +526,12 @@ cdef class Variable: from numpy fixed length string arrays when the `_Encoding` variable attribute is set. - If `chartostring` is set to `True`, when data is read from a character variable - (dtype = `S1`) that has an `_Encoding` attribute, it is converted to a numpy - fixed length unicode string array (dtype = `UN`, where `N` is the length - of the the rightmost dimension of the variable). The value of `_Encoding` - is the unicode encoding that is used to decode the bytes into strings. + If `chartostring` is set to `True`, when data is read from a character + variable (dtype = `S1`) that has an `_Encoding` attribute, it is + converted to a numpy fixed length unicode string array (dtype = `UN`, + where `N` is the length of the rightmost dimension of the variable). + The value of `_Encoding` is the unicode encoding that is used to decode + the bytes into strings. When numpy string data is written to a variable it is converted back to indiviual bytes, with the number of bytes in each string equalling the @@ -560,8 +567,9 @@ cdef class Variable: scale_factor is assumed to be one. In addition, if `scale` is set to `True`, and if the variable has an - attribute `_Unsigned` set, and the variable has a signed integer data type, - a view to the data is returned with the corresponding unsigned integer datatype. + attribute `_Unsigned` set, and the variable has a signed integer data + type, a view to the data is returned with the corresponding unsigned + integer datatype. The default value of `scale` is `False` @@ -576,20 +584,20 @@ cdef class Variable: Turn on or off automatic conversion of variable data to and from masked arrays. - If `mask` is set to `True`, when data is read from a variable - it is converted to a masked array if any of the values are exactly - equal to the either the netCDF _FillValue or the value specified by the - missing_value variable attribute. The fill_value of the masked array - is set to the missing_value attribute (if it exists), otherwise - the netCDF _FillValue attribute (which has a default value - for each data type). If the variable has no missing_value attribute, the - _FillValue is used instead. If the variable has valid_min/valid_max and - missing_value attributes, data outside the specified range will be masked. - When data is written to a variable, the masked - array is converted back to a regular numpy array by replacing all the - masked values by the missing_value attribute of the variable (if it - exists). If the variable has no missing_value attribute, the _FillValue - is used instead. + If `mask` is set to `True`, when data is read from a variable it is + converted to a masked array if any of the values are exactly equal to + the either the netCDF _FillValue or the value specified by the + missing_value variable attribute. The fill_value of the masked array is + set to the missing_value attribute (if it exists), otherwise the netCDF + _FillValue attribute (which has a default value for each data type). If + the variable has no missing_value attribute, the _FillValue is used + instead. If the variable has valid_min/valid_max and missing_value + attributes, data outside the specified range will be masked. When data + is written to a variable, the masked array is converted back to a + regular numpy array by replacing all the masked values by the + missing_value attribute of the variable (if it exists). If the + variable has no missing_value attribute, the _FillValue is used + instead. The default value of `mask` is `False` """ @@ -620,7 +628,7 @@ cdef class Variable: # create mask from missing values. mvalmask = np.zeros(data.shape, np.bool_) if mval.shape == (): # mval a scalar. - mval = [mval] # make into iterable. + mval = [mval] # make into iterate-able. for m in mval: # is scalar missing value a NaN? try: @@ -1111,28 +1119,28 @@ cdef class Variable: PyArray_DATA(data), buffcount, bufftype) _check_err(ierr) - def _put_varn(self, start, count, num, ndarray data, bufcount, Datatype buftype, collective = True): + def _put_varn(self, ndarray data, num, starts, counts=None, bufcount=None, buftype=None, collective = True): cdef int ierr, ndims cdef MPI_Offset buffcount cdef MPI_Datatype bufftype - cdef size_t **startp - cdef size_t **countp + cdef size_t **startsp + cdef size_t **countsp cdef int num_req num_req = num ndims = len(self.dimensions) - max_num_req = len(start) + max_num_req = len(starts) - startp = malloc(max_num_req * sizeof(size_t*)); + startsp = malloc(max_num_req * sizeof(size_t*)); for i in range(max_num_req): - startp[i] = malloc(ndims * sizeof(size_t)); + startsp[i] = malloc(ndims * sizeof(size_t)); for j in range(ndims): - startp[i][j] = start[i, j] + startsp[i][j] = starts[i, j] - countp = malloc(max_num_req * sizeof(size_t*)); + countsp = malloc(max_num_req * sizeof(size_t*)); for i in range(max_num_req): - countp[i] = malloc(ndims * sizeof(size_t)); + countsp[i] = malloc(ndims * sizeof(size_t)); for j in range(ndims): - countp[i][j] = count[i, j] + countsp[i][j] = counts[i, j] if not PyArray_ISCONTIGUOUS(data): data = data.copy() @@ -1151,14 +1159,183 @@ cdef class Variable: bufftype = buftype.ob_mpi if collective: with nogil: - ierr = ncmpi_put_varn_all(self._file_id, self._varid, num_req, startp, countp,\ - PyArray_DATA(data), buffcount, bufftype) + ierr = ncmpi_put_varn_all(self._file_id, + self._varid, + num_req, + startsp, + countsp, + PyArray_DATA(data), + buffcount, + bufftype) else: with nogil: - ierr = ncmpi_put_varn(self._file_id, self._varid, num_req, startp, countp,\ - PyArray_DATA(data), buffcount, bufftype) + ierr = ncmpi_put_varn(self._file_id, + self._varid, + num_req, + startsp, + countsp, + PyArray_DATA(data), + buffcount, + bufftype) _check_err(ierr) + def put_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None): + """ + put_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None) + + Method call to write to one or more netCDF variables in independent I/O + mode. The behavior of the method varies depends on the pattern of + provided optional arguments - `starts`, and `counts`. This method is + equivalent to making multiple calls to :meth:`Variable.put_vara`, + same variable or more than one variable. Combining multiple `put_vara` + calls may achieve a better performance. + + - `data`, `num`,`starts`, `counts` - Write multiple subarrays of + values The part of the netCDF variable to write is specified by + giving multiple subarrays and each subarray is specified by a + corner and a vector of edge lengths that refer to an array section + of the netCDF variable. The example code and diagram below + illustrates a 4 subarray section in a 4 * 10 two-dimensional + variable ("-" means skip). + + :: + + num = 4 + starts[0][0] = 0; starts[0][1] = 5; counts[0][0] = 1; counts[0][1] = 2 + starts[1][0] = 1; starts[1][1] = 0; counts[1][0] = 1; counts[1][1] = 1 + starts[2][0] = 2; starts[2][1] = 6; counts[2][0] = 1; counts[2][1] = 2 + starts[3][0] = 3; starts[3][1] = 0; counts[3][0] = 1; counts[3][1] = 3 + - - - - - a b - - - + a b c d e f g h -> c - - - - - - - - - + - - - - - - d e - - + f g h - - - - - - - + + :param data: the numpy array that stores array values to be written, + which serves as a write buffer. When writing a single data value, + it can also be a single numeric (e.g. np.int32) python variable. + The datatype should match with the variable's datatype. Note this + numpy array write buffer can be in any shape as long as the number + of elements (buffer size) is matched. If the in-memory type of data + values differs from the netCDF variable type defined in the file, + type conversion will automatically be applied. + :type data: numpy.ndarray + + :param num: An integer specifying the number of subarrays. + :type num: int + + :param starts: + A 2D array of integers containing starting array indices + of `num` number of subarrays. The first dimension of `starts` + should be of size `num`, indicating the number of subarrays of the + variable to be written. The second dimension is of size equal to + the number dimensions of the variable. For example, when `num` = 3 + and the variable defined in the file is a 2D array, `starts` should + be a 3x2 array. Each of the subarray starting indices identify + the indices in the variable where the first of the data values will + be written. Each `starts[i]` is a vector specifying the index in + the variable where the first of the data values will be written. + The elements of `starts[i][*]` must correspond to the variable’s + dimensions in order. Hence, if the variable is a record variable, + the first index, `starts[i][0]` would correspond to the starting + record number for writing the data values. + :type starts: numpy.ndarray + + :param counts: [Optional] + A 2D array of integers specifying the lengths along each + dimension of `num` number of subarrays to be written. The first + dimension of `counts` should be of size `num`, indicating the + number of subarrays of the variable to be written. The second + dimension is of size equal to the number dimensions of the + variable. For example, when `num` = 3 and the variable defined in + the file is a 2D array, `counts` should be a 3x2 array. Each of + the subarray `counts[i]` is a vector specifying the lengths along + each dimension of the block of data values to be written and must + correspond to the variable’s dimensions in order. When this + argument is not supplied, it is equivalent to providing counts of + all 1s. + :type counts: numpy.ndarray + + :param bufcount: [Optional] + An integer indicates the number of MPI derived data type elements + in the write buffer to be written to the file. + :type bufcount: int + + :param buftype: [Optional] + An MPI derived data type that describes the memory layout of the + write buffer. + :type buftype: mpi4py.MPI.Datatype + """ + self._put_varn(data, num, starts, counts, bufcount = bufcount, + buftype = buftype, collective = False) + + def put_varn_all(self, data, num, starts, counts=None, bufcount=None, buftype=None): + """ + put_varn_all(self, data, num, starts, counts=None, bufcount=None, buftype=None) + + This method call is the same as method :meth:`Variable.put_varn`, + except it is collective and can only be called in the collective I/O + mode. Please refer to :meth:`Variable.put_varn` for its argument + usage. + """ + self._put_varn(data, num, starts, counts, bufcount = bufcount, + buftype = buftype, collective = True) + + def iput_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None): + """ + iput_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None) + + This method call is the nonblocking counterpart of + :meth:`Variable.put_varn`. The syntax is the same as + :meth:`Variable.put_varn`. For the argument usage, please refer to + method :meth:`Variable.put_varn`. This method returns a request ID + that can be used in :meth:`File.wait` or :meth:`File.wait_all`. The + posted write request may not be committed until :meth:`File.wait` or + :meth:`File.wait_all` is called. + + .. note:: + Unlike :meth:`Variable.put_varn`, the posted nonblocking write + requests may not be committed to the file until the time of calling + :meth:`File.wait` or :meth:`File.wait_all`. Users should not + alter the contents of the write buffer once the request is posted + until the :meth:`File.wait` or :meth:`File.wait_all` is + returned. Any change to the buffer contents in between will result + in unexpected error. + + :return: The request ID, which can be used in a successive call to + :meth:`File.wait` or :meth:`File.wait_all` for the completion + of the nonblocking operation. + :rtype: int + """ + return self._iput_varn(data, num, starts, counts, bufcount, buftype, + buffered=False) + + def bput_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None): + """ + bput_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None) + + This method call is the nonblocking, buffered counterpart of + :meth:`Variable.put_varn`. For the argument usage, please refer to + method :meth:`Variable.put_varn`. This method returns a request ID + that can be used in :meth:`File.wait` or :meth:`File.wait_all`. The + posted write request may not be committed until :meth:`File.wait` or + :meth:`File.wait_all` is called. + + .. note:: + Unlike :meth:`Variable.iput_varn`, the write data is buffered + (cached) internally by PnetCDF and will be flushed to the file at + the time of calling :meth:`File.wait` or :meth:`File.wait_all`. + Once the call to this method returns, the caller is free to change + the contents of write buffer. Prior to calling this method, make + sure :meth:`File.attach_buff` is called to allocate an internal + buffer for accommodating the write requests. + + :return: The request ID, which can be used in a successive call to + :meth:`File.wait` or :meth:`File.wait_all` for the completion + of the nonblocking operation. + :rtype: int + """ + return self._iput_varn(data, num, starts, counts, bufcount, buftype, buffered=True) + def _put_vars(self, start, count, stride, ndarray data, bufcount, Datatype buftype, collective = True): cdef int ierr, ndims cdef MPI_Offset buffcount @@ -1255,14 +1432,14 @@ cdef class Variable: - def put_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None): + def put_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None): """ - put_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None) + put_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None) Method call to write in parallel to the netCDF variable in independent I/O mode. The behavior of the method varies depends on the pattern of - provided optional arguments - `start`, `count`, `stride`, `num` and - `imap`. + provided optional arguments - `start`, `count`, `stride`, `bufcount` + and `buftype`. - `data` - Write an entire variable Write a netCDF variable entirely of an opened netCDF file, i.e. @@ -1329,24 +1506,6 @@ cdef class Variable: distance from a to b is 3 in buffer => imap[0] = 3 distance from a to c is 8 in buffer => imap[1] = 8 - - `data`, `start`, `count`, `num` - Write a list of subarrays of values - The part of the netCDF variable to write is specified by giving a list of subarrays and each subarray is specified by a corner and a vector of - edge lengths that refer to an array section of the netCDF variable. The example code and diagram below illustrates a lists of 4 specified - subarray sections in a 4 * 10 two-dimensional variable ("-" means skip). - - - :: - - num = 4 - start[0][0] = 0; start[0][1] = 5; count[0][0] = 1; count[0][1] = 2 - start[1][0] = 1; start[1][1] = 0; count[1][0] = 1; count[1][1] = 1 - start[2][0] = 2; start[2][1] = 6; count[2][0] = 1; count[2][1] = 2 - start[3][0] = 3; start[3][1] = 0; count[3][0] = 1; count[3][1] = 3 - - - - - - a b - - - - a b c d e f g h -> c - - - - - - - - - - - - - - - - d e - - - f g h - - - - - - - - :param data: the numpy array that stores array values to be written, which serves as a write buffer. When writing a single data value, it can also be a single numeric (e.g. np.int32) python variable. The datatype should match with the variable's datatype. Note this numpy array write buffer can be in any shape as long as the number of elements (buffer size) is matched. @@ -1375,9 +1534,6 @@ cdef class Variable: netCDF variable’s dimensions. :type stride: numpy.ndarray - :param num: [Optional] Only relevant when writing a list of subarrays. An integer specifying the number of subarrays. - :type num: int - :param imap: [Optional] Only relevant when writing a subsampled array or a mapped array. An array of integers the mapping between the dimensions of a netCDF variable and the in-memory structure of the internal data array. The elements of the index mapping vector correspond, in order, to the netCDF variable’s dimensions. Each element value of imap should equal the memory location distance in write buffer between two adjacent elements along the corresponding dimension of netCDF variable. @@ -1392,17 +1548,15 @@ cdef class Variable: :type buftype: mpi4py.MPI.Datatype Operational mode: This method must be called while the file is in independent data mode.""" - if data is not None and all(arg is None for arg in [start, count, stride, num, imap]): + if data is not None and all(arg is None for arg in [start, count, stride, imap]): self._put_var(data, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start]) and all(arg is None for arg in [count, stride, num, imap]): + elif all(arg is not None for arg in [data, start]) and all(arg is None for arg in [count, stride, imap]): self._put_var1(data, start, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count]) and all(arg is None for arg in [stride, num, imap]): + elif all(arg is not None for arg in [data, start, count]) and all(arg is None for arg in [stride, imap]): self._put_vara(start, count, data, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, stride]) and all(arg is None for arg in [num, imap]): + elif all(arg is not None for arg in [data, start, count, stride]) and all(arg is None for arg in [imap]): self._put_vars(start, count, stride, data, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, num]) and all(arg is None for arg in [stride, imap]): - self._put_varn(start, count, num, data, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, stride, imap]) and all(arg is None for arg in [num]): + elif all(arg is not None for arg in [data, start, count, stride, imap]): self._put_varm(data, start, count, stride, imap, collective = False, bufcount = bufcount, buftype = buftype) else: raise ValueError("Invalid input arguments for put_var") @@ -1412,138 +1566,12 @@ cdef class Variable: put_var_all(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None) Method call to write in parallel to the netCDF variable in the - collective I/O mode. The behavior of the method varies depends on the - pattern of provided optional arguments - `start`, `count`, `stride`, - `num` and `imap`. - - - `data` - Write an entire variable - Write a netCDF variable entirely of an opened netCDF file, i.e. - calling this API with only argument `data`. This is the simplest - interface to use for writing a value in a scalar variable or whenever - all the values of a multidimensional variable can all be written at - once. - - .. note:: Be careful when using the simplest forms of this interface - with record variables. When there is no record written into the file - yet, calling this API with only argument `data`, nothing will be - written. Similarly, when writing the entire record variable, one must - take the number of records into account. - - - `data`, `start` - Write a single data value (a single element) - Put a single data value specified by `start` into a variable of an - opened netCDF file. For example, start = [0,5] would specify the - following position in a 4 * 10 two-dimensional - variable ("-" means skip). - - :: - - - - - - - a - - - - - a -> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count` - Write a subarray of values - The part of the netCDF variable to write is specified by giving a corner index and a vector of edge lengths that refer to - an array section of the netCDF variable. For example, start = [0,5] and count = [2,2] would specify the following array - section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a b - - - - a b -> - - - - - c d - - - - c d - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count`, `stride` - Write a subsampled array of values - The part of the netCDF variable to write is specified by giving a corner, a vector of edge lengths and stride vector that - refer to a subsampled array section of the netCDF variable. For example, start = [0,2], count = [2,4] and stride = [1,2] - would specify the following array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - a - b - c - d - - a b c d -> - - e - f - g - h - - e f g h - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count`, `imap`, `stride` (optional) - Write a mapped array of values - The mapped array section is specified by giving a corner, a vector of counts, a stride vector, and an index mapping vector. - The index mapping vector (imap) is a vector of integers that specifies the mapping between the dimensions of a netCDF variable - and the in-memory structure of the internal data array. For example, imap = [3,8], start = [0,5] and count = [2,2] would specify the following - section in write butter and array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a c - - - - a - - b a c - - - - - b d - - - - - - - - -> b d -> - - - - - - - - - - - c - - d - - - - - - - - - - - distance from a to b is 3 in buffer => imap[0] = 3 - distance from a to c is 8 in buffer => imap[1] = 8 - - - `data`, `start`, `count`, `num` - Write a list of subarrays of values - The part of the netCDF variable to write is specified by giving a list of subarrays and each subarray is specified by a corner and a vector of - edge lengths that refer to an array section of the netCDF variable. The example code and diagram below illustrates a lists of 4 specified - subarray sections in a 4 * 10 two-dimensional variable ("-" means skip). - - - :: - - num = 4 - start[0][0] = 0; start[0][1] = 5; count[0][0] = 1; count[0][1] = 2 - start[1][0] = 1; start[1][1] = 0; count[1][0] = 1; count[1][1] = 1 - start[2][0] = 2; start[2][1] = 6; count[2][0] = 1; count[2][1] = 2 - start[3][0] = 3; start[3][1] = 0; count[3][0] = 1; count[3][1] = 3 - - - - - - a b - - - - a b c d e f g h -> c - - - - - - - - - - - - - - - - d e - - - f g h - - - - - - - - - :param data: the numpy array that stores array values to be written, which serves as a write buffer. When writing a single data value, - it can also be a single numeric (e.g. np.int32) python variable. The datatype should match with the variable's datatype. Note this numpy array - write buffer can be in any shape as long as the number of elements (buffer size) is matched. - - :type data: numpy.ndarray - - :param start: [Optional] Only relevant when writing a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the index in the variable where the first of the data values will be written. The - elements of `start` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for writing the data values. When writing to a list of subarrays, `start` - is 2D array of size [num][ndims] and each start[i] is a vector specifying the index in the variable where the first of the data values - will be written. - :type start: numpy.ndarray - - :param count: [Optional] Only relevant when writing a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the edge lengths along each dimension of the block of data values to be written. The - elements of `count` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for writing the data values. When writing to a list of subarrays, `count` - is 2D array of size [num][ndims] and each count[i] is a vector specifying the edge lengths along each dimension of the block of - data values to be written. - - :type count: numpy.ndarray - - :param stride: [Optional] Only relevant when writing a subsampled array or a mapped array. An array of integers specifying - the sampling interval along each dimension of the netCDF variable. The elements of the stride vector correspond, in order, to the - netCDF variable’s dimensions. - :type stride: numpy.ndarray - - :param num: [Optional] Only relevant when writing a list of subarrays. An integer specifying the number of subarrays. - :type num: int - - :param imap: [Optional] Only relevant when writing a subsampled array or a mapped array. An array of integers the mapping between - the dimensions of a netCDF variable and the in-memory structure of the internal data array. The elements of the index mapping vector - correspond, in order, to the netCDF variable’s dimensions. Each element value of imap should equal the memory location distance in write buffer between two adjacent elements along the corresponding dimension of netCDF variable. - :type imap: numpy.ndarray - - :param bufcount: [Optional] Optional for all types of writing patterns. An integer indicates the number of MPI derived data type elements - in the write buffer to be written to the file. - :type bufcount: int - - :param buftype: [Optional] Optional for all types of writing patterns. An MPI derived data type that describes the memory layout of the - write buffer. - :type buftype: mpi4py.MPI.Datatype + collective I/O mode. For the argument usage, please refer to method + :meth:`Variable.put_var`. The only difference is this method is a + collective operation. - Operational mode: This method must be called while the file is in collective data mode. + Operational mode: This method must be called while the file is in + collective data mode. """ if data is not None and all(arg is None for arg in [start, count, stride, num, imap]): self._put_var(data, collective = True, bufcount = bufcount, buftype = buftype) @@ -1747,28 +1775,28 @@ cdef class Variable: _check_err(ierr) - def _get_varn(self, ndarray buff, start, count, num, bufcount, Datatype buftype, collective = True): + def _get_varn(self, ndarray data, num, starts, counts, bufcount, Datatype buftype, collective = True): cdef int ierr, ndims cdef MPI_Offset buffcount cdef MPI_Datatype bufftype - cdef size_t **startp - cdef size_t **countp - + cdef size_t **startsp + cdef size_t **countsp cdef int num_req + num_req = num ndims = len(self.dimensions) - max_num_req = len(start) - startp = malloc(max_num_req * sizeof(size_t*)); + max_num_req = len(starts) + startsp = malloc(max_num_req * sizeof(size_t*)); for i in range(max_num_req): - startp[i] = malloc(ndims * sizeof(size_t)); + startsp[i] = malloc(ndims * sizeof(size_t)); for j in range(ndims): - startp[i][j] = start[i][j] + startsp[i][j] = starts[i][j] - countp = malloc(max_num_req * sizeof(size_t*)); + countsp = malloc(max_num_req * sizeof(size_t*)); for i in range(max_num_req): - countp[i] = malloc(ndims * sizeof(size_t)); + countsp[i] = malloc(ndims * sizeof(size_t)); for j in range(ndims): - countp[i][j] = count[i][j] + countsp[i][j] = counts[i][j] if bufcount is None: buffcount = 1 @@ -1779,17 +1807,27 @@ cdef class Variable: bufftype = MPI_DATATYPE_NULL else: bufftype = buftype.ob_mpi + if collective: with nogil: - ierr = ncmpi_get_varn_all(self._file_id, self._varid, num_req,\ - startp, countp, \ - PyArray_DATA(buff), buffcount, bufftype) - + ierr = ncmpi_get_varn_all(self._file_id, + self._varid, + num_req, + startsp, + countsp, + PyArray_DATA(data), + buffcount, + bufftype) else: with nogil: - ierr = ncmpi_get_varn(self._file_id, self._varid, num_req,\ - startp, countp, \ - PyArray_DATA(buff), buffcount, bufftype) + ierr = ncmpi_get_varn(self._file_id, + self._varid, + num_req, + startsp, + countsp, + PyArray_DATA(data), + buffcount, + bufftype) _check_err(ierr) @@ -1871,15 +1909,15 @@ cdef class Variable: imapp, PyArray_DATA(buff), buffcount, bufftype) _check_err(ierr) - def get_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None): + def get_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None): """ - get_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None) + get_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None) Method call to read in parallel from the netCDF variable in the independent I/O mode. The behavior of the method varies depends on the pattern of provided optional arguments - `start`, `count`, `stride`, - `num` and `imap`. The method requires a empty array (`data`) as a read - buffer from caller to store returned array values. + and `imap`. The method requires a empty array (`data`) as a read buffer + from caller to store returned array values. - `data` - Read an entire variable Read all the values from a netCDF variable of an opened netCDF file. @@ -1945,61 +1983,57 @@ cdef class Variable: distance from a to b is 3 in buffer => imap[0] = 3 distance from a to c is 8 in buffer => imap[1] = 8 - - `data`, `start`, `count`, `num` - Read a list of subarrays of values - The part of the netCDF variable to read is specified by giving a list of subarrays and each subarray is specified by a corner and a vector of - edge lengths that refer to an array section of the netCDF variable. The example code and diagram below illustrates a lists of 4 specified - subarray sections in a 4 * 10 two-dimensional variable ("-" means skip). - - - :: - - num = 4 - start[0][0] = 0; start[0][1] = 5; count[0][0] = 1; count[0][1] = 2 - start[1][0] = 1; start[1][1] = 0; count[1][0] = 1; count[1][1] = 1 - start[2][0] = 2; start[2][1] = 6; count[2][0] = 1; count[2][1] = 2 - start[3][0] = 3; start[3][1] = 0; count[3][0] = 1; count[3][1] = 3 - - - - - - a b - - - - a b c d e f g h <= c - - - - - - - - - - - - - - - - d e - - - f g h - - - - - - - - - :param data: the numpy array that stores array values to be written, which serves as a read buffer. The datatype should match with the - variable's datatype. Note this numpy array read buffer can be in any shape as long as the number of elements (buffer size) is matched. + :param data: + the numpy array that stores array values to be read from the file, + which serves as a read buffer. The datatype should match with the + variable's datatype. Note this numpy array read buffer can be in + any shape as long as the number of elements (buffer size) is + matched. :type data: numpy.ndarray - :param start: [Optional] Only relevant when reading a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the index in the variable where the first of the data values will be written. The - elements of `start` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for reading the data values. When reading to a list of subarrays, `start` - is 2D array of size [num][ndims] and each start[i] is a vector specifying the index in the variable where the first of the data values - will be written. + :param start: [Optional] + Only relevant when reading a array of values, a subsampled array, a + mapped array or a list of subarrays. An array of integers + specifying the index in the variable where the first of the data + values will be written. The elements of `start` must correspond to + the variable’s dimensions in order. Hence, if the variable is a + record variable, the first index would correspond to the starting + record number for reading the data values. :type start: numpy.ndarray - :param count: [Optional] Only relevant when reading a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the edge lengths along each dimension of the block of data values to be written. The - elements of `count` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for reading the data values. When reading to a list of subarrays, `count` - is 2D array of size [num][ndims] and each count[i] is a vector specifying the edge lengths along each dimension of the block of - data values to be written. - + :param count: [Optional] + Only relevant when reading a array of values, a subsampled array, a + mapped array or a list of subarrays. An array of integers + specifying the edge lengths along each dimension of the block of + data values to be read. The elements of `count` must correspond + to the variable’s dimensions in order. Hence, if the variable is a + record variable, the first index would correspond to the starting + record number for reading the data values. :type count: numpy.ndarray - :param stride: [Optional] Only relevant when reading a subsampled array or a mapped array. An array of integers specifying - the sampling interval along each dimension of the netCDF variable. The elements of the stride vector correspond, in order, to the - netCDF variable’s dimensions. + :param stride: [Optional] + Only relevant when reading a subsampled array or a mapped array. An + array of integers specifying the sampling interval along each + dimension of the netCDF variable. The elements of the stride vector + correspond, in order, to the netCDF variable’s dimensions. :type stride: numpy.ndarray - :param num: [Optional] Only relevant when reading a list of subarrays. An integer specifying the number of subarrays. - :type num: int - - :param imap: [Optional] Only relevant when reading a subsampled array or a mapped array. An array of integers the mapping between - the dimensions of a netCDF variable and the in-memory structure of the internal data array. The elements of the index mapping vector - correspond, in order, to the netCDF variable’s dimensions. Each element value of imap should equal the memory location distance in read buffer between two adjacent elements along the corresponding dimension of netCDF variable. + :param imap: [Optional] + Only relevant when reading a subsampled array or a mapped array. An + array of integers the mapping between the dimensions of a netCDF + variable and the in-memory structure of the internal data array. + The elements of the index mapping vector correspond, in order, to + the netCDF variable’s dimensions. Each element value of imap should + equal the memory location distance in read buffer between two + adjacent elements along the corresponding dimension of netCDF + variable. :type imap: numpy.ndarray - :param bufcount: [Optional] Optional for all types of reading patterns. An integer indicates the number of MPI derived data type elements - in the read buffer to be written to the file. + :param bufcount: [Optional] + Optional for all types of reading patterns. An integer indicates + the number of MPI derived data type elements in the read buffer to + store data read from the file. :type bufcount: int :param buftype: [Optional] Optional for all types of reading patterns. An MPI derived data type that describes the memory layout of the @@ -2014,172 +2048,144 @@ cdef class Variable: # 1. Among all behaviors of get_var get_varm always requires a buffer argument # 2. Other i/o methods (iget/put/iput) all require buffer array as mandatory argument - if all(arg is None for arg in [start, count, stride, num, imap]): + if all(arg is None for arg in [start, count, stride, imap]): self._get_var(data, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start]) and all(arg is None for arg in [count, stride, num, imap]): + elif all(arg is not None for arg in [start]) and all(arg is None for arg in [count, stride, imap]): self._get_var1(data, start, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start, count]) and all(arg is None for arg in [stride, num, imap]): + elif all(arg is not None for arg in [start, count]) and all(arg is None for arg in [stride, imap]): self._get_vara(data, start, count, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start, count, stride]) and all(arg is None for arg in [num, imap]): + elif all(arg is not None for arg in [start, count, stride]) and all(arg is None for arg in [imap]): self._get_vars(data, start, count, stride, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start, count, num]) and all(arg is None for arg in [stride, imap]): - self._get_varn(data, start, count, num, collective = False, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start, count, imap]) and all(arg is None for arg in [num]): + elif all(arg is not None for arg in [start, count, imap]): self._get_varm(data, start, count, stride, imap, collective = False, bufcount = bufcount, buftype = buftype) else: raise ValueError("Invalid input arguments for get_var") - def get_var_all(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None): + def get_var_all(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None): """ - get_var_all(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None) + get_var_all(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None) Method call to read in parallel from the netCDF variable in the - collective I/O mode. The behavior of the method varies depends on the - pattern of provided optional arguments - `index`, `start`, `count`, - `stride`, `num` and `imap`. The method requires a empty array (`data`) - as a read buffer from caller to store returned array values. - - - `data` - Read an entire variable - Read all the values from a netCDF variable of an opened netCDF file. - This is the simplest interface to use for reading the value of a - scalar variable or when all the values of a multidimensional variable - can be read at once. + collective I/O mode. For the argument usage, please refer to method + :meth:`Variable.get_var`. The only difference is this method is a + collective operation. - .. note:: Be careful when using this simplest form to read a record - variable when you don’t specify how many records are to be read. If - you try to read all the values of a record variable into an array but - there are more records in the file than you assume, more data will be - read than you expect, which may cause a segmentation violation. + Operational mode: This method must be called while the file is in + collective data mode. + """ + if all(arg is None for arg in [start, count, stride, imap]): + self._get_var(data, collective = True, bufcount = bufcount, buftype = buftype) + elif all(arg is not None for arg in [start]) and all(arg is None for arg in [count, stride, imap]): + self._get_var1(data, start, collective = True, bufcount = bufcount, buftype = buftype) + elif all(arg is not None for arg in [start, count]) and all(arg is None for arg in [stride, imap]): + self._get_vara(data, start, count, collective = True, bufcount = bufcount, buftype = buftype) + elif all(arg is not None for arg in [start, count, stride]) and all(arg is None for arg in [imap]): + self._get_vars(data, start, count, stride, collective = True, bufcount = bufcount, buftype = buftype) + elif all(arg is not None for arg in [start, count, imap]): + self._get_varm(data, start, count, stride, imap, collective = True, bufcount = bufcount, buftype = buftype) + else: + raise ValueError("Invalid input arguments for get_var_all") - - `data`, `start` - Read a single data value (a single element) - Put a single data value specified by `start` from a variable of an - opened netCDF file that is in data mode. For example, start = [0,5] - would specify the following position in a 4 * 10 two-dimensional - variable ("-" means skip). + def get_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None): + """ + get_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None) + + Method call to read in parallel from a netCDF variables. This an + independent I/O call. The behavior of the method varies depends on the + pattern of provided optional arguments - `starts`, and `counts`. This + method is equivalent to making multiple calls to + :meth:`Variable.get_vara`. Combining multiple + :meth:`Variable.get_vara` calls may achieve a better performance. + + - `data`, `num`,`starts`, `counts` - Write multiple subarrays of + values The part of the netCDF variable to read is specified by + giving multiple subarrays and each subarray is specified by a + corner and a vector of edge lengths that refer to an array section + of the netCDF variable. The example code and diagram below + illustrates a 4 subarray section in a 4 * 10 two-dimensional + variable ("-" means skip). :: - - - - - - a - - - - - - - - - - - - - - - -> a - - - - - - - - - - - - - - - - - - - - - - + num = 4 + starts[0][0] = 0; starts[0][1] = 5; counts[0][0] = 1; counts[0][1] = 2 + starts[1][0] = 1; starts[1][1] = 0; counts[1][0] = 1; counts[1][1] = 1 + starts[2][0] = 2; starts[2][1] = 6; counts[2][0] = 1; counts[2][1] = 2 + starts[3][0] = 3; starts[3][1] = 0; counts[3][0] = 1; counts[3][1] = 3 + - - - - - a b - - - + a b c d e f g h <- c - - - - - - - - - + - - - - - - d e - - + f g h - - - - - - - - - `data`, `start`, `count` - Read an subarray of values - The part of the netCDF variable to read is specified by giving a corner index and a vector of edge lengths that refer to - an array section of the netCDF variable. For example, start = [0,5] and count = [2,2] would specify the following array - section in a 4 * 10 two-dimensional variable ("-" means skip). + :param data: the numpy array that stores array values to be read, + which serves as a read buffer. When reading a single data value, + it can also be a single numeric (e.g. np.int32) python variable. + The datatype should match with the variable's datatype. Note this + numpy array read buffer can be in any shape as long as the number + of elements (buffer size) is matched. If the in-memory type of data + values differs from the netCDF variable type defined in the file, + type conversion will automatically be applied. + :type data: numpy.ndarray - :: + :param num: An integer specifying the number of subarrays. + :type num: int - - - - - - a b - - - - - - - - - c d - - - a b - - - - - - - - - - - -> c d - - - - - - - - - - - + :param starts: + A 2D array of integers containing starting array indices + of `num` number of subarrays. The first dimension of `starts` + should be of size `num`, indicating the number of subarrays of the + variable to be read. The second dimension is of size equal to + the number dimensions of the variable. For example, when `num` = 3 + and the variable defined in the file is a 2D array, `starts` should + be a 3x2 array. Each of the subarray starting indices identify + the indices in the variable where the first of the data values will + be read. Each `starts[i]` is a vector specifying the index in + the variable where the first of the data values will be read. + The elements of `starts[i][*]` must correspond to the variable’s + dimensions in order. Hence, if the variable is a record variable, + the first index, `starts[i][0]` would correspond to the starting + record number for reading the data values. + :type starts: numpy.ndarray + + :param counts: [Optional] + A 2D array of integers specifying the lengths along each + dimension of `num` number of subarrays to be read. The first + dimension of `counts` should be of size `num`, indicating the + number of subarrays of the variable to be read. The second + dimension is of size equal to the number dimensions of the + variable. For example, when `num` = 3 and the variable defined in + the file is a 2D array, `counts` should be a 3x2 array. Each of + the subarray `counts[i]` is a vector specifying the lengths along + each dimension of the block of data values to be read and must + correspond to the variable’s dimensions in order. When this + argument is not supplied, it is equivalent to providing counts of + all 1s. + :type counts: numpy.ndarray + + :param bufcount: [Optional] + An integer indicates the number of MPI derived data type elements + in the read buffer to store data read from the file. + :type bufcount: int - - `data`, `start`, `count`, `stride` - Read a subsampled array of values - The part of the netCDF variable to read is specified by giving a corner, a vector of edge lengths and stride vector that - refer to a subsampled array section of the netCDF variable. For example, start = [0,2], count = [2,4] and stride = [1,2] - would specify the following array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - a - b - c - d - - - - e - f - g - h - a b c d - - - - - - - - - - - -> e f g h - - - - - - - - - - - - - - `data`, `start`, `count`, `imap`, `stride` (optional) - Read a mapped array of values - The mapped array section is specified by giving a corner, a vector of counts, a stride vector, and an index mapping vector. - The index mapping vector (imap) is a vector of integers that specifies the mapping between the dimensions of a netCDF variable - and the in-memory structure of the internal data array. For example, imap = [3,8], start = [0,5] and count = [2,2] would specify the following - section in read butter and array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a c - - - - a - - b a c - - - - - b d - - - - - - - - <= b d <= - - - - - - - - - - - c - - d - - - - - - - - - - - distance from a to b is 3 in buffer => imap[0] = 3 - distance from a to c is 8 in buffer => imap[1] = 8 - - - `data`, `start`, `count`, `num` - Read a list of subarrays of values - The part of the netCDF variable to read is specified by giving a list of subarrays and each subarray is specified by a corner and a vector of - edge lengths that refer to an array section of the netCDF variable. The example code and diagram below illustrates a lists of 4 specified - subarray sections in a 4 * 10 two-dimensional variable ("-" means skip). - - - :: - - num = 4 - start[0][0] = 0; start[0][1] = 5; count[0][0] = 1; count[0][1] = 2 - start[1][0] = 1; start[1][1] = 0; count[1][0] = 1; count[1][1] = 1 - start[2][0] = 2; start[2][1] = 6; count[2][0] = 1; count[2][1] = 2 - start[3][0] = 3; start[3][1] = 0; count[3][0] = 1; count[3][1] = 3 - - - - - - a b - - - - a b c d e f g h <= c - - - - - - - - - - - - - - - - d e - - - f g h - - - - - - - - - :param data: the numpy array that stores array values to be written, which serves as a read buffer. The datatype should match with the - variable's datatype. Note this numpy array read buffer can be in any shape as long as the number of elements (buffer size) is matched. - - :type data: numpy.ndarray - - :param start: [Optional] Only relevant when reading a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the index in the variable where the first of the data values will be written. The - elements of `start` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for reading the data values. When reading to a list of subarrays, `start` - is 2D array of size [num][ndims] and each start[i] is a vector specifying the index in the variable where the first of the data values - will be written. - :type start: numpy.ndarray - - :param count: [Optional] Only relevant when reading a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the edge lengths along each dimension of the block of data values to be written. The - elements of `count` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for reading the data values. When reading to a list of subarrays, `count` - is 2D array of size [num][ndims] and each count[i] is a vector specifying the edge lengths along each dimension of the block of - data values to be written. - - :type count: numpy.ndarray - - :param stride: [Optional] Only relevant when reading a subsampled array or a mapped array. An array of integers specifying - the sampling interval along each dimension of the netCDF variable. The elements of the stride vector correspond, in order, to the - netCDF variable’s dimensions. - :type stride: numpy.ndarray - - :param num: [Optional] Only relevant when reading a list of subarrays. An integer specifying the number of subarrays. - :type num: int - - :param imap: [Optional] Only relevant when reading a subsampled array or a mapped array. An array of integers the mapping between - the dimensions of a netCDF variable and the in-memory structure of the internal data array. The elements of the index mapping vector - correspond, in order, to the netCDF variable’s dimensions. Each element value of imap should equal the memory location distance in read buffer between two adjacent elements along the corresponding dimension of netCDF variable. - :type imap: numpy.ndarray - - :param bufcount: [Optional] Optional for all types of reading patterns. An integer indicates the number of MPI derived data type elements - in the read buffer to be written to the file. - :type bufcount: int - - :param buftype: [Optional] Optional for all types of reading patterns. An MPI derived data type that describes the memory layout of the - read buffer. + :param buftype: [Optional] + An MPI derived data type that describes the memory layout of the + write buffer. :type buftype: mpi4py.MPI.Datatype + """ + return self._get_varn(data, num, starts, counts, bufcount = bufcount, + buftype = buftype, collective = False) - Operational mode: This method must be called while the file is in collective data mode. + def get_varn_all(self, data, num, starts, counts=None, bufcount=None, buftype=None): """ - if all(arg is None for arg in [start, count, stride, num, imap]): - self._get_var(data, collective = True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start]) and all(arg is None for arg in [count, stride, num, imap]): - self._get_var1(data, start, collective = True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start, count]) and all(arg is None for arg in [stride, num, imap]): - self._get_vara(data, start, count, collective = True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start, count, stride]) and all(arg is None for arg in [num, imap]): - self._get_vars(data, start, count, stride, collective = True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start, count, num]) and all(arg is None for arg in [stride, imap]): - self._get_varn(data, start, count, num, collective = True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [start, count, imap]) and all(arg is None for arg in [num]): - self._get_varm(data, start, count, stride, imap, collective = True, bufcount = bufcount, buftype = buftype) - else: - raise ValueError("Invalid input arguments for get_var_all") + get_varn_all(self, data, num, starts, counts=None, bufcount=None, buftype=None) + + This method call is the same as method :meth:`Variable.get_varn`, + except it is collective and can only be called while the file in the + collective I/O mode. Please refer to :meth:`Variable.get_varn` for + its argument usage. + """ + return self._get_varn(data, num, starts, counts, bufcount = bufcount, + buftype = buftype, collective = True) def _get(self,start,count,stride): """Private method to retrieve data from a netCDF variable""" @@ -2425,28 +2431,28 @@ cdef class Variable: _check_err(ierr) return request - def _iput_varn(self, start, count, num, ndarray data, bufcount, Datatype buftype, buffered=False): + def _iput_varn(self, ndarray data, num, starts, counts, bufcount, Datatype buftype, buffered=False): cdef int ierr, ndims cdef MPI_Offset buffcount cdef MPI_Datatype bufftype - cdef size_t **startp - cdef size_t **countp + cdef size_t **startsp + cdef size_t **countsp cdef int num_req cdef int request num_req = num ndims = len(self.dimensions) - max_num_req = len(start) - startp = malloc(max_num_req * sizeof(size_t*)); + max_num_req = len(starts) + startsp = malloc(max_num_req * sizeof(size_t*)); for i in range(max_num_req): - startp[i] = malloc(ndims * sizeof(size_t)); + startsp[i] = malloc(ndims * sizeof(size_t)); for j in range(ndims): - startp[i][j] = start[i, j] + startsp[i][j] = starts[i, j] - countp = malloc(max_num_req * sizeof(size_t*)); + countsp = malloc(max_num_req * sizeof(size_t*)); for i in range(max_num_req): - countp[i] = malloc(ndims * sizeof(size_t)); + countsp[i] = malloc(ndims * sizeof(size_t)); for j in range(ndims): - countp[i][j] = count[i, j] + countsp[i][j] = counts[i, j] if not PyArray_ISCONTIGUOUS(data): data = data.copy() @@ -2465,12 +2471,26 @@ cdef class Variable: bufftype = buftype.ob_mpi if not buffered: with nogil: - ierr = ncmpi_iput_varn(self._file_id, self._varid, num_req, startp, countp,\ - PyArray_DATA(data), buffcount, bufftype, &request) + ierr = ncmpi_iput_varn(self._file_id, + self._varid, + num_req, + startsp, + countsp, + PyArray_DATA(data), + buffcount, + bufftype, + &request) else: with nogil: - ierr = ncmpi_bput_varn(self._file_id, self._varid, num_req, startp, countp,\ - PyArray_DATA(data), buffcount, bufftype, &request) + ierr = ncmpi_bput_varn(self._file_id, + self._varid, + num_req, + startsp, + countsp, + PyArray_DATA(data), + buffcount, + bufftype, + &request) _check_err(ierr) return request @@ -2524,336 +2544,88 @@ cdef class Variable: _check_err(ierr) return request - def bput_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None): + def bput_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None): """ - bput_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None) + bput_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None) - Method call to to post a buffered write request to write in parallel to - the netCDF variable. The behavior of the method varies depends on the - pattern of provided optional arguments - `start`, `count`, `stride`, - `num` and `imap`. This method returns a request ID that can be selected - in ``File.wait`` or ``File.wait_all``. The posted write request will - not be executed until ``File.wait`` or ``File.wait_all`` is called. + Method call to post a nonblocking, buffered write request to write to + the netCDF variable. The syntax is the same as + :meth:`Variable.put_var`. For the argument usage, please refer to + :meth:`Variable.put_var`. This method returns a request ID that can + be used in :meth:`File.wait` or :meth:`File.wait_all`. The posted + write request may not be committed until :meth:`File.wait` or + :meth:`File.wait_all` is called. .. note:: Note that this method requires a numpy array (`data`) as a - write buffer from caller prepared for writing returned array values - when ``File.wait`` or ``File.wait_all`` is called. Unlike - ``Variable.iput``, the write data is buffered (cached) internally by - PnetCDF and will be flushed to the file at the time of calling - ``File.wait`` or ``File.wait_all``. Once the call to this method - returns, the caller is free to change the contents of write buffer. - Prior to calling this method, make sure ``File.attach_buff`` is called - to allocate an internal buffer for accommodating the write requests. - - - `data` - Request to write an entire variable - Request to write all the values of a variable into a netCDF variable - of an opened netCDF file. This is the simplest interface to use for - writing a value in a scalar variable or whenever all the values of a - multidimensional variable can all be written at once. - - .. note:: Be careful when using this simplest forms to write a record - variable. If you try to write all the values of a record variable into - a netCDF file that has no record data yet (hence has 0 records), - nothing will be written. Similarly, if you try to write all of a - record variable but there are more records in the file than you - assume, more data may be written to the file than you supply, which - may result in a segmentation violation. - - - `data`, `start` - Request to write a single data value (a single - element) Put a single data value specified by `start` into a variable - of an opened netCDF file that is in data mode. For example, start = - [0,5] would specify the following position in a 4 * 10 two-dimensional - variable ("-" means skip). - - :: - - - - - - - a - - - - - a -> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count` - Request to write a subarray of values - The part of the netCDF variable to write is specified by giving a corner index and a vector of edge lengths that refer to - an array section of the netCDF variable. For example, start = [0,5] and count = [2,2] would specify the following array - section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a b - - - - a b -> - - - - - c d - - - - c d - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count`, `stride` - Request to write a subsampled array of values - The part of the netCDF variable to write is specified by giving a corner, a vector of edge lengths and stride vector that - refer to a subsampled array section of the netCDF variable. For example, start = [0,2], count = [2,4] and stride = [1,2] - would specify the following array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - a - b - c - d - - a b c d -> - - e - f - g - h - - e f g h - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count`, `imap`, `stride` (optional) - Request to write a mapped array of values - The mapped array section is specified by giving a corner, a vector of counts, a stride vector, and an index mapping vector. - The index mapping vector (imap) is a vector of integers that specifies the mapping between the dimensions of a netCDF variable - and the in-memory structure of the internal data array. For example, imap = [3,8], start = [0,5] and count = [2,2] would specify the following - section in write butter and array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a c - - - - a - - b a c - - - - - b d - - - - - - - - -> b d -> - - - - - - - - - - - c - - d - - - - - - - - - - - distance from a to b is 3 in buffer => imap[0] = 3 - distance from a to c is 8 in buffer => imap[1] = 8 - - - `data`, `start`, `count`, `num` - Request to write a list of subarrays of values - The part of the netCDF variable to write is specified by giving a list of subarrays and each subarray is specified by a corner and a vector of - edge lengths that refer to an array section of the netCDF variable. The example code and diagram below illustrates a lists of 4 specified - subarray sections in a 4 * 10 two-dimensional variable ("-" means skip). - - - :: - - num = 4 - start[0][0] = 0; start[0][1] = 5; count[0][0] = 1; count[0][1] = 2 - start[1][0] = 1; start[1][1] = 0; count[1][0] = 1; count[1][1] = 1 - start[2][0] = 2; start[2][1] = 6; count[2][0] = 1; count[2][1] = 2 - start[3][0] = 3; start[3][1] = 0; count[3][0] = 1; count[3][1] = 3 - - - - - - a b - - - - a b c d e f g h -> c - - - - - - - - - - - - - - - - d e - - - f g h - - - - - - - - - :param data: the numpy array that stores array values to be written, which serves as a write buffer. When writing a single data value, - it can also be a single numeric (e.g. np.int32) python variable. The datatype should match with the variable's datatype. Note this numpy array - write buffer can be in any shape as long as the number of elements (buffer size) is matched. - - :type data: numpy.ndarray - - :param start: [Optional] Only relevant when writing a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the index in the variable where the first of the data values will be written. The - elements of `start` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for writing the data values. When writing to a list of subarrays, `start` - is 2D array of size [num][ndims] and each start[i] is a vector specifying the index in the variable where the first of the data values - will be written. - :type start: numpy.ndarray - - :param count: [Optional] Only relevant when writing a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the edge lengths along each dimension of the block of data values to be written. The - elements of `count` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for writing the data values. When writing to a list of subarrays, `count` - is 2D array of size [num][ndims] and each count[i] is a vector specifying the edge lengths along each dimension of the block of - data values to be written. - - :type count: numpy.ndarray - - :param stride: [Optional] Only relevant when writing a subsampled array or a mapped array. An array of integers specifying - the sampling interval along each dimension of the netCDF variable. The elements of the stride vector correspond, in order, to the - netCDF variable’s dimensions. - :type stride: numpy.ndarray - - :param num: [Optional] Only relevant when writing a list of subarrays. An integer specifying the number of subarrays. - :type num: int - - :param imap: [Optional] Only relevant when writing a subsampled array or a mapped array. An array of integers the mapping between - the dimensions of a netCDF variable and the in-memory structure of the internal data array. The elements of the index mapping vector - correspond, in order, to the netCDF variable’s dimensions. Each element value of imap should equal the memory location distance in write buffer between two adjacent elements along the corresponding dimension of netCDF variable. - :type imap: numpy.ndarray - - :param bufcount: [Optional] Optional for all types of writing patterns. An integer indicates the number of MPI derived data type elements - in the write buffer to be written to the file. - :type bufcount: int - - :param buftype: [Optional] Optional for all types of writing patterns. An MPI derived data type that describes the memory layout of the - write buffer. - :type buftype: mpi4py.MPI.Datatype - - :return: The reqeust ID, which can be used in a successive call to ``File.wait`` or ``File.wait_all`` for the completion of the nonblocking operation. + write buffer from caller prepared for writing returned array values + when :meth:`File.wait` or :meth:`File.wait_all` is called. + Unlike :meth:`Variable.iput_var`, the write data is buffered + (cached) internally by PnetCDF and will be flushed to the file at + the time of calling :meth:`File.wait` or :meth:`File.wait_all`. + Once the call to this method returns, the caller is free to change + the contents of write buffer. Prior to calling this method, make + sure :meth:`File.attach_buff` is called to allocate an internal + buffer for accommodating the write requests. + + :return: The request ID, which can be used in a successive call to + :meth:`File.wait` or :meth:`File.wait_all` for the completion + of the nonblocking operation. :rtype: int - Operational mode: This method must be called while the file is in independent data mode.""" + Operational mode: This method can be called while the file is in either + collective or independent data mode. + """ - if data is not None and all(arg is None for arg in [start, count, stride, num, imap]): + if data is not None and all(arg is None for arg in [start, count, stride, imap]): return self._iput_var(data, buffered=True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start]) and all(arg is None for arg in [count, stride, num, imap]): + elif all(arg is not None for arg in [data, start]) and all(arg is None for arg in [count, stride, imap]): return self._iput_var1(data, start, buffered=True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count]) and all(arg is None for arg in [stride, num, imap]): + elif all(arg is not None for arg in [data, start, count]) and all(arg is None for arg in [stride, imap]): return self._iput_vara(start, count, data, buffered=True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, stride]) and all(arg is None for arg in [num, imap]): + elif all(arg is not None for arg in [data, start, count, stride]) and all(arg is None for arg in [imap]): return self._iput_vars(start, count, stride, data, buffered=True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, num]) and all(arg is None for arg in [stride, imap]): - return self._iput_varn(start, count, num, data, buffered=True, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, imap]) and all(arg is None for arg in [num]): + elif all(arg is not None for arg in [data, start, count, imap]): return self._iput_varm(data, start, count, stride, imap, buffered=True, bufcount = bufcount, buftype = buftype) else: raise ValueError("Invalid input arguments for bput_var") - def iput_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None): + def iput_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None): """ - iput_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None) + iput_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None) - Method call to to post a request to write in parallel to the netCDF - variable. The behavior of the method varies depends on the pattern of - provided optional arguments - `start`, `count`, `stride`, `num` and - `imap`. This method returns a request ID that can be selected in - ``File.wait`` or ``File.wait_all``. The posted write request will not - be executed until ``File.wait`` or ``File.wait_all`` is called. + Method call to post a nonblocking request to write to the netCDF + variable. The syntax is the same as :meth:`Variable.put_var`. For the + argument usage, please refer to :meth:`Variable.put_var`. This method + returns a request ID that can This method returns a request ID that can + be used in :meth:`File.wait` or :meth:`File.wait_all`. The posted + write request may not be committed until :meth:`File.wait` or + :meth:`File.wait_all` is called. .. note:: Note that this method requires a numpy array (`data`) as a - write buffer from caller prepared for writing returned array values - when ``File.wait`` or ``File.wait_all`` is called. Users should not - alter the contents of the write buffer once the request is posted - until the ``File.wait`` or ``File.wait_all`` is returned. Any change - to the buffer contents in between will result in unexpected error. - - - `data` - Request to write an entire variable - Request to write all the values of a variable into a netCDF variable - of an opened netCDF file. This is the simplest interface to use for - writing a value in a scalar variable or whenever all the values of a - multidimensional variable can all be written at once. - - .. note:: Be careful when using this simplest forms to read a record - variable. If you try to write all the values of a record variable into - a netCDF file that has no record data yet (hence has 0 records), - nothing will be written. Similarly, if you try to write all of a - record variable but there are more records in the file than you - assume, more data may be written to the file than you supply, which - may result in a segmentation violation. - - - `data`, `start` - Request to write a single data value (a single element) - Put a single data value specified by `start` into a variable of an - opened netCDF file that is in data mode. For example, start = [0,5] - would specify the following position in a 4 * 10 two-dimensional - variable ("-" means skip). - - :: - - - - - - - a - - - - - a -> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count` - Request to write a subarray of values - The part of the netCDF variable to write is specified by giving a corner index and a vector of edge lengths that refer to - an array section of the netCDF variable. For example, start = [0,5] and count = [2,2] would specify the following array - section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a b - - - - a b -> - - - - - c d - - - - c d - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count`, `stride` - Request to write a subsampled array of values - The part of the netCDF variable to write is specified by giving a corner, a vector of edge lengths and stride vector that - refer to a subsampled array section of the netCDF variable. For example, start = [0,2], count = [2,4] and stride = [1,2] - would specify the following array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - a - b - c - d - - a b c d -> - - e - f - g - h - - e f g h - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count`, `imap`, `stride` (optional) - Request to write a mapped array of values - The mapped array section is specified by giving a corner, a vector of counts, a stride vector, and an index mapping vector. - The index mapping vector (imap) is a vector of integers that specifies the mapping between the dimensions of a netCDF variable - and the in-memory structure of the internal data array. For example, imap = [3,8], start = [0,5] and count = [2,2] would specify the following - section in write butter and array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a c - - - - a - - b a c - - - - - b d - - - - - - - - -> b d -> - - - - - - - - - - - c - - d - - - - - - - - - - - distance from a to b is 3 in buffer => imap[0] = 3 - distance from a to c is 8 in buffer => imap[1] = 8 - - - `data`, `start`, `count`, `num` - Request to write a list of subarrays of values - The part of the netCDF variable to write is specified by giving a list of subarrays and each subarray is specified by a corner and a vector of - edge lengths that refer to an array section of the netCDF variable. The example code and diagram below illustrates a lists of 4 specified - subarray sections in a 4 * 10 two-dimensional variable ("-" means skip). - - - :: - - num = 4 - start[0][0] = 0; start[0][1] = 5; count[0][0] = 1; count[0][1] = 2 - start[1][0] = 1; start[1][1] = 0; count[1][0] = 1; count[1][1] = 1 - start[2][0] = 2; start[2][1] = 6; count[2][0] = 1; count[2][1] = 2 - start[3][0] = 3; start[3][1] = 0; count[3][0] = 1; count[3][1] = 3 - - - - - - a b - - - - a b c d e f g h -> c - - - - - - - - - - - - - - - - d e - - - f g h - - - - - - - - - :param data: the numpy array that stores array values to be written, which serves as a write buffer. When writing a single data value, - it can also be a single numeric (e.g. np.int32) python variable. The datatype should match with the variable's datatype. Note this numpy array - write buffer can be in any shape as long as the number of elements (buffer size) is matched. - - :type data: numpy.ndarray - - :param start: [Optional] Only relevant when writing a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the index in the variable where the first of the data values will be written. The - elements of `start` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for writing the data values. When writing to a list of subarrays, `start` - is 2D array of size [num][ndims] and each start[i] is a vector specifying the index in the variable where the first of the data values - will be written. - :type start: numpy.ndarray - - :param count: [Optional] Only relevant when writing a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the edge lengths along each dimension of the block of data values to be written. The - elements of `count` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for writing the data values. When writing to a list of subarrays, `count` - is 2D array of size [num][ndims] and each count[i] is a vector specifying the edge lengths along each dimension of the block of - data values to be written. - - :type count: numpy.ndarray - - :param stride: [Optional] Only relevant when writing a subsampled array or a mapped array. An array of integers specifying - the sampling interval along each dimension of the netCDF variable. The elements of the stride vector correspond, in order, to the - netCDF variable’s dimensions. - :type stride: numpy.ndarray - - :param num: [Optional] Only relevant when writing a list of subarrays. An integer specifying the number of subarrays. - :type num: int - - :param imap: [Optional] Only relevant when writing a subsampled array or a mapped array. An array of integers the mapping between - the dimensions of a netCDF variable and the in-memory structure of the internal data array. The elements of the index mapping vector - correspond, in order, to the netCDF variable’s dimensions. Each element value of imap should equal the memory location distance in write buffer between two adjacent elements along the corresponding dimension of netCDF variable. - :type imap: numpy.ndarray - - :param bufcount: [Optional] Optional for all types of writing patterns. An integer indicates the number of MPI derived data type elements - in the write buffer to be written to the file. - :type bufcount: int - - :param buftype: [Optional] Optional for all types of writing patterns. An MPI derived data type that describes the memory layout of the - write buffer. - :type buftype: mpi4py.MPI.Datatype - - :return: The reqeust ID, which can be used in a successive call to ``File.wait`` or ``File.wait_all`` for the completion of the nonblocking operation. + write buffer from caller prepared for writing returned array values + when :meth:`File.wait` or :meth:`File.wait_all` is called. + Users should not alter the contents of the write buffer once the + request is posted until the :meth:`File.wait` or + :meth:`File.wait_all` is returned. Any change to the buffer + contents in between will result in unexpected error. + + :return: The request ID, which can be used in a successive call to + :meth:`File.wait` or :meth:`File.wait_all` for the completion + of the nonblocking operation. :rtype: int - Operational mode: This method must be called while the file is in independent data mode.""" - if data is not None and all(arg is None for arg in [start, count, stride, num, imap]): + Operational mode: This method can be called while the file is in either + collective or independent data mode. + """ + if data is not None and all(arg is None for arg in [start, count, stride, imap]): return self._iput_var(data, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start]) and all(arg is None for arg in [count, stride, num, imap]): + elif all(arg is not None for arg in [data, start]) and all(arg is None for arg in [count, stride, imap]): return self._iput_var1(data, start, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count]) and all(arg is None for arg in [stride, num, imap]): + elif all(arg is not None for arg in [data, start, count]) and all(arg is None for arg in [stride, imap]): return self._iput_vara(start, count, data, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, stride]) and all(arg is None for arg in [num, imap]): + elif all(arg is not None for arg in [data, start, count, stride]) and all(arg is None for arg in [imap]): return self._iput_vars(start, count, stride, data, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, num]) and all(arg is None for arg in [stride, imap]): - return self._iput_varn(start, count, num, data, bufcount = bufcount, buftype = buftype) - elif all(arg is not None for arg in [data, start, count, imap]) and all(arg is None for arg in [num]): + elif all(arg is not None for arg in [data, start, count, imap]): return self._iput_varm(data, start, count, stride, imap, bufcount = bufcount, buftype = buftype) else: raise ValueError("Invalid input arguments for iput_var") @@ -2966,28 +2738,54 @@ cdef class Variable: _check_err(ierr) return request - def _iget_varn(self, ndarray buff, start, count, num, bufcount, Datatype buftype): + def iget_varn(self, ndarray data, num, starts, counts=None, bufcount=None, Datatype buftype=None): + """ + iget_varn(self, data, num, starts, counts=None, bufcount=None, buftype=None) + + This method call is the nonblocking counterpart of + :meth:`Variable.get_varn`. The syntax is the same as + :meth:`Variable.get_varn`. For the argument usage, please refer to + method :meth:`Variable.get_varn`. This method returns a request ID + that can be used in :meth:`File.wait` or :meth:`File.wait_all`. The + posted write request may not be committed until :meth:`File.wait` or + :meth:`File.wait_all` is called. + + .. note:: + Unlike :meth:`Variable.get_varn`, the posted nonblocking read + requests may not be committed until the time of calling + :meth:`File.wait` or :meth:`File.wait_all`. Users should not + alter the contents of the read buffer once the request is posted + until the :meth:`File.wait` or :meth:`File.wait_all` is + returned. Any change to the buffer contents in between will result + in unexpected error. + + :return: The request ID, which can be used in a successive call to + :meth:`File.wait` or :meth:`File.wait_all` for the completion + of the nonblocking operation. + :rtype: int + """ + cdef int ierr, ndims cdef MPI_Offset buffcount cdef MPI_Datatype bufftype - cdef size_t **startp - cdef size_t **countp + cdef size_t **startsp + cdef size_t **countsp cdef int num_req cdef int request num_req = num ndims = len(self.dimensions) - max_num_req = len(start) - startp = malloc(max_num_req * sizeof(size_t*)); + max_num_req = len(starts) + startsp = malloc(max_num_req * sizeof(size_t*)); for i in range(max_num_req): - startp[i] = malloc(ndims * sizeof(size_t)); + startsp[i] = malloc(ndims * sizeof(size_t)); for j in range(ndims): - startp[i][j] = start[i][j] + startsp[i][j] = starts[i][j] - countp = malloc(max_num_req * sizeof(size_t*)); + countsp = malloc(max_num_req * sizeof(size_t*)); for i in range(max_num_req): - countp[i] = malloc(ndims * sizeof(size_t)); + countsp[i] = malloc(ndims * sizeof(size_t)); for j in range(ndims): - countp[i][j] = count[i][j] + countsp[i][j] = counts[i][j] if bufcount is None: buffcount = 1 @@ -2999,9 +2797,15 @@ cdef class Variable: else: bufftype = buftype.ob_mpi with nogil: - ierr = ncmpi_iget_varn(self._file_id, self._varid, num_req,\ - startp, countp, \ - PyArray_DATA(buff), buffcount, bufftype, &request) + ierr = ncmpi_iget_varn(self._file_id, + self._varid, + num_req, + startsp, + countsp, + PyArray_DATA(data), + buffcount, + bufftype, + &request) _check_err(ierr) return request @@ -3043,166 +2847,42 @@ cdef class Variable: _check_err(ierr) return request - def iget_var(self, data=None, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None): + def iget_var(self, data=None, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None): """ - iget_var(self, data, start=None, count=None, stride=None, num=None, imap=None, bufcount=None, buftype=None) + iget_var(self, data, start=None, count=None, stride=None, imap=None, bufcount=None, buftype=None) - Method call to post a request to read in parallel from the netCDF - variable. The behavior of the method varies depends on the pattern of - provided optional arguments - `start`, `count`, `stride`, `num` and - `imap`. This method returns a request ID that can be selected in - ``File.wait`` or ``File.wait_all``. The posted read request will not be - executed until ``File.wait`` or ``File.wait_all`` is called. + Method call to post a nonblocking request to read from the netCDF + variable. The syntax is the same as :meth:`Variable.get_var`. For the + argument usage, please refer to :meth:`Variable.get_var`. This + method returns a request ID that can be used in :meth:`File.wait` or + :meth:`File.wait_all`. The posted read request may not be committed + until :meth:`File.wait` or :meth:`File.wait_all` is called. .. note:: Note that this method requires a empty array (`data`) as a - read buffer from caller prepared for storing returned array values - when ``File.wait`` or ``File.wait_all`` is called. User is expected to - retain this buffer array handler (the numpy variable) until the read - buffer is executed and the transaction is completed. - - - `data` - Request to read an entire variable - Request to read all the values from a netCDF variable of an opened - netCDF file. This is the simplest interface to use for reading the - value of a scalar variable or when all the values of a - multidimensional variable can be read at once. - - .. note:: Be careful when using the simplest forms of this interface - with record variables when you don’t specify how many records are to - be read. If you try to read all the values of a record variable into - an array but there are more records in the file than you assume, more - data will be read than you expect, which may cause a segmentation - violation. - - - `data`, `start` - Request to read a single data value (a single element) - Put a single data value specified by `start` from a variable of an - opened netCDF file that is in data mode. For example, start = [0,5] - would specify the following position in a 4 * 10 two-dimensional - variable ("-" means skip). - - :: - - - - - - - a - - - - - - - - - - - - - - - -> a - - - - - - - - - - - - - - - - - - - - - - - - - `data`, `start`, `count` - Request to read a subarray of values - The part of the netCDF variable to read is specified by giving a corner index and a vector of edge lengths that refer to - an array section of the netCDF variable. For example, start = [0,5] and count = [2,2] would specify the following array - section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a b - - - - - - - - - c d - - - a b - - - - - - - - - - - -> c d - - - - - - - - - - - - - - `data`, `start`, `count`, `stride` - Request to read a subsampled array of values - The part of the netCDF variable to read is specified by giving a corner, a vector of edge lengths and stride vector that - refer to a subsampled array section of the netCDF variable. For example, start = [0,2], count = [2,4] and stride = [1,2] - would specify the following array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - a - b - c - d - - - - e - f - g - h - a b c d - - - - - - - - - - - -> e f g h - - - - - - - - - - - - - - `data`, `start`, `count`, `imap`, `stride` (optional) - Request to read a mapped array of values - The mapped array section is specified by giving a corner, a vector of counts, a stride vector, and an index mapping vector. - The index mapping vector (imap) is a vector of integers that specifies the mapping between the dimensions of a netCDF variable - and the in-memory structure of the internal data array. For example, imap = [3,8], start = [0,5] and count = [2,2] would specify the following - section in read butter and array section in a 4 * 10 two-dimensional variable ("-" means skip). - - :: - - - - - - - a c - - - - a - - b a c - - - - - b d - - - - - - - - <= b d <= - - - - - - - - - - - c - - d - - - - - - - - - - - distance from a to b is 3 in buffer => imap[0] = 3 - distance from a to c is 8 in buffer => imap[1] = 8 - - - `data`, `start`, `count`, `num` - Request to read a list of subarrays of values - The part of the netCDF variable to read is specified by giving a list of subarrays and each subarray is specified by a corner and a vector of - edge lengths that refer to an array section of the netCDF variable. The example code and diagram below illustrates a lists of 4 specified - subarray sections in a 4 * 10 two-dimensional variable ("-" means skip). - - - :: - - num = 4 - start[0][0] = 0; start[0][1] = 5; count[0][0] = 1; count[0][1] = 2 - start[1][0] = 1; start[1][1] = 0; count[1][0] = 1; count[1][1] = 1 - start[2][0] = 2; start[2][1] = 6; count[2][0] = 1; count[2][1] = 2 - start[3][0] = 3; start[3][1] = 0; count[3][0] = 1; count[3][1] = 3 - - - - - - a b - - - - a b c d e f g h <= c - - - - - - - - - - - - - - - - d e - - - f g h - - - - - - - - - :param data: the numpy array that stores array values to be written, which serves as a read buffer. The datatype should match with the - variable's datatype. Note this numpy array read buffer can be in any shape as long as the number of elements (buffer size) is matched. - - :type data: numpy.ndarray - - :param start: [Optional] Only relevant when reading a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the index in the variable where the first of the data values will be written. The - elements of `start` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for reading the data values. When reading to a list of subarrays, `start` - is 2D array of size [num][ndims] and each start[i] is a vector specifying the index in the variable where the first of the data values - will be written. - :type start: numpy.ndarray - - :param count: [Optional] Only relevant when reading a array of values, a subsampled array, a mapped array or a list of subarrays. - An array of integers specifying the edge lengths along each dimension of the block of data values to be written. The - elements of `count` must correspond to the variable’s dimensions in order. Hence, if the variable is a record variable, the first - index would correspond to the starting record number for reading the data values. When reading to a list of subarrays, `count` - is 2D array of size [num][ndims] and each count[i] is a vector specifying the edge lengths along each dimension of the block of - data values to be written. - - :type count: numpy.ndarray - - :param stride: [Optional] Only relevant when reading a subsampled array or a mapped array. An array of integers specifying - the sampling interval along each dimension of the netCDF variable. The elements of the stride vector correspond, in order, to the - netCDF variable’s dimensions. - :type stride: numpy.ndarray - - :param num: [Optional] Only relevant when reading a list of subarrays. An integer specifying the number of subarrays. - :type num: int - - :param imap: [Optional] Only relevant when reading a subsampled array or a mapped array. An array of integers the mapping between - the dimensions of a netCDF variable and the in-memory structure of the internal data array. The elements of the index mapping vector - correspond, in order, to the netCDF variable’s dimensions. Each element value of imap should equal the memory location distance in read buffer between two adjacent elements along the corresponding dimension of netCDF variable. - :type imap: numpy.ndarray - - :param bufcount: [Optional] Optional for all types of reading patterns. An integer indicates the number of MPI derived data type elements - in the read buffer to be written to the file. - :type bufcount: int - - :param buftype: [Optional] Optional for all types of reading patterns. An MPI derived data type that describes the memory layout of the - read buffer. - :type buftype: mpi4py.MPI.Datatype - - :return: The reqeust ID, which can be used in a successive call to ``File.wait`` or ``File.wait_all`` for the completion of the nonblocking operation. + read buffer from caller prepared for storing returned array values + when :meth:`File.wait` or :meth:`File.wait_all` is called. User + is expected to retain this buffer array handler (the numpy + variable) until the read buffer is committed and the transaction is + completed. + + :return: The request ID, which can be used in a successive call to + :meth:`File.wait` or :meth:`File.wait_all` for the completion + of the nonblocking operation. :rtype: int - Operational mode: This method can be called in either define or (collective or independent) data mode. + Operational mode: This method can be called in either define, + collective, or independent data mode. """ - if data is not None and all(arg is None for arg in [start, count, stride, num, imap]): + if data is not None and all(arg is None for arg in [start, count, stride, imap]): return self._iget_var(data, bufcount, buftype) - elif all(arg is not None for arg in [data, start]) and all(arg is None for arg in [count, stride, num, imap]): + elif all(arg is not None for arg in [data, start]) and all(arg is None for arg in [count, stride, imap]): return self._iget_var1(data, start, bufcount, buftype) - elif all(arg is not None for arg in [data, start, count]) and all(arg is None for arg in [stride, num, imap]): + elif all(arg is not None for arg in [data, start, count]) and all(arg is None for arg in [stride, imap]): return self._iget_vara(data, start, count, bufcount, buftype) - elif all(arg is not None for arg in [data, start, count, stride]) and all(arg is None for arg in [num, imap]): + elif all(arg is not None for arg in [data, start, count, stride]) and all(arg is None for arg in [imap]): return self._iget_vars(data, start, count, stride, bufcount, buftype) - elif all(arg is not None for arg in [data, start, count, num]) and all(arg is None for arg in [stride, imap]): - return self._iget_varn(data, start, count, num, bufcount, buftype) - elif all(arg is not None for arg in [data, start, count, imap]) and all(arg is None for arg in [num]): + elif all(arg is not None for arg in [data, start, count, imap]): return self._iget_varm(data, start, count, stride, imap, bufcount, buftype) else: raise ValueError("Invalid input arguments for iget_var") @@ -3211,11 +2891,13 @@ cdef class Variable: """ inq_offset(self) - Returns the starting file offset of this netCDF variable + :return: The starting file offset of this netCDF variable. + :rtype: int64 """ cdef int ierr cdef int offset with nogil: - ierr = ncmpi_inq_varoffset(self._file_id, self._varid, &offset) + ierr = ncmpi_inq_varoffset(self._file_id, self._varid, + &offset) _check_err(ierr) return offset diff --git a/src/pnetcdf/_utils.pyx b/src/pnetcdf/_utils.pyx index 21f350a..289d4d9 100644 --- a/src/pnetcdf/_utils.pyx +++ b/src/pnetcdf/_utils.pyx @@ -296,7 +296,7 @@ cdef _get_att(file, int varid, name, encoding='utf-8'): cdef _get_att_names(int file_id, int varid): - # Private function to get all the attribute names in a group + # Private function to get all the attribute names of a variable cdef int ierr, numatts, n cdef char namstring[NC_MAX_NAME+1] if varid == NC_GLOBAL: diff --git a/test/tst_var_bput_varn.py b/test/tst_var_bput_varn.py index 028cc0d..e7ca294 100644 --- a/test/tst_var_bput_varn.py +++ b/test/tst_var_bput_varn.py @@ -4,13 +4,14 @@ # """ -This example program is intended to illustrate the use of the pnetCDF python -API. The program runs in non-blocking mode and makes a request to write a list -of subarray of values to a variable into a netCDF variable of an opened netCDF -file using bput_var method of `Variable` class. This method is a buffered -version of bput_var and requires the user to attach an internal buffer of size -equal to the sum of all requests using attach_buff method of `File` class. The -library will internally invoke ncmpi_bput_vara and ncmpi_attach_buffer in C. +This program test ibput_varn() method of `Variable` class, a nonblocking, +buffered API, by making multiple calls to it to write to different NetCDF +variables of an opened netCDF file. Each call also writes to multiple +subarrays of the same variable. This method is a buffered version of +iput_varn() and requires the user to first attach an internal buffer of size +equal to the sum of all requests using attach_buff() method of `File` class. +The library will internally invoke ncmpi_bput_varn() and ncmpi_attach_buffer() +in C. """ import numpy as np @@ -165,8 +166,8 @@ def run_test(format): for i in range(num_reqs): v = f.variables[f'data{i}'] assert(f.inq_buff_size() - f.inq_buff_usage() > 0) - # post the request to write an array of values - req_id = v.bput_var(data, start = starts, count = counts, num = num_subarrays) + # post the request to write multiple subarrays + req_id = v.bput_varn(data, num_subarrays, starts, counts) # track the request ID for each write request req_ids.append(req_id) @@ -182,7 +183,7 @@ def run_test(format): # post requests to write the 2nd half of variables w/o tracking req ids for i in range(num_reqs): v = f.variables[f'data{num_reqs + i}'] - v.bput_var(data, start = starts, count = counts, num = num_subarrays) + v.bput_varn(data, num_subarrays, starts, counts) # commit the posted requests all at once using wait_all (collective i/o) f.wait_all(num = pnetcdf.NC_PUT_REQ_ALL) @@ -218,3 +219,4 @@ def run_test(format): except BaseException as err: print("Error: type:", type(err), str(err)) raise + diff --git a/test/tst_var_get_varn.py b/test/tst_var_get_varn.py index bb7b6e3..5a52ff3 100644 --- a/test/tst_var_get_varn.py +++ b/test/tst_var_get_varn.py @@ -4,20 +4,17 @@ # import pnetcdf -from numpy.random import seed, randint -from numpy.testing import assert_array_equal, assert_equal, assert_array_almost_equal -import tempfile, unittest, os, random, sys +from numpy.testing import assert_array_equal +import unittest, os, sys import numpy as np from mpi4py import MPI from utils import validate_nc_file import io import argparse - file_formats = ['NC_64BIT_DATA', 'NC_64BIT_OFFSET', None] file_name = "tst_var_get_varn.nc" - comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() @@ -85,7 +82,8 @@ # - - - - - - - 3 3 3 else: num_reqs = 0 -#Obtain the size of returned array, needed for generating reference array + +# Obtain the size of returned array, needed for generating reference array buf_len = np.sum(np.prod(counts, axis=1)) class VariablesTestCase(unittest.TestCase): @@ -96,7 +94,9 @@ def setUp(self): else: self.file_path = file_name self._file_format = file_formats.pop(0) - f = pnetcdf.File(filename=self.file_path, mode = 'w', format=self._file_format, comm=comm, info=None) + f = pnetcdf.File(filename=self.file_path, mode = 'w', + format=self._file_format, comm=comm, info=None) + # define dimensions and variables f.def_dim('x',xdim) f.def_dim('y',ydim) @@ -107,8 +107,8 @@ def setUp(self): var1[:] = data comm.Barrier() - assert validate_nc_file(os.environ.get('PNETCDF_DIR'), self.file_path) == 0 if os.environ.get('PNETCDF_DIR') is not None else True - + if os.environ.get('PNETCDF_DIR') is not None: + assert validate_nc_file(os.environ.get('PNETCDF_DIR'), self.file_path) == 0 def tearDown(self): # Remove the temporary files @@ -120,16 +120,19 @@ def runTest(self): """testing variable get varn for CDF-5/CDF-2/CDF-1 file""" dataref = np.full((buf_len,), rank, np.float32) f = pnetcdf.File(self.file_path, 'r') - # test collective i/o get_var + + # test collective i/o get_varn v1 = f.variables['var1'] buff = np.empty((buf_len,), v1.dtype) - v1.get_var_all(buff, start = starts, count = counts, num = num_reqs) + v1.get_varn_all(buff, num_reqs, starts, counts) assert_array_equal(buff, dataref) - # test independent i/o get_var + + # test independent i/o get_varn f.begin_indep() buff = np.empty((buf_len,), v1.dtype) - v1.get_var(buff, start = starts, count = counts, num = num_reqs) + v1.get_varn(buff, num_reqs, starts, counts) assert_array_equal(buff, dataref) + f.close() if __name__ == '__main__': @@ -143,3 +146,4 @@ def runTest(self): if not result.wasSuccessful(): print(output.getvalue()) sys.exit(1) + diff --git a/test/tst_var_iget_varn.py b/test/tst_var_iget_varn.py index 9d0af86..16120fd 100644 --- a/test/tst_var_iget_varn.py +++ b/test/tst_var_iget_varn.py @@ -4,15 +4,15 @@ # """ - This example program is intended to illustrate the use of the pnetCDF python API. - The program runs in non-blocking mode and makes a request to read a list of subarray of - a netCDF variable of an opened netCDF file using iget_var method of `Variable` class. The - library will internally invoke ncmpi_iget_varn in C. +This program tests iget_varn() method of `Variable` class, by making a +nonblocking request to read a list of subarrays of a netCDF variable of an +opened netCDF file. The library will internally invoke ncmpi_iget_varn() in C. """ + import pnetcdf -from numpy.random import seed, randint -from numpy.testing import assert_array_equal, assert_equal, assert_array_almost_equal -import tempfile, unittest, os, random, sys +from numpy.random import seed +from numpy.testing import assert_array_equal +import unittest, os, random, sys import numpy as np from mpi4py import MPI from pnetcdf import strerror, strerrno @@ -90,14 +90,16 @@ # - - - - - - - 3 3 3 else: num_subarray_reqs = 0 + # obtain the buffer size of returned array buf_len = 0 for i in range(num_subarray_reqs): w_req_len = np.prod(counts[i,:]) buf_len += w_req_len + # generate reference array for comparing at the testing phase dataref = np.full((buf_len,), rank, np.float32) -# total number of iget_var requests for this test program +# total number of subarray requests for this test program num_reqs = 10 # initialize a list to store references of variable values v_datas = [] @@ -110,58 +112,71 @@ def setUp(self): else: self.file_path = file_name self._file_format = file_formats.pop(0) - f = pnetcdf.File(filename=self.file_path, mode = 'w', format=self._file_format, comm=comm, info=None) + + f = pnetcdf.File(filename=self.file_path, mode = 'w', + format=self._file_format, comm=comm, info=None) + dx = f.def_dim('x',xdim) dy = f.def_dim('y',ydim) # define 20 netCDF variables for i in range(num_reqs * 2): v = f.def_var(f'data{i}', pnetcdf.NC_FLOAT, (dx, dy)) - # initialize variable values + f.enddef() + + # initialize and write variable values for i in range(num_reqs * 2): v = f.variables[f'data{i}'] v[:] = data - f.close() - assert validate_nc_file(os.environ.get('PNETCDF_DIR'), self.file_path) == 0 if os.environ.get('PNETCDF_DIR') is not None else True + f.close() + if os.environ.get('PNETCDF_DIR') is not None: + assert validate_nc_file(os.environ.get('PNETCDF_DIR'), self.file_path) == 0 f = pnetcdf.File(self.file_path, 'r') + # each process post 10 requests to read a list of subarrays from the variable req_ids = [] v_datas.clear() + for i in range(num_reqs): v = f.variables[f'data{i}'] buff = np.empty(shape = buf_len, dtype = v.datatype) # post the request to read multiple slices (subarrays) of the variable - req_id = v.iget_var(buff, start = starts, count = counts, num = num_subarray_reqs) + req_id = v.iget_varn(buff, num_subarray_reqs, starts, counts) # track the reqeust ID for each read reqeust req_ids.append(req_id) # store the reference of variable values v_datas.append(buff) - f.end_indep() + # commit those 10 requests to the file at once using wait_all (collective i/o) req_errs = [None] * num_reqs f.wait_all(num_reqs, req_ids, req_errs) + # check request error msg for each unsuccessful requests for i in range(num_reqs): if strerrno(req_errs[i]) != "NC_NOERR": print(f"Error on request {i}:", strerror(req_errs[i])) - # post 10 requests to read an array of values for the last 10 variables w/o tracking req ids + # post 10 requests to read an array of values for the last 10 + # variables w/o tracking req ids for i in range(num_reqs, num_reqs * 2): v = f.variables[f'data{i}'] buff = np.empty(buf_len, dtype = v.datatype) # post the request to read a list of subarrays from the variable - v.iget_var(buff, start = starts, count = counts, num = num_subarray_reqs) + v.iget_varn(buff, num_subarray_reqs, starts, counts) # store the reference of variable values v_datas.append(buff) - # commit all pending get requests to the file at once using wait_all (collective i/o) + # commit all pending get requests to the file at once using wait_all + # (collective i/o) req_errs = f.wait_all(num = pnetcdf.NC_GET_REQ_ALL) + f.close() - assert validate_nc_file(os.environ.get('PNETCDF_DIR'), self.file_path) == 0 if os.environ.get('PNETCDF_DIR') is not None else True + if os.environ.get('PNETCDF_DIR') is not None: + assert validate_nc_file(os.environ.get('PNETCDF_DIR'), self.file_path) == 0 def tearDown(self): @@ -188,3 +203,4 @@ def runTest(self): if not result.wasSuccessful(): print(output.getvalue()) sys.exit(1) + diff --git a/test/tst_var_iput_varn.py b/test/tst_var_iput_varn.py index a42ad21..be332f1 100644 --- a/test/tst_var_iput_varn.py +++ b/test/tst_var_iput_varn.py @@ -4,22 +4,19 @@ # """ - This example program is intended to illustrate the use of the pnetCDF python API. The program - runs in non-blocking mode and makes a request to write a list of subarray of values to a variable - into a netCDF variable of an opened netCDF file using iput_var method of `Variable` class. The - library will internally invoke ncmpi_iput_varn in C. +This program tests nonblocking method, iput_varn() of `Variable` class, by +making a write request consisting of multiple subarrays to a variable of an +opened netCDF file. The library will internally invoke ncmpi_iput_varn() in C. """ import pnetcdf -from numpy.random import seed, randint -from numpy.testing import assert_array_equal, assert_equal, assert_array_almost_equal -import tempfile, unittest, os, random, sys +from numpy.testing import assert_array_equal +import unittest, os, sys import numpy as np from mpi4py import MPI from pnetcdf import strerror, strerrno from utils import validate_nc_file import io -seed(0) file_formats = ['NC_64BIT_DATA', 'NC_64BIT_OFFSET', None] file_name = "tst_var_iput_varn.nc" @@ -115,7 +112,10 @@ def setUp(self): else: self.file_path = file_name self._file_format = file_formats.pop(0) - f = pnetcdf.File(filename=self.file_path, mode = 'w', format=self._file_format, comm=comm, info=None) + + f = pnetcdf.File(filename=self.file_path, mode = 'w', + format=self._file_format, comm=comm, info=None) + dx = f.def_dim('x',xdim) dy = f.def_dim('y',ydim) @@ -136,11 +136,12 @@ def setUp(self): for i in range(num_reqs): v = f.variables[f'data{i}'] # post the request to write an array of values - req_id = v.iput_var(data, start = starts, count = counts, num = num_subarrays) + req_id = v.iput_varn(data, num_subarrays, starts, counts) # track the reqeust ID for each write reqeust req_ids.append(req_id) - # all processes commit those 10 requests to the file at once using wait_all (collective i/o) + # all processes commit those 10 requests to the file at once using + # wait_all (collective i/o) req_errs = [None] * num_reqs f.wait_all(num_reqs, req_ids, req_errs) @@ -149,13 +150,15 @@ def setUp(self): if strerrno(req_errs[i]) != "NC_NOERR": print(f"Error on request {i}:", strerror(req_errs[i])) - # post 10 requests to write an array of values for the last 10 variables w/o tracking req ids + # post 10 requests to write an array of values for the last 10 + # variables w/o tracking req ids for i in range(num_reqs): v = f.variables[f'data{num_reqs + i}'] # post the request to write an array of values - v.iput_var(data, start = starts, count = counts, num = num_subarrays) + v.iput_varn(data, num_subarrays, starts, counts) - # all processes commit all pending requests to the file at once using wait_all (collective i/o) + # all processes commit all pending requests to the file at once using + # wait_all (collective I/O) f.wait_all(num = pnetcdf.NC_PUT_REQ_ALL) f.close() @@ -186,3 +189,4 @@ def runTest(self): if not result.wasSuccessful(): print(output.getvalue()) sys.exit(1) + diff --git a/test/tst_var_put_varn.py b/test/tst_var_put_varn.py index 32b9685..230ef64 100644 --- a/test/tst_var_put_varn.py +++ b/test/tst_var_put_varn.py @@ -4,21 +4,17 @@ # import pnetcdf -from numpy.random import seed, randint -from numpy.testing import assert_array_equal, assert_equal, assert_array_almost_equal -import tempfile, unittest, os, random, sys +from numpy.testing import assert_array_equal +import unittest, os, sys import numpy as np from mpi4py import MPI from utils import validate_nc_file import io import argparse - - file_formats = ['NC_64BIT_DATA', 'NC_64BIT_OFFSET', None] file_name = "tst_var_put_varn.nc" - comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() @@ -43,7 +39,10 @@ def setUp(self): else: self.file_path = file_name self._file_format = file_formats.pop(0) - f = pnetcdf.File(filename=self.file_path, mode = 'w', format=self._file_format, comm=comm, info=None) + + f = pnetcdf.File(filename=self.file_path, mode = 'w', + format=self._file_format, comm=comm, info=None) + # define dimensions and variables f.def_dim('x',xdim) f.def_dim('y',ydim) @@ -107,23 +106,28 @@ def setUp(self): # - - - - - - - 3 3 3 else: num_reqs = 0 + # allocate write buffer buf_len = 0 for i in range(num_reqs): w_req_len = np.prod(counts[i,:]) buf_len += w_req_len + data = np.empty(buf_len, dtype=np.float32) data.fill(rank) - # collective put_var - var1.put_var_all(data, start = starts, count = counts, num = num_reqs) - # independent put_var + # test collective put_varn + var1.put_varn_all(data, num_reqs, starts, counts) + + # test independent put_varn f.begin_indep() - var2.put_var(data, start = starts, count = counts, num = num_reqs) + var2.put_varn(data, num_reqs, starts, counts) + f.close() - comm.Barrier() - assert validate_nc_file(os.environ.get('PNETCDF_DIR'), self.file_path) == 0 if os.environ.get('PNETCDF_DIR') is not None else True + comm.Barrier() + if os.environ.get('PNETCDF_DIR') is not None: + assert validate_nc_file(os.environ.get('PNETCDF_DIR'), self.file_path) == 0 def tearDown(self): # Remove the temporary files @@ -132,15 +136,17 @@ def tearDown(self): os.remove(self.file_path) def runTest(self): - """testing variable put vara all""" + """testing API put_varn_all""" f = pnetcdf.File(self.file_path, 'r') - # test collective i/o put_var + # create a variable for testing collective i/o put_varn v1 = f.variables['var1'] assert_array_equal(v1[:], dataref) - # test independent i/o put_var + + # create a variable for testing independent i/o put_varn v2 = f.variables['var2'] assert_array_equal(v2[:], dataref) + f.close() if __name__ == '__main__':