Skip to content

Commit f1ce057

Browse files
authored
Merge pull request #897 from pastewka/master
Support for pnetcdf
2 parents 8200187 + 8b04ea2 commit f1ce057

File tree

9 files changed

+136
-48
lines changed

9 files changed

+136
-48
lines changed

Diff for: .travis.yml

+28-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,23 @@ matrix:
5353
- mpich
5454
- libmpich-dev
5555
- libhdf5-mpich-dev
56+
# test MPI with latest released version
57+
- python: 3.7
58+
dist: xenial
59+
env:
60+
- MPI=1
61+
- CC=mpicc.mpich
62+
- DEPENDS="numpy>=1.9.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
63+
- NETCDF_VERSION=4.6.3
64+
- PNETCDF_VERSION=1.11.0
65+
- NETCDF_DIR=$HOME
66+
- PATH=${NETCDF_DIR}/bin:${PATH} # pick up nc-config here
67+
addons:
68+
apt:
69+
packages:
70+
- mpich
71+
- libmpich-dev
72+
- libhdf5-mpich-dev
5673
# test with netcdf-c from github master
5774
- python: 3.7
5875
dist: xenial
@@ -86,6 +103,8 @@ script:
86103
- cd test
87104
- python run_all.py
88105
- |
106+
echo "MPI = ${MPI}"
107+
echo "PNETCDF_VERSION = ${PNETCDF_VERSION}"
89108
if [ $MPI -eq 1 ] ; then
90109
cd ../examples
91110
mpirun.mpich -np 4 python mpi_example.py
@@ -94,6 +113,14 @@ script:
94113
exit 1
95114
else
96115
echo "mpi test passed!"
97-
exit 0
116+
fi
117+
if [ -n "${PNETCDF_VERSION}" ] ; then
118+
mpirun.mpich -np 4 python mpi_example.py NETCDF3_64BIT_DATA
119+
if [ $? -ne 0 ] ; then
120+
echo "PnetCDF mpi test failed!"
121+
exit 1
122+
else
123+
echo "PnetCDF mpi test passed!"
124+
fi
98125
fi
99126
fi

Diff for: Changelog

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
current master
2+
===============
3+
* added support for parallel IO in the classic netcdf-3 formats through the
4+
pnetcdf library.
5+
16
version 1.4.3.2 (tag v1.4.3.2)
27
===============================
38
* include missing membuf.pyx file in release source tarball.

Diff for: ci/travis/build-parallel-netcdf.sh

+15-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,19 @@
22

33
set -e
44

5-
echo "Using downloaded netCDF version ${NETCDF_VERSION} with parallel capabilities enabled"
65
pushd /tmp
6+
if [ -n "${PNETCDF_VERSION}" ]; then
7+
echo "Using downloaded PnetCDF version ${PNETCDF_VERSION}"
8+
wget https://parallel-netcdf.github.io/Release/pnetcdf-${PNETCDF_VERSION}.tar.gz
9+
tar -xzf pnetcdf-${PNETCDF_VERSION}.tar.gz
10+
pushd pnetcdf-${PNETCDF_VERSION}
11+
./configure --prefix $NETCDF_DIR --enable-shared --disable-fortran --disable-cxx
12+
NETCDF_EXTRA_CONFIG="--enable-pnetcdf"
13+
make -j 2
14+
make install
15+
popd
16+
fi
17+
echo "Using downloaded netCDF version ${NETCDF_VERSION} with parallel capabilities enabled"
718
if [ ${NETCDF_VERSION} == "GITMASTER" ]; then
819
git clone http://github.com/Unidata/netcdf-c netcdf-c
920
pushd netcdf-c
@@ -14,9 +25,10 @@ else
1425
pushd netcdf-c-${NETCDF_VERSION}
1526
fi
1627
# for Ubuntu xenial
17-
export CPPFLAGS="-I/usr/include/hdf5/mpich"
28+
export CPPFLAGS="-I/usr/include/hdf5/mpich -I${NETCDF_DIR}/include"
29+
export LDFLAGS="-L${NETCDF_DIR}/lib"
1830
export LIBS="-lhdf5_mpich_hl -lhdf5_mpich -lm -lz"
19-
./configure --prefix $NETCDF_DIR --enable-netcdf-4 --enable-shared --disable-dap --enable-parallel
31+
./configure --prefix $NETCDF_DIR --enable-netcdf-4 --enable-shared --disable-dap --enable-parallel4 $NETCDF_EXTRA_CONFIG
2032
make -j 2
2133
make install
2234
popd

Diff for: examples/mpi_example.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
# to run: mpirun -np 4 python mpi_example.py
2+
import sys
23
from mpi4py import MPI
34
import numpy as np
45
from netCDF4 import Dataset
6+
if len(sys.argv) == 2:
7+
format = sys.argv[1]
8+
else:
9+
format = 'NETCDF4_CLASSIC'
510
rank = MPI.COMM_WORLD.rank # The process ID (integer 0-3 for 4-process run)
11+
if rank == 0:
12+
print('Creating file with format {}'.format(format))
613
nc = Dataset('parallel_test.nc', 'w', parallel=True, comm=MPI.COMM_WORLD,
7-
info=MPI.Info(),format='NETCDF4_CLASSIC')
14+
info=MPI.Info(),format=format)
815
# below should work also - MPI_COMM_WORLD and MPI_INFO_NULL will be used.
916
#nc = Dataset('parallel_test.nc', 'w', parallel=True)
1017
d = nc.createDimension('dim',4)

Diff for: include/netCDF4.pxi

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ cdef extern from "netcdf.h":
5151
NC_CLOBBER
5252
NC_NOCLOBBER # Don't destroy existing file on create
5353
NC_64BIT_OFFSET # Use large (64-bit) file offsets
54+
NC_64BIT_DATA # Use cdf-5 format
5455
NC_NETCDF4 # Use netCDF-4/HDF5 format
5556
NC_CLASSIC_MODEL # Enforce strict netcdf-3 rules.
5657
# Use these 'mode' flags for both nc_create and nc_open.
@@ -703,7 +704,7 @@ IF HAS_NC_CREATE_MEM:
703704
int flags
704705
int nc_close_memio(int ncid, NC_memio* info);
705706

706-
IF HAS_NC_PAR:
707+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
707708
cdef extern from "mpi-compat.h": pass
708709
cdef extern from "netcdf_par.h":
709710
ctypedef int MPI_Comm

Diff for: netCDF4/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ._netCDF4 import (__version__, __netcdf4libversion__, __hdf5libversion__,
77
__has_rename_grp__, __has_nc_inq_path__,
88
__has_nc_inq_format_extended__, __has_nc_open_mem__,
9-
__has_nc_create_mem__,__has_cdf5_format__,__has_nc_par__)
9+
__has_nc_create_mem__, __has_cdf5_format__,
10+
__has_parallel4_support__, __has_pnetcdf_support__)
1011
__all__ =\
1112
['Dataset','Variable','Dimension','Group','MFDataset','MFTime','CompoundType','VLType','date2num','num2date','date2index','stringtochar','chartostring','stringtoarr','getlibversion','EnumType']

Diff for: netCDF4/_netCDF4.pyx

+50-29
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ Requires
6363
If you want [OPeNDAP](http://opendap.org) support, add `--enable-dap`.
6464
If you want HDF4 SD support, add `--enable-hdf4` and add
6565
the location of the HDF4 headers and library to `$CPPFLAGS` and `$LDFLAGS`.
66-
- for MPI parallel IO support, MPI-enabled versions of the HDF5 and netcdf
67-
libraries are required, as is the [mpi4py](http://mpi4py.scipy.org) python
68-
module.
66+
- for MPI parallel IO support, an MPI-enabled versions of the netcdf library
67+
is required, as is the [mpi4py](http://mpi4py.scipy.org) python module.
68+
Parallel IO further depends on the existence of MPI-enabled HDF5 or the
69+
[PnetCDF](https://parallel-netcdf.github.io/) library.
6970
7071
7172
Install
@@ -918,13 +919,14 @@ specified names.
918919
919920
## <div id='section13'>13) Parallel IO.
920921
921-
If MPI parallel enabled versions of netcdf and hdf5 are detected, and
922-
[mpi4py](https://mpi4py.scipy.org) is installed, netcdf4-python will
923-
be built with parallel IO capabilities enabled. Since parallel IO
924-
uses features of HDF5, it can only be used with NETCDF4 or
925-
NETCDF4_CLASSIC formatted files. To use parallel IO,
926-
your program must be running in an MPI environment using
927-
[mpi4py](https://mpi4py.scipy.org).
922+
If MPI parallel enabled versions of netcdf and hdf5 or pnetcdf are detected,
923+
and [mpi4py](https://mpi4py.scipy.org) is installed, netcdf4-python will
924+
be built with parallel IO capabilities enabled. Parallel IO of NETCDF4 or
925+
NETCDF4_CLASSIC formatted files is only available if the MPI parallel HDF5
926+
library is available. Parallel IO of classic netcdf-3 file formats is only
927+
available if the [PnetCDF](https://parallel-netcdf.github.io/) library is
928+
available. To use parallel IO, your program must be running in an MPI
929+
environment using [mpi4py](https://mpi4py.scipy.org).
928930
929931
:::python
930932
>>> from mpi4py import MPI
@@ -971,9 +973,12 @@ participate in doing IO. To toggle back and forth between
971973
the two types of IO, use the `netCDF4.Variable.set_collective`
972974
`netCDF4.Variable`method. All metadata
973975
operations (such as creation of groups, types, variables, dimensions, or attributes)
974-
are collective. There are a couple of important limitatons of parallel IO:
976+
are collective. There are a couple of important limitations of parallel IO:
975977
976-
- only works with NETCDF4 or NETCDF4_CLASSIC formatted files.
978+
- parallel IO for NETCDF4 or NETCDF4_CLASSIC formatted files is only available
979+
if the netcdf library was compiled with MPI enabled HDF5.
980+
- parallel IO for all classic netcdf-3 file formats is only available if the
981+
netcdf library was compiled with PnetCDF.
977982
- If a variable has an unlimited dimension, appending data must be done in collective mode.
978983
If the write is done in independent mode, the operation will fail with a
979984
a generic "HDF Error".
@@ -1202,7 +1207,7 @@ import_array()
12021207
include "constants.pyx"
12031208
include "membuf.pyx"
12041209
include "netCDF4.pxi"
1205-
IF HAS_NC_PAR:
1210+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
12061211
cimport mpi4py.MPI as MPI
12071212
from mpi4py.libmpi cimport MPI_Comm, MPI_Info, MPI_Comm_dup, MPI_Info_dup, \
12081213
MPI_Comm_free, MPI_Info_free, MPI_INFO_NULL,\
@@ -1240,7 +1245,8 @@ __has_nc_inq_format_extended__ = HAS_NC_INQ_FORMAT_EXTENDED
12401245
__has_cdf5_format__ = HAS_CDF5_FORMAT
12411246
__has_nc_open_mem__ = HAS_NC_OPEN_MEM
12421247
__has_nc_create_mem__ = HAS_NC_CREATE_MEM
1243-
__has_nc_par__ = HAS_NC_PAR
1248+
__has_parallel4_support__ = HAS_PARALLEL4_SUPPORT
1249+
__has_pnetcdf_support__ = HAS_PNETCDF_SUPPORT
12441250
_needsworkaround_issue485 = __netcdf4libversion__ < "4.4.0" or \
12451251
(__netcdf4libversion__.startswith("4.4.0") and \
12461252
"-development" in __netcdf4libversion__)
@@ -1282,20 +1288,29 @@ _intnptonctype = {'i1' : NC_BYTE,
12821288
_format_dict = {'NETCDF3_CLASSIC' : NC_FORMAT_CLASSIC,
12831289
'NETCDF4_CLASSIC' : NC_FORMAT_NETCDF4_CLASSIC,
12841290
'NETCDF4' : NC_FORMAT_NETCDF4}
1291+
# create dictionary mapping string identifiers to netcdf create format codes
1292+
_cmode_dict = {'NETCDF3_CLASSIC' : NC_CLASSIC_MODEL,
1293+
'NETCDF4_CLASSIC' : NC_CLASSIC_MODEL | NC_NETCDF4,
1294+
'NETCDF4' : NC_NETCDF4}
12851295
IF HAS_CDF5_FORMAT:
12861296
# NETCDF3_64BIT deprecated, saved for compatibility.
12871297
# use NETCDF3_64BIT_OFFSET instead.
12881298
_format_dict['NETCDF3_64BIT_OFFSET'] = NC_FORMAT_64BIT_OFFSET
12891299
_format_dict['NETCDF3_64BIT_DATA'] = NC_FORMAT_64BIT_DATA
1300+
_cmode_dict['NETCDF3_64BIT_OFFSET'] = NC_64BIT_OFFSET
1301+
_cmode_dict['NETCDF3_64BIT_DATA'] = NC_64BIT_DATA
12901302
ELSE:
12911303
_format_dict['NETCDF3_64BIT'] = NC_FORMAT_64BIT
1304+
_cmode_dict['NETCDF3_64BIT'] = NC_64BIT_OFFSET
12921305
# invert dictionary mapping
12931306
_reverse_format_dict = dict((v, k) for k, v in _format_dict.iteritems())
12941307
# add duplicate entry (NETCDF3_64BIT == NETCDF3_64BIT_OFFSET)
12951308
IF HAS_CDF5_FORMAT:
12961309
_format_dict['NETCDF3_64BIT'] = NC_FORMAT_64BIT_OFFSET
1310+
_cmode_dict['NETCDF3_64BIT'] = NC_64BIT_OFFSET
12971311
ELSE:
12981312
_format_dict['NETCDF3_64BIT_OFFSET'] = NC_FORMAT_64BIT
1313+
_cmode_dict['NETCDF3_64BIT_OFFSET'] = NC_64BIT_OFFSET
12991314

13001315
# default fill_value to numpy datatype mapping.
13011316
default_fillvals = {#'S1':NC_FILL_CHAR,
@@ -2084,7 +2099,7 @@ strings.
20842099
cdef char *path
20852100
cdef char namstring[NC_MAX_NAME+1]
20862101
cdef int cmode
2087-
IF HAS_NC_PAR:
2102+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
20882103
cdef MPI_Comm mpicomm
20892104
cdef MPI_Info mpiinfo
20902105

@@ -2107,12 +2122,20 @@ strings.
21072122
raise ValueError(msg)
21082123

21092124
if parallel:
2110-
IF HAS_NC_PAR != 1:
2125+
IF HAS_PARALLEL4_SUPPORT != 1 and HAS_PNETCDF_SUPPORT != 1:
21112126
msg='parallel mode requires MPI enabled netcdf-c'
21122127
raise ValueError(msg)
21132128
ELSE:
2114-
if format not in ['NETCDF4','NETCDF4_CLASSIC']:
2115-
msg='parallel mode only works with format=NETCDF4 or NETCDF4_CLASSIC'
2129+
parallel_formats = []
2130+
IF HAS_PARALLEL4_SUPPORT:
2131+
parallel_formats += ['NETCDF4','NETCDF4_CLASSIC']
2132+
IF HAS_PNETCDF_SUPPORT:
2133+
parallel_formats += ['NETCDF3_CLASSIC',
2134+
'NETCDF3_64BIT_OFFSET',
2135+
'NETCDF3_64BIT_DATA',
2136+
'NETCDF3_64BIT']
2137+
if format not in parallel_formats:
2138+
msg='parallel mode only works with the following formats: ' + ' '.join(parallel_formats)
21162139
raise ValueError(msg)
21172140
if comm is not None:
21182141
mpicomm = comm.ob_mpi
@@ -2122,9 +2145,7 @@ strings.
21222145
mpiinfo = info.ob_mpi
21232146
else:
21242147
mpiinfo = MPI_INFO_NULL
2125-
cmode = NC_MPIIO | NC_NETCDF4
2126-
if format == 'NETCDF4_CLASSIC':
2127-
cmode = cmode | NC_CLASSIC_MODEL
2148+
cmode = NC_MPIIO | _cmode_dict[format]
21282149

21292150
self._inmemory = False
21302151
if mode == 'w':
@@ -2144,7 +2165,7 @@ strings.
21442165
else:
21452166
if clobber:
21462167
if parallel:
2147-
IF HAS_NC_PAR:
2168+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
21482169
ierr = nc_create_par(path, NC_CLOBBER | cmode, \
21492170
mpicomm, mpiinfo, &grpid)
21502171
ELSE:
@@ -2159,7 +2180,7 @@ strings.
21592180
ierr = nc_create(path, NC_CLOBBER, &grpid)
21602181
else:
21612182
if parallel:
2162-
IF HAS_NC_PAR:
2183+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
21632184
ierr = nc_create_par(path, NC_NOCLOBBER | cmode, \
21642185
mpicomm, mpiinfo, &grpid)
21652186
ELSE:
@@ -2194,7 +2215,7 @@ strings.
21942215
version 4.4.1 or higher of the netcdf C lib, and rebuild netcdf4-python."""
21952216
raise ValueError(msg)
21962217
elif parallel:
2197-
IF HAS_NC_PAR:
2218+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
21982219
ierr = nc_open_par(path, NC_NOWRITE | NC_MPIIO, \
21992220
mpicomm, mpiinfo, &grpid)
22002221
ELSE:
@@ -2205,7 +2226,7 @@ strings.
22052226
ierr = nc_open(path, NC_NOWRITE, &grpid)
22062227
elif mode == 'r+' or mode == 'a':
22072228
if parallel:
2208-
IF HAS_NC_PAR:
2229+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
22092230
ierr = nc_open_par(path, NC_WRITE | NC_MPIIO, \
22102231
mpicomm, mpiinfo, &grpid)
22112232
ELSE:
@@ -2217,7 +2238,7 @@ strings.
22172238
elif mode == 'as' or mode == 'r+s':
22182239
if parallel:
22192240
# NC_SHARE ignored
2220-
IF HAS_NC_PAR:
2241+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
22212242
ierr = nc_open_par(path, NC_WRITE | NC_MPIIO, \
22222243
mpicomm, mpiinfo, &grpid)
22232244
ELSE:
@@ -2231,7 +2252,7 @@ strings.
22312252
if clobber:
22322253
if parallel:
22332254
# NC_SHARE ignored
2234-
IF HAS_NC_PAR:
2255+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
22352256
ierr = nc_create_par(path, NC_CLOBBER | cmode, \
22362257
mpicomm, mpiinfo, &grpid)
22372258
ELSE:
@@ -2246,7 +2267,7 @@ strings.
22462267
else:
22472268
if parallel:
22482269
# NC_SHARE ignored
2249-
IF HAS_NC_PAR:
2270+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
22502271
ierr = nc_create_par(path, NC_NOCLOBBER | cmode, \
22512272
mpicomm, mpiinfo, &grpid)
22522273
ELSE:
@@ -5345,7 +5366,7 @@ NC_CHAR).
53455366
turn on or off collective parallel IO access. Ignored if file is not
53465367
open for parallel access.
53475368
"""
5348-
IF HAS_NC_PAR:
5369+
IF HAS_PARALLEL4_SUPPORT or HAS_PNETCDF_SUPPORT:
53495370
# set collective MPI IO mode on or off
53505371
if value:
53515372
ierr = nc_var_par_access(self._grpid, self._varid,

0 commit comments

Comments
 (0)