From c9b88f65566d41430ec337958469ac7b37608012 Mon Sep 17 00:00:00 2001 From: Jean-Matthieu Etancelin <jean-matthieu.etancelin@univ-pau.fr> Date: Thu, 28 Feb 2019 17:10:42 +0100 Subject: [PATCH] Re-enable HDF5 parallel interface. (reverting commit 9c195f33). It works only in a few specific cases (1d topology + cuting direction in the last varying direction) --- CMakeLists.txt | 14 ++--- ci/docker_images/ubuntu/bionic/Dockerfile | 2 +- hysop/__init__.py.in | 1 - hysop/operator/hdf_io.py | 68 +++++------------------ hysop/tools/io_utils.py | 40 ++++--------- setup.py.in | 7 ++- 6 files changed, 35 insertions(+), 97 deletions(-) mode change 100644 => 100755 setup.py.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f00922a9..8982adb15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,6 @@ option(WITH_TESTS "Enable testing. Default = OFF" ON) option(BUILD_SHARED_LIBS "Enable dynamic library build, default = ON." ON) option(USE_CXX "Expand hysop with some new functions from a generated c++ to python interface, wrapped into hysop.cpp2hysop module. Default = ON." OFF) option(WITH_SCALES "compile/create scales lib and link it with HySoP. Default = ON." ON) -option(WITH_PARALLEL_HDF5 "Enable parallel hdf5 interface. Default = OFf." OFF) option(WITH_FFTW "Link with fftw library (required for some HySoP solvers), default = ON" ON) option(WITH_EXTRAS "Link with some extra fortran libraries (like arnoldi solver), default = OFF" OFF) option(WITH_GPU "Use of GPU (required for some HySoP solvers), default = ON" ON) @@ -232,13 +231,11 @@ if(WITH_EXTRAS) endif() # ========= Check parallel hdf5 availability ========= -if(WITH_PARALLEL_HDF5) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "import h5py; print(h5py.get_config().mpi)" - OUTPUT_VARIABLE H5PY_PARALLEL_ENABLED) - if(H5PY_PARALLEL_ENABLED EQUAL "True") - message(FATAL_ERROR "h5py is not build with parallel support.") - endif() +execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "import h5py; print(h5py.get_config().mpi)" + OUTPUT_VARIABLE H5PY_PARALLEL_ENABLED) +if(H5PY_PARALLEL_ENABLED EQUAL "True") + message(FATAL_ERROR "h5py is not build with parallel support.") endif() # ========= Check which opencl devices are available on the system ========= @@ -641,7 +638,6 @@ if(VERBOSE_MODE) message(STATUS " Project uses Scales : ${WITH_SCALES}") message(STATUS " Project uses FFTW : ${WITH_FFTW}") message(STATUS " Project uses GPU : ${WITH_GPU}") - message(STATUS " Project uses parallel hdf5 interface : ${WITH_PARALLEL_HDF5}") message(STATUS " ${PROJECT_NAME} profile mode : ${PROFILE}") message(STATUS " ${PROJECT_NAME} debug mode : ${DEBUG}") message(STATUS " Enable -OO run? : ${OPTIM}") diff --git a/ci/docker_images/ubuntu/bionic/Dockerfile b/ci/docker_images/ubuntu/bionic/Dockerfile index de940fd5f..872147e97 100644 --- a/ci/docker_images/ubuntu/bionic/Dockerfile +++ b/ci/docker_images/ubuntu/bionic/Dockerfile @@ -89,7 +89,7 @@ RUN pip install --upgrade numba # RUN cd /tmp && git clone https://github.com/sphinx-contrib/doxylink.git && cd doxylink/sphinxcontrib/doxylink \ # && mv doxylink.py doxylink.py3 && strip-hints doxylink.py3 > doxylink.py && rm doxylink.py3 \ # && mv parsing.py parsing.py3 && strip-hints parsing.py3 > parsing.py && rm parsing.py3 \ -# && python setup.py install +# && cd ../.. && python setup.py install # scitools (python-scitools does not exist on ubuntu:bionic) diff --git a/hysop/__init__.py.in b/hysop/__init__.py.in index fffc7a197..9a74933c9 100644 --- a/hysop/__init__.py.in +++ b/hysop/__init__.py.in @@ -33,7 +33,6 @@ __GPU_ENABLED__ = "@WITH_GPU@" is "ON" __FFTW_ENABLED__ = "@WITH_FFTW@" is "ON" __SCALES_ENABLED__ = "@WITH_SCALES@" is "ON" __OPTIMIZE__ = not __debug__ -__PARALLEL_HDF5__ = "@WITH_PARALLEL_HDF5@" is "ON" __VERBOSE__ = get_env('VERBOSE', ("@VERBOSE@" is "ON")) __DEBUG__ = get_env('DEBUG', ("@DEBUG@" is "ON")) diff --git a/hysop/operator/hdf_io.py b/hysop/operator/hdf_io.py index 7570d4123..2984ccb2d 100755 --- a/hysop/operator/hdf_io.py +++ b/hysop/operator/hdf_io.py @@ -1,5 +1,3 @@ -# coding: utf-8 - """I/O operators .. currentmodule:: hysop.operator.hdf_io @@ -11,7 +9,6 @@ """ import functools from abc import ABCMeta, abstractmethod -from hysop import __PARALLEL_HDF5__ from hysop.deps import h5py, sys from hysop.core.graph.graph import discretized from hysop.constants import DirectionLabels, HYSOP_REAL, Backend, TranspositionState @@ -116,8 +113,6 @@ class HDF_IO(ComputationalGraphOperator): self.topology = None self._local_compute_slices = None self._global_grid_resolution = None - self._local_grid_resolution = None - self._all_local_grid_resolution = None self._global_slices = None # Dictionnary of discrete fields. Key = name in hdf file, # Value = discrete field @@ -188,10 +183,6 @@ class HDF_IO(ComputationalGraphOperator): # Global resolution for hdf5 output self._global_grid_resolution = refmesh.grid_resolution - # Local resolution for hdf5 output - self._local_grid_resolution = refmesh.local_resolution - self._all_local_grid_resolution = self.topology.cart_comm.gather( - self._local_grid_resolution, root=self.io_params.io_leader) local_compute_slices = {} global_compute_slices = {} @@ -245,13 +236,9 @@ class HDF_IO(ComputationalGraphOperator): self._hdf_file = h5py.File(filename, mode) compression = 'gzip' else: - if __PARALLEL_HDF5__: - self._hdf_file = h5py.File(filename, mode, driver='mpio', - comm=self.topology.comm) - compression = None - else: - self._hdf_file = h5py.File(filename.format(rk=self.topology.cart_rank), mode) - compression = 'gzip' + self._hdf_file = h5py.File(filename, mode, driver='mpio', + comm=self.topology.comm) + compression = None return compression @classmethod @@ -369,11 +356,7 @@ class HDF_Writer(HDF_IO): """Set output file name for current iteration""" msg = 'count < 0, simu must be initialized.' assert i >= 0, msg - if self.topology.cart_size == 1 or __PARALLEL_HDF5__: - return self.io_params.filename + "_{0:05d}".format(i) + '.h5' - else: - return self.io_params.filename + "_{0:05d}".format(i) + "_rk{rk:03d}.h5" - + return self.io_params.filename + "_{0:05d}".format(i) + '.h5' @op_apply def apply(self, simulation=None, **kwds): @@ -406,12 +389,9 @@ class HDF_Writer(HDF_IO): write_step = tuple(step) ds_names = self.dataset.keys() - joinrkfiles = None - if self.topology.cart_size > 1 and not __PARALLEL_HDF5__: - joinrkfiles = range(self.topology.cart_size) grid_attributes = XMF.prepare_grid_attributes( ds_names, - resolution, origin, step, joinrkfiles=joinrkfiles) + resolution, origin, step) self.grid_attributes_template = grid_attributes @@ -437,13 +417,9 @@ class HDF_Writer(HDF_IO): assert (f is not None) assert (lastp is not None) for (i, t) in self._xdmf_data_files: - if self.topology.cart_size == 1 or __PARALLEL_HDF5__: - filenames = {'filename': self._get_filename(i).split('/')[-1]} - else: - filenames = dict(('filename'+str(r), self._get_filename(i).format(rk=r).split('/')[-1]) for r in range(self.topology.cart_size)) - filenames.update(('resolution'+str(r), XMF._list_format(self._all_local_grid_resolution[r])) for r in range(self.topology.cart_size)) + filename = self._get_filename(i).split('/')[-1] grid_attrs = self.grid_attributes_template.format( - niteration=i, time=t, **filenames) + niteration=i, time=t, filename=filename) f.seek(lastp) f.write(grid_attrs) self._last_xmf_pos = f.tell() @@ -454,13 +430,7 @@ class HDF_Writer(HDF_IO): self._xdmf_data_files = [] def _step_HDF5(self, simu): - """Write an h5 file with data on each mpi process. - - If parallel interface of HDF5 is not enabled, each rank is - writing its own h5 file. All files are concatenated in the xmf - part with a 'JOIN' function. If parallel interface enabled, - only one h5 file is written by all ranks. - """ + """Write an h5 file with data on each mpi process.""" # Remarks: # - force np.float64, ParaView seems unable to read float32 # - writing compressed hdf5 files (gzip compression seems the best) @@ -474,21 +444,13 @@ class HDF_Writer(HDF_IO): # Get the names of output input_fields and create the corresponding # datasets - if self.topology.cart_size == 1 or __PARALLEL_HDF5__: - for name in self.dataset: - ds = self._hdf_file.create_dataset(name, - self._global_grid_resolution, - dtype=npw.float64, - compression=compression) - # In parallel, each proc must write at the right place of the dataset - ds[self._global_compute_slices[name]] = self._data_getters[name]() - else: - for name in self.dataset: - ds = self._hdf_file.create_dataset(name, - self._local_grid_resolution, - dtype=npw.float64, - compression=compression) - ds[...] = self._data_getters[name]() + for name in self.dataset: + ds = self._hdf_file.create_dataset(name, + self._global_grid_resolution, + dtype=npw.float64, + compression=compression) + # In parallel, each proc must write at the right place of the dataset + ds[self._global_compute_slices[name]] = self._data_getters[name]() # Collect datas required to write the xdmf file # --> add tuples (counter, time). diff --git a/hysop/tools/io_utils.py b/hysop/tools/io_utils.py index b66ec46c9..3de3fc9a4 100755 --- a/hysop/tools/io_utils.py +++ b/hysop/tools/io_utils.py @@ -234,7 +234,7 @@ class IOParams(namedtuple("IOParams", ['filename', 'filepath', IO.check_dir(filename) return super(IOParams, cls).__new__(cls, filename, filepath, - frequency, fileformat, + frequency, fileformat, io_leader, visu_leader, kwds) @@ -387,7 +387,7 @@ class XMF(object): @staticmethod def prepare_grid_attributes(dataset_names, - resolution, origin, step, joinrkfiles=None): + resolution, origin, step): """ Prepare XDMF header as a string. @@ -398,7 +398,6 @@ class XMF(object): resolution: 3d tuple origin: 3d tuple step: 3d tuple - joinrkfiles : (optional) Returns: -------- @@ -406,10 +405,7 @@ class XMF(object): the xml-like header formattable with the following keywords: niteration : iteration number time: time in seconds - filename: target file name, in sequential or with parallel hdf5 support - filename0, ... filenameN : target file names for each rank 0 to N, in parallel without HDF5 parallel support - resolution0, ... resolutionN : local resolutions for each rank 0 to N, in parallel without HDF5 parallel support - + filename: target file name """ # The header (xml-like), saved in a string. # always use a 3D mesh because paraview does not like 2D meshes (uses axe (Y,Z) instead of (X,Y)). @@ -437,29 +433,13 @@ class XMF(object): xml_grid += " <Attribute Name=\"" xml_grid += name + "\"" xml_grid += " AttributeType=\"Scalar\" Center=\"Node\">\n" - if joinrkfiles is None: - xml_grid += " <DataItem Dimensions=\"" - xml_grid += XMF._list_format(resolution) + " \"" - xml_grid += " NumberType=\"Float\" Precision=\"8\" Format=\"HDF\"" - xml_grid += " Compression=\"Raw\">\n" # - xml_grid += " {filename}" - xml_grid += ":/" + name - xml_grid += "\n </DataItem>\n" - else: - xml_grid += " <DataItem Dimensions=\"" - xml_grid += XMF._list_format(resolution) + " \"" - xml_grid += " ItemType=\"Function\" Function=\"JOIN(" - xml_grid += " ; ".join("$"+str(i) for i in joinrkfiles) - xml_grid += ")\">\n" - for i in joinrkfiles: - xml_grid += " <DataItem Dimensions=\"" - xml_grid += "{resolution"+str(i)+"}" + " \"" - xml_grid += " NumberType=\"Float\" Precision=\"8\" Format=\"HDF\"" - xml_grid += " Compression=\"Raw\">\n" # - xml_grid += " {filename"+str(i)+"}" - xml_grid += ":/" + name - xml_grid += "\n </DataItem>\n" - xml_grid += " </DataItem>\n" + xml_grid += " <DataItem Dimensions=\"" + xml_grid += XMF._list_format(resolution) + " \"" + xml_grid += " NumberType=\"Float\" Precision=\"8\" Format=\"HDF\"" + xml_grid += " Compression=\"Raw\">\n" # + xml_grid += " {filename}" + xml_grid += ":/" + name + xml_grid += "\n </DataItem>\n" xml_grid += " </Attribute>\n" xml_grid += " </Grid>\n" return xml_grid diff --git a/setup.py.in b/setup.py.in old mode 100644 new mode 100755 index 9b1630a43..370ff9bda --- a/setup.py.in +++ b/setup.py.in @@ -57,7 +57,7 @@ def parseCMakeDefines(var): defines = parseCMakeVar(var) if len(defines)==0: return None - + # regex to match compiler defines like -DMACRO_NAME or # -DMACRO_NAME = MACRO_VALUE p = re.compile('\s*(?:-D)?\s*(\w+)(?:\s*=\s*(\w+))?\s*') @@ -126,7 +126,7 @@ def create_fortran_extension(name, pyf_file=None, src_dirs=None, sources=None, for sdir in src_dirs: sources += glob.glob(os.path.join(sdir, '*.f95')) sources += glob.glob(os.path.join(sdir, '*.f90')) - + # Reorder source list with fortran modules # dependencies. It seems that this is not taken into # account in f2py or distutils. @@ -147,6 +147,7 @@ def create_fortran_extension(name, pyf_file=None, src_dirs=None, sources=None, # --- set compilation flags --- fortran_flags = ['@Fortran_FLAGS@'] + fortran_flags = list(set([_ for _ in fortran_flags[0].split(' ') if _ != ''])) # we trust cmake for external libraries and # add them to linker, without using libraries option @@ -308,7 +309,7 @@ if enable_fortran is "ON": num_dirs = [] for sd in subdirs: num_dirs.append(os.path.join(fortran_root, sd)) - + hysop_libdir = [ld.strip() for ld in hysop_libdir] # hysop_libdir = ' '.join([ '-L{}'.format(ld) if ld[1]!='L' else hysop_libdir]) num_dirs.append('@GENERATED_FORTRAN_FILES_DIR@') -- GitLab