Skip to content

Commit

Permalink
meta
Browse files Browse the repository at this point in the history
  • Loading branch information
scivision committed Jan 13, 2022
1 parent 30294c1 commit a27c829
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 2 deletions.
206 changes: 206 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# h5fortran-MPI API

This document provides a listing of h5fortran-mpi `public` scoped user-facing procedures and methods with a summary of their parameters.

All examples assume:

```fortran
use h5mpi, only: hdf5_file, HSIZE_T, HID_T
type(hdf5_file) :: h
```

Query HDF5 library version:

```fortran
use h5mpi, only : hdf5version
print *, hdf5version()
```

## Open / close HDF5 file reference

More than one HDF5 file can be open in a program, by declaring unique file handle (variable) like:

```fortran
type(hdf5_file) :: h1, h2, h3
```

```fortran
call h%open(filename, action, mpi, comp_lvl)
!! Opens hdf5 file
character(*), intent(in) :: filename
character(*), intent(in), optional :: action !< 'r', 'w', 'rw', 'r+'
logical, intent(in) :: mpi !< .true.: use HDF5-MPI .false.: use serial HDF5
integer, intent(in), optional :: comp_lvl !< 0: no compression. 1-9: ZLIB compression, higher is more compressior
```

```fortran
call h%close(close_hdf5_interface)
!! This must be called on each HDF5 file to flush buffers to disk
!! data loss can occur if program terminates before this procedure
!!
!! close_hdf5_interface is when you know you have exactly one HDF5 file in your
!! application, if true it closes ALL files, even those invoked directly from HDF5.
logical, intent(in), optional :: close_hdf5_interface
```

To avoid memory leaks or corrupted files, always "close()" all hDF5 files before STOPping the Fortran program.

```fortran
call h%flush()
!! request operating system flush data to disk.
```

## Disk variable (dataset) inquiry

To allocate variables before reading data, inquire about dataset characteristics with these procedures.

```fortran
rank = h%ndim(dataset_name)
character(*), intent(in) :: dataset_name
```

Get disk dataset shape (1D vector)

```fortran
call h%shape(dataset_name, dims)
character(*), intent(in) :: dataset_name
integer(HSIZE_T), intent(out), allocatable :: dims(:)
```

Dataset "dname" data class (i.e. integer, float, string, ...)

```fortran
integer :: class
!! H5T_INTEGER_F, H5T_FLOAT_F, H5T_STRING_F
class = h%class(dname)
character(*), intent(in) :: dname
```

Dataset "dname" datatype

```fortran
integer(HID_T) :: dtype
!! H5T_NATIVE_REAL, H5T_NATIVE_DOUBLE, H5T_NATIVE_INTEGER, H5T_NATIVE_CHARACTER, H5T_STD_I64LE
dtype = h%dtype(dname)
character(*), intent(in) :: dname
```

Does dataset "dname" exist in this HDF5 file?

```fortran
exists = h%exist(dname)
character(*), intent(in) :: dname
```

Is dataset "dname" contiguous on disk?

```fortran
tf = h%is_contig(dname)
!! is dataset contiguous
character(*), intent(in) :: dname
```

Is dataset compact (< 64K)

```fortran
tf = h%is_compact(dname)
!! is dataset compact layout
character(*), intent(in) :: dname
```

Is dataset chunked?

```fortran
tf = h%is_chunked(dname)
!! is dataset chunked
character(*), intent(in) :: dname
```

Is this an HDF5 file?

```fortran
use h5mpi, only: is_hdf5
tf = is_hdf5('myfile.txt') !< probably false
tf = is_hdf5('myfile.h5') !< true if a valid HDF5 file
```

These are more advanced inquiries into the memory layout of the dataset, for advanced users:

```fortran
Layout = h%layout(dname)
!! integer :: H5D_CONTIGUOUS_F, H5D_CHUNKED_F, H5D_VIRTUAL_F, H5D_COMPACT_F
character(*), intent(in) :: dname
```

```fortran
call h%chunks(dname, chunk_size)
character(*), intent(in) :: dname
integer, intent(out) :: chunk_size(:)
```

## create dataset softlink

HDF5 can create dataset softlinks within an HDF5 file:

```fortran
call h%softlink(tgt, link)
character(*), intent(in) :: tgt, & !< target path to link dataset
link !< soft link path to create
```

## file write operations

Write data from memory to disk HDF5 dataset:
When file has been opened for MPI collective read via: `%open(..., mpi=.true.)` the data is distributed
via MPI to the workers.
If overall dataset dimensions "dset_dims" is present, data is collectively gathered from the workers as per HDF5-MPI docs.
Otherwise, h5fortran-mpi assumes that root has all the data to be written and ignores the workers.

```fortran
call h%write(dname, value, dset_dims, istart, iend, chunk_size, compact)
!! write 0d..7d dataset
character(*), intent(in) :: dname
class(*), intent(in) :: value(..) !< array to write
integer, intent(in), dimension(rank(value)), optional :: dset_dims
integer, intent(in), optional, dimension(rank(value)) :: istart, iend !< array slicing for hyperslab
integer, intent(in), optional :: chunk_size(rank(value)) !< override auto-chunking
logical, intent(in), optional :: compact !< faster I/O for sub-64 kB datasets
```

Write dataset attribute (e.g. units or instrument):

```fortran
call h%writeattr(dname, attr, attrval)
character(*), intent(in) :: dname, attr !< dataset name, attribute name
class(*), intent(in) :: attrval(:) !< character, real, integer
```

## file read operations

Read data from disk to memory:
When file has been opened for MPI collective read via: `%open(..., mpi=.true.)` the data is distributed
via MPI to the workers.
For example, if no slicing is specified, the whole dataset is read by root and broadcast to the workers.
If slicing is specified, the data is read and distributed among the workers as per HDF5-MPI docs.

```fortran
call h%read(dname, value, istart, iend)
character(*), intent(in) :: dname
class(*), intent(inout) :: value(..) !< read array to this ALLOCATED variable of rank 0d..7d
integer, intent(in), optional, dimension(rank(value)) :: istart, iend !< array slicing
```

Read dataset attribute into memory:

```fortran
call h%readattr(dname, attr, attrval)
character(*), intent(in) :: dname, attr !< dataset name, attribute name
class(*), intent(inout) :: attrval(:) !< character scalar; real vector, integer vector
```
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ authors:
given-names: Michael
orcid: https://orcid.org/0000-0002-1637-6526
title: h5fortran-mpi
doi:
doi: 10.5281/zenodo.5847354
date-released: 2022-01-14
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# h5fortran-mpi

[![DOI](https://zenodo.org/badge/377901005.svg)](https://zenodo.org/badge/latestdoi/377901005)

[![ci](https://github.com/geospace-code/h5fortran-mpi/actions/workflows/ci.yml/badge.svg)](https://github.com/geospace-code/h5fortran-mpi/actions/workflows/ci.yml)
[![ci_macos](https://github.com/geospace-code/h5fortran-mpi/actions/workflows/ci_macos.yml/badge.svg)](https://github.com/geospace-code/h5fortran-mpi/actions/workflows/ci_macos.yml)
[![intel-oneapi](https://github.com/geospace-code/h5fortran-mpi/actions/workflows/intel-oneapi.yml/badge.svg)](https://github.com/geospace-code/h5fortran-mpi/actions/workflows/intel-oneapi.yml)
Expand Down
2 changes: 1 addition & 1 deletion codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"issueTracker": "https://github.com/geospace-code/h5fortran-mpi/issues",
"name": "h5fortran-mpi",
"version": "1.0.0",
"identifier": "",
"identifier": "10.5281/zenodo.5847354",
"description": "Lightweight object-oriented HDF5-MPI parallel Fortran interface",
"applicationCategory": "file I/O",
"developmentStatus": "active",
Expand Down
79 changes: 79 additions & 0 deletions scripts/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
cmake_minimum_required(VERSION 3.20...3.22)
project(HDF5_build
LANGUAGES C Fortran
)

option(hdf5_parallel "build HDF5 parallel MPI" on)

if(NOT HDF5_VERSION)
set(HDF5_VERSION 1.12.1) # default version to build
endif()

# --- system checks
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
message(FATAL_ERROR "please specify where to install HDF5 under, like
cmake -B build -DCMAKE_INSTALL_PREFIX=~/mylibs")
endif()

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules/)


if(NOT MPI_ROOT AND DEFINED ENV{MPI_ROOT})
set(MPI_ROOT $ENV{MPI_ROOT})
endif()

if(CMAKE_SYSTEM_NAME STREQUAL Linux AND MPI_ROOT)
set(ld_path $ENV{LD_LIBRARY_PATH})
cmake_path(CONVERT "${ld_path}" TO_CMAKE_PATH_LIST ld_path NORMALIZE)
cmake_path(CONVERT "${MPI_ROOT}" TO_CMAKE_PATH_LIST MPI_ROOT NORMALIZE)

if(NOT "${ld_path}" MATCHES "${MPI_ROOT}/lib")
message(WARNING "${MPI_ROOT}/lib not found in LD_LIBRARY_PATH: $ENV{LD_LIBRARY_PATH}
HDF5 build may fail due to bugs in HDF5 package CMake scripts.
Fix this by adding to ~/.bashrc or similar:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${MPI_ROOT}/lib")
endif()
endif()

# HDF5 install fails to work (link) if prior HDF5 library is installed there
find_library(_hdf5_libprior NAMES hdf5 PATHS ${CMAKE_INSTALL_PREFIX} PATH_SUFFIXES lib NO_DEFAULT_PATH NO_CACHE)
find_path(_hdf5_incprior NAMES hdf5.h PATHS ${CMAKE_INSTALL_PREFIX} PATH_SUFFIXES include NO_DEFAULT_PATH NO_CACHE)
find_program(_hdf5_binprior NAMES h5cc PATHS ${CMAKE_INSTALL_PREFIX} PATH_SUFFIXES bin NO_DEFAULT_PATH NO_CACHE)
if(_hdf5_binprior)
cmake_path(GET _hdf5_binprior PARENT_PATH _hdf5_binprior)
else()
set(_hdf5_binprior "")
endif()
if(_hdf5_libprior)
cmake_path(GET _hdf5_libprior PARENT_PATH _hdf5_libprior)
endif()
if(_hdf5_libprior OR _hdf5_incprior OR _hdf5_binprior)
message(FATAL_ERROR "HDF5 library already installed:
${_hdf5_libprior}
${_hdf5_incprior}
${_hdf5_binprior}
Please pick a new install location or completely remove the old HDF5 install directory.
Otherwise, HDF5 will fail to link correctly with prior version and this version mixed.")
endif()

# --- commence HDF5 build/install
include(${PROJECT_SOURCE_DIR}/../cmake/libraries.cmake)

set_directory_properties(PROPERTIES EP_UPDATE_DISCONNECTED true)

message(STATUS "Build / install HDF5 ${HDF5_VERSION} to ${CMAKE_INSTALL_PREFIX}")

if(hdf5_parallel)
find_package(MPI COMPONENTS C REQUIRED)
include(${PROJECT_SOURCE_DIR}/../cmake/check_mpi.cmake)
check_mpi_version()
endif()

include(${PROJECT_SOURCE_DIR}/../cmake/hdf5.cmake)

# --- features
include(FeatureSummary)

add_feature_info(HDF5parallel hdf5_parallel "HDF5 MPI layer")

feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

0 comments on commit a27c829

Please sign in to comment.