diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8d61025ec0377f930f400895d15feecdeea84231
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,37 @@
+defaults: &defaults
+  working_directory: /home/adios2
+  steps:
+    - checkout:
+        path: /home/adios2/source
+    - run:
+        name: Update
+        command: bash source/scripts/circle/runOnCircle.sh update
+    - run:
+        name: Configure
+        command: bash source/scripts/circle/runOnCircle.sh configure
+    - run:
+        name: Build
+        command: bash source/scripts/circle/runOnCircle.sh build
+    - run:
+        name: Test
+        command: bash source/scripts/circle/runOnCircle.sh test
+
+version: 2
+
+jobs:
+  "el7-gcc48":
+    <<: *defaults
+    docker:
+      - image: ornladios/adios2:el7-ohpc
+
+  "el7-gcc7-openmpi":
+    <<: *defaults
+    docker:
+      - image: ornladios/adios2:el7-ohpc
+
+workflows:
+  version: 2
+  build:
+    jobs:
+      - "el7-gcc48"
+      - "el7-gcc7-openmpi"
diff --git a/.gitignore b/.gitignore
index 04e38af8eabeb74636565371d2f097b1143b8827..e8f3faf4a79b32344df3ad93783d6da29a36cd74 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,5 @@
 *.bp.dir
 build/
 
+# Mac OSX finder-related files
+.DS_Store
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e31f749bf9acf63d09836b41d882dec7323219db..cdafaba1843dbc5b579a49fd179fde211400f17f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@
 # accompanying file Copyright.txt for details.
 #------------------------------------------------------------------------------#
 
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.6)
 
 # Fail immediately if not using an out-of-source build
 if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
@@ -45,7 +45,7 @@ set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
 include(ADIOSFunctions)
 
 # Default to a debug build if not specified
-if(NOT CMAKE_BUILD_TYPE)
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
   set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build." FORCE)
 endif()
 
@@ -59,18 +59,10 @@ include(CMakeDependentOption)
 # to that.  Otherwise base the default on whether or not shared libs are even
 # supported.
 get_property(SHARED_LIBS_SUPPORTED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS)
-if(DEFINED ADIOS2_BUILD_SHARED_LIBS)
-  set(ADIOS2_BUILD_SHARED_LIBS_DEFAULT ${ADIOS2_BUILD_SHARED_LIBS})
-elseif(DEFINED BUILD_SHARED_LIBS)
-  set(ADIOS2_BUILD_SHARED_LIBS_DEFAULT ${BUILD_SHARED_LIBS})
-else()
-  set(ADIOS2_BUILD_SHARED_LIBS_DEFAULT ${SHARED_LIBS_SUPPORTED})
-endif()
-cmake_dependent_option(ADIOS2_BUILD_SHARED_LIBS
-  "Build shared libraries (so/dylib/dll)." ${ADIOS2_BUILD_SHARED_LIBS_DEFAULT}
+cmake_dependent_option(BUILD_SHARED_LIBS
+  "Build shared libraries (so/dylib/dll)." ${SHARED_LIBS_SUPPORTED}
   "SHARED_LIBS_SUPPORTED" OFF
 )
-set(BUILD_SHARED_LIBS ${ADIOS2_BUILD_SHARED_LIBS})
 mark_as_advanced(BUILD_SHARED_LIBS)
 
 # Setup PIC defaults.  If explicitly specified somehow, then default 
@@ -98,15 +90,18 @@ adios_option(ZeroMQ    "Enable support for ZeroMQ" AUTO)
 adios_option(HDF5      "Enable support for the HDF5 engine" AUTO)
 adios_option(ADIOS1    "Enable support for the ADIOS 1.x engine" AUTO)
 adios_option(Python    "Enable support for Python bindings" AUTO)
+adios_option(Fortran   "Enable support for Fortran bindings" OFF)
 adios_option(SysVShMem "Enable support for SysV Shared Memory IPC on *NIX" AUTO)
 include(${ADIOS2_SOURCE_DIR}/cmake/DetectOptions.cmake)
 
 if(ADIOS2_HAVE_MPI)
-  # Workaround for OpenMPI forcing the link of C++ bindings
-  add_definitions(-DOMPI_SKIP_MPICXX)
+  # Workaround for MPI forcing the link of C++ bindings
+  add_definitions(-DOMPI_SKIP_MPICXX -DMPICH_SKIP_MPICXX)
 endif()
 
-GenerateADIOSHeaderConfig(MPI ZFP BZip2 ADIOS1 HDF5 ZeroMQ DataMan Python SysVShMem)
+GenerateADIOSHeaderConfig(MPI ZFP BZip2 ADIOS1 HDF5 DataMan Python Fortran 
+                          SysVShMem)
+
 install(FILES ${ADIOS2_BINARY_DIR}/source/adios2/ADIOSConfig.h
   DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/adios2
 )
@@ -146,7 +141,9 @@ else()
   set(ADIOS2_BUILD_TESTING_DEFAULT ON)
 endif()
 unset(BUILD_TESTING)
-option(ADIOS2_BUILD_TESTING "Build the ADIOS2 testing tree" ${ADIOS2_BUILD_TESTING_DEFAULT})
+option(ADIOS2_BUILD_TESTING "Build the ADIOS2 testing tree" 
+       ${ADIOS2_BUILD_TESTING_DEFAULT})
+       
 include(CTest)
 set(BUILD_TESTING ${ADIOS2_BUILD_TESTING})
 if(BUILD_TESTING)
@@ -165,9 +162,18 @@ GenerateADIOSPackageConfig()
 message("")
 message("ADIOS2 build configuration:")
 message("  ADIOS Version: ${ADIOS2_VERSION}")
-message("  C++ Compiler : ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} ${CMAKE_CXX_COMPILER_WRAPPER}")
+message("  C++ Compiler : ${CMAKE_CXX_COMPILER_ID} " 
+                         "${CMAKE_CXX_COMPILER_VERSION} " 
+                         "${CMAKE_CXX_COMPILER_WRAPPER}")
 message("    ${CMAKE_CXX_COMPILER}")
 message("")
+if(ADIOS2_HAVE_Fortran)
+  message("  Fortran Compiler : ${CMAKE_Fortran_COMPILER_ID} "
+                               "${CMAKE_Fortran_COMPILER_VERSION} "
+                               "${CMAKE_Fortran_COMPILER_WRAPPER}")
+  message("    ${CMAKE_Fortran_COMPILER}")
+  message("")
+endif()
 message("  Installation prefix: ${CMAKE_INSTALL_PREFIX}")
 message("  Features:")
 if(BUILD_SHARED_LIBS)
@@ -181,7 +187,8 @@ message("    Library Type: ${msg_lib_type}")
 message("    Build Type:   ${CMAKE_BUILD_TYPE}")
 message("    Testing: ${BUILD_TESTING}")
 message("    Build Options:")
-foreach(opt BZip2 ZFP MPI DataMan ZeroMQ HDF5 ADIOS1 Python SysVShMem)
+
+foreach(opt BZip2 ZFP MPI DataMan ZeroMQ HDF5 ADIOS1 Python Fortran SysVShMem)
   message_pad("      ${opt}" 15 label)
   if(${ADIOS2_HAVE_${opt}})
     message("${label}: ON")
diff --git a/ReadMe.md b/ReadMe.md
index 5781eadd310df4f8f6353f80db73d892b61fdf46..1f8e52fbdf3c82e261c079d85092c382fdc84964 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -1,3 +1,11 @@
+[![GitHub (pre-)release](https://img.shields.io/github/release/ornladios/adios2/all.svg)]()
+[![GitHub license](http://dmlc.github.io/img/apache2.svg)](./LICENSE)
+
+
+[![Circle CI](https://circleci.com/gh/ornladios/ADIOS2.svg?style=shield)](https://circleci.com/gh/ornladios/ADIOS2)
+[![Travis CI](https://travis-ci.org/ornladios/ADIOS2.svg)](https://travis-ci.org/ornladios/ADIOS2)
+[![AppVeyor CI](https://ci.appveyor.com/api/projects/status/0s2a3qp57hgbvlhj?svg=true)](https://ci.appveyor.com/project/ornladios/adios2)
+
 # Adaptable Input / Output System (ADIOS) v2.0
 This is v2.0 of the ADIOS I/O system, developed as part of the
 U.S. Department of Energy Exascale Computing Program.
@@ -72,6 +80,7 @@ ADIOS2 build configuration:
       HDF5     : ON
       ADIOS1   : OFF
       Python   : ON
+      C        : ON
       SysVShMem: ON
 
 -- Configuring done
@@ -92,6 +101,7 @@ The following options can be specified with CMake's `-DVAR=VALUE` syntax to cont
 | `ADIOS2_USE_HDF5`    | **`AUTO`**/``ON``/``OFF`` | Enable the [HDF5](https://www.hdfgroup.org) engine.                              |
 | `ADIOS2_USE_ADIOS1`  | **`AUTO`**/``ON``/``OFF`` | Enable the [ADIOS 1.x](https://www.olcf.ornl.gov/center-projects/adios/) engine. |
 | `ADIOS2_USE_Python`  | **`AUTO`**/``ON``/``OFF`` | Enable the Python >= 2.7 bindings. |
+| `ADIOS2_USE_C`       | **`AUTO`**/``ON``/``OFF`` | Enable the C bindings library libadios2_c.so or libadios2_c.a |
 
 Note: The `ADIOS2_USE_HDF5` and `ADIOS2_USE_ADIOS1` options require the use of a matching serial or parallel version depending on whether `ADIOS2_USE_MPI` is enabled.  SImilary, enabling MPI and Python bindings requires the presence of `mpi4py`.
 
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d0e4f72b4ca1a344af41f9d535fc7fc38a812ff5
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,48 @@
+version: 1.0.{build}
+
+skip_branch_with_pr: true
+
+os:
+  - Visual Studio 2015
+  - Visual Studio 2017
+
+platform:
+  - x86
+
+# install:
+#   ### Having trouble with enabling python on windows
+#   - set PATH=C:\Python27-x64;C:\Python27-x64\Scripts;%PATH%
+#   - "python.exe -m pip install numpy"
+
+# init:
+#  - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
+
+before_build:
+- del /q /f "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets"
+- cd C:\projects\adios2
+- git reset --hard %APPVEYOR_PULL_REQUEST_HEAD_COMMIT%
+- ps: |
+    $env:CUSTOM_BUILD_NAME = ("{0}_{1}_vs{2}" -f $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH, $env:APPVEYOR_BUILD_NUMBER, $env:APPVEYOR_BUILD_WORKER_IMAGE.split()[2] )
+- ctest.exe -VV -S ../adios2/scripts\appveyor\av_default.cmake
+    -Ddashboard_full=OFF
+    -Ddashboard_do_update=true
+    -DCTEST_BUILD_NAME=%CUSTOM_BUILD_NAME%
+
+build_script:
+- ctest.exe -VV -S ../adios2/scripts\appveyor\av_default.cmake
+    -Ddashboard_full=OFF
+    -Ddashboard_do_configure=true
+    -DCTEST_BUILD_NAME=%CUSTOM_BUILD_NAME%
+- ctest.exe -VV -S ../adios2/scripts\appveyor\av_default.cmake
+    -Ddashboard_full=OFF
+    -Ddashboard_do_build=true
+    -DCTEST_BUILD_NAME=%CUSTOM_BUILD_NAME%
+
+test_script:
+- ctest.exe -VV -S ../adios2/scripts\appveyor\av_default.cmake
+    -Ddashboard_full=OFF
+    -Ddashboard_do_test=true
+    -DCTEST_BUILD_NAME=%CUSTOM_BUILD_NAME%
+
+# on_finish:
+#  - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
diff --git a/bindings/C/CMakeLists.txt b/bindings/C/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7ba156809ce432785faa84567fa3b646f1c399fe
--- /dev/null
+++ b/bindings/C/CMakeLists.txt
@@ -0,0 +1,20 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+
+
+target_sources(adios2 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/adios2_c.cpp)
+target_include_directories(adios2
+  PUBLIC
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+)
+
+install(
+  FILES adios2_c.h 
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+install(
+  FILES adios2/adios2_c_enums.h
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/adios2
+)
diff --git a/bindings/C/adios2/adios2_c_enums.h b/bindings/C/adios2/adios2_c_enums.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ea471ee62f4013d8ef8702c6d44083d8737a2c2
--- /dev/null
+++ b/bindings/C/adios2/adios2_c_enums.h
@@ -0,0 +1,74 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * adios2_c_enums.h
+ *
+ *  Created on: Aug 7, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#ifndef ADIOS2_BINDINGS_C_ADIOS2_C_ENUMS_H_
+#define ADIOS2_BINDINGS_C_ADIOS2_C_ENUMS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    adios2_debug_mode_on = 0,
+    adios2_debug_mode_off = 1,
+} adios2_debug_mode;
+
+typedef enum {
+    adios2_constant_dims_true = 0,
+    adios2_constant_dims_false = 1,
+} adios2_constant_dims;
+
+typedef enum {
+
+    adios2_type_char = 0,
+    adios2_type_int = 1,
+    adios2_type_float = 2,
+    adios2_type_double = 3,
+    adios2_type_float_complex = 4,
+    adios2_type_double_complex = 5,
+
+    adios2_type_int8_t = 6,
+    adios2_type_int16_t = 7,
+    adios2_type_int32_t = 8,
+    adios2_type_int64_t = 9,
+
+    adios2_type_string,
+    adios2_type_signed_char,
+
+    adios2_type_short,
+
+    adios2_type_long_int,
+    adios2_type_long_long_int,
+
+    adios2_type_unsigned_char,
+    adios2_type_unsigned_short,
+    adios2_type_unsigned_int,
+    adios2_type_unsigned_long_int,
+    adios2_type_unsigned_long_long_int,
+
+    adios2_type_uint8_t,
+    adios2_type_uint16_t,
+    adios2_type_uint32_t,
+    adios2_type_uint64_t
+} adios2_type;
+
+typedef enum {
+    adios2_open_mode_undefined,
+    adios2_open_mode_write,
+    adios2_open_mode_read,
+    adios2_open_mode_append,
+    adios2_open_mode_read_write
+} adios2_open_mode;
+
+#ifdef __cplusplus
+} // end extern C
+#endif
+
+#endif /* ADIOS2_BINDINGS_C_ADIOS2_C_ENUMS_H_ */
diff --git a/bindings/C/adios2_c.cpp b/bindings/C/adios2_c.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fb13516feffa1031645f1e229dfe2723581295a3
--- /dev/null
+++ b/bindings/C/adios2_c.cpp
@@ -0,0 +1,338 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * adios2_c.h : ADIOS2 C bindings definitions
+ *
+ *  Created on: Mar 13, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include "adios2_c.h"
+
+#include <string>
+#include <vector>
+
+#include <adios2.h>
+#include <adios2/ADIOSMPI.h>
+
+adios2_ADIOS *adios2_init_config(const char *config_file, MPI_Comm mpi_comm,
+                                 const adios2_debug_mode debug_mode)
+{
+    const bool debugBool = (debug_mode == adios2_debug_mode_on) ? true : false;
+    adios2_ADIOS *adios = reinterpret_cast<adios2_ADIOS *>(
+        new adios2::ADIOS(config_file, mpi_comm, debugBool));
+
+    return adios;
+}
+
+adios2_ADIOS *adios2_init(MPI_Comm mpi_comm, const adios2_debug_mode debug_mode)
+{
+    return adios2_init_config("", mpi_comm, debug_mode);
+}
+
+adios2_ADIOS *adios2_init_config_nompi(const char *config_file,
+                                       const adios2_debug_mode debug_mode)
+{
+    return adios2_init_config(config_file, MPI_COMM_SELF, debug_mode);
+}
+
+adios2_ADIOS *adios2_init_nompi(const adios2_debug_mode debug_mode)
+{
+    return adios2_init_config("", MPI_COMM_SELF, debug_mode);
+}
+
+adios2_IO *adios2_declare_io(adios2_ADIOS *adios, const char *ioName)
+{
+    adios2_IO *io = reinterpret_cast<adios2_IO *>(
+        &reinterpret_cast<adios2::ADIOS *>(adios)->DeclareIO(ioName));
+    return io;
+}
+
+adios2_Variable *
+adios2_define_variable(adios2_IO *io, const char *name, const adios2_type type,
+                       const size_t ndims, const size_t *shape,
+                       const size_t *start, const size_t *count,
+                       const adios2_constant_dims constant_dims)
+{
+    const bool constantSizeBool =
+        (constant_dims == adios2_constant_dims_true) ? true : false;
+
+    std::vector<size_t> shapeV, startV, countV;
+
+    if (shape != NULL)
+    {
+        shapeV.assign(shape, shape + ndims);
+    }
+
+    if (start != NULL)
+    {
+        startV.assign(start, start + ndims);
+    }
+
+    if (count != NULL)
+    {
+        countV.assign(count, count + ndims);
+    }
+
+    adios2::IO &ioCpp = *reinterpret_cast<adios2::IO *>(io);
+    adios2::VariableBase *variable = nullptr;
+
+    switch (type)
+    {
+    case (adios2_type_string):;
+        break;
+
+    case (adios2_type_char):
+        variable =
+            dynamic_cast<adios2::Variable<char> *>(&ioCpp.DefineVariable<char>(
+                name, shapeV, startV, countV, constantSizeBool));
+        break;
+
+    case (adios2_type_signed_char):
+        variable = dynamic_cast<adios2::Variable<signed char> *>(
+            &ioCpp.DefineVariable<char>(name, shapeV, startV, countV,
+                                        constantSizeBool));
+        break;
+
+    case (adios2_type_short):
+        variable = dynamic_cast<adios2::Variable<short> *>(
+            &ioCpp.DefineVariable<short>(name, shapeV, startV, countV,
+                                         constantSizeBool));
+        break;
+
+    case (adios2_type_int):
+        variable =
+            dynamic_cast<adios2::Variable<int> *>(&ioCpp.DefineVariable<int>(
+                name, shapeV, startV, countV, constantSizeBool));
+        break;
+
+    case (adios2_type_long_int):
+        variable = dynamic_cast<adios2::Variable<long int> *>(
+            &ioCpp.DefineVariable<long int>(name, shapeV, startV, countV,
+                                            constantSizeBool));
+        break;
+
+    case (adios2_type_long_long_int):
+        variable = dynamic_cast<adios2::Variable<long long int> *>(
+            &ioCpp.DefineVariable<long long int>(name, shapeV, startV, countV,
+                                                 constantSizeBool));
+        break;
+
+    case (adios2_type_unsigned_char):
+        variable = dynamic_cast<adios2::Variable<unsigned char> *>(
+            &ioCpp.DefineVariable<unsigned char>(name, shapeV, startV, countV,
+                                                 constantSizeBool));
+        break;
+
+    case (adios2_type_unsigned_short):
+        variable = dynamic_cast<adios2::Variable<unsigned short> *>(
+            &ioCpp.DefineVariable<unsigned short>(name, shapeV, startV, countV,
+                                                  constantSizeBool));
+        break;
+
+    case (adios2_type_unsigned_int):
+        variable = dynamic_cast<adios2::Variable<unsigned int> *>(
+            &ioCpp.DefineVariable<unsigned int>(name, shapeV, startV, countV,
+                                                constantSizeBool));
+        break;
+    case (adios2_type_unsigned_long_int):
+        variable = dynamic_cast<adios2::Variable<unsigned long int> *>(
+            &ioCpp.DefineVariable<unsigned long int>(name, shapeV, startV,
+                                                     countV, constantSizeBool));
+        break;
+
+    case (adios2_type_unsigned_long_long_int):
+        variable = dynamic_cast<adios2::Variable<unsigned long long int> *>(
+            &ioCpp.DefineVariable<unsigned long long int>(
+                name, shapeV, startV, countV, constantSizeBool));
+        break;
+
+    case (adios2_type_float):
+        variable = dynamic_cast<adios2::Variable<float> *>(
+            &ioCpp.DefineVariable<float>(name, shapeV, startV, countV,
+                                         constantSizeBool));
+        break;
+
+    case (adios2_type_double):
+        variable = dynamic_cast<adios2::Variable<double> *>(
+            &ioCpp.DefineVariable<double>(name, shapeV, startV, countV,
+                                          constantSizeBool));
+        break;
+
+    case (adios2_type_float_complex):
+        variable = dynamic_cast<adios2::Variable<std::complex<float>> *>(
+            &ioCpp.DefineVariable<std::complex<float>>(
+                name, shapeV, startV, countV, constantSizeBool));
+        break;
+
+    case (adios2_type_double_complex):
+        variable = dynamic_cast<adios2::Variable<std::complex<double>> *>(
+            &ioCpp.DefineVariable<std::complex<double>>(
+                name, shapeV, startV, countV, constantSizeBool));
+        break;
+
+    case (adios2_type_int8_t):
+
+        variable = dynamic_cast<adios2::Variable<int8_t> *>(
+            &ioCpp.DefineVariable<int8_t>(name, shapeV, startV, countV,
+                                          constantSizeBool));
+        break;
+
+    case (adios2_type_int16_t):
+        variable = dynamic_cast<adios2::Variable<int16_t> *>(
+            &ioCpp.DefineVariable<int16_t>(name, shapeV, startV, countV,
+                                           constantSizeBool));
+        break;
+
+    case (adios2_type_int32_t):
+        variable = dynamic_cast<adios2::Variable<int32_t> *>(
+            &ioCpp.DefineVariable<int32_t>(name, shapeV, startV, countV,
+                                           constantSizeBool));
+        break;
+
+    case (adios2_type_int64_t):
+        variable = dynamic_cast<adios2::Variable<int64_t> *>(
+            &ioCpp.DefineVariable<int64_t>(name, shapeV, startV, countV,
+                                           constantSizeBool));
+        break;
+
+    case (adios2_type_uint8_t):
+
+        variable = dynamic_cast<adios2::Variable<uint8_t> *>(
+            &ioCpp.DefineVariable<uint8_t>(name, shapeV, startV, countV,
+                                           constantSizeBool));
+        break;
+
+    case (adios2_type_uint16_t):
+        variable = dynamic_cast<adios2::Variable<uint16_t> *>(
+            &ioCpp.DefineVariable<uint16_t>(name, shapeV, startV, countV,
+                                            constantSizeBool));
+        break;
+
+    case (adios2_type_uint32_t):
+        variable = dynamic_cast<adios2::Variable<uint32_t> *>(
+            &ioCpp.DefineVariable<uint32_t>(name, shapeV, startV, countV,
+                                            constantSizeBool));
+        break;
+
+    case (adios2_type_uint64_t):
+        variable = dynamic_cast<adios2::Variable<uint64_t> *>(
+            &ioCpp.DefineVariable<uint64_t>(name, shapeV, startV, countV,
+                                            constantSizeBool));
+        break;
+    }
+
+    return reinterpret_cast<adios2_Variable *>(variable);
+}
+
+adios2_Variable *adios2_get_variable(adios2_IO *io, const char *name)
+{
+    adios2::VariableBase *variable =
+        reinterpret_cast<adios2::IO *>(io)->GetVariableBase(name);
+
+    return reinterpret_cast<adios2_Variable *>(variable);
+}
+
+void adios2_set_engine(adios2_IO *io, const char *engine_type)
+{
+    reinterpret_cast<adios2::IO *>(io)->SetEngine(engine_type);
+}
+
+void adios2_set_param(adios2_IO *io, const char *key, const char *value)
+{
+    reinterpret_cast<adios2::IO *>(io)->SetSingleParameter(key, value);
+}
+
+unsigned int adios2_add_transport(adios2_IO *io, const char *transport_type)
+{
+    return reinterpret_cast<adios2::IO *>(io)->AddTransport(transport_type);
+}
+
+void adios2_set_transport_param(adios2_IO *io,
+                                const unsigned int transport_index,
+                                const char *key, const char *value)
+{
+    reinterpret_cast<adios2::IO *>(io)->SetTransportSingleParameter(
+        transport_index, key, value);
+}
+
+struct adios2_Engine
+{
+    std::shared_ptr<adios2::Engine> EngineCpp;
+};
+
+adios2_Engine *adios2_open(adios2_IO *io, const char *name,
+                           const adios2_open_mode open_mode)
+{
+    auto &ioCpp = *reinterpret_cast<adios2::IO *>(io);
+    return adios2_open_new_comm(io, name, open_mode, ioCpp.m_MPIComm);
+}
+
+adios2_Engine *adios2_open_new_comm(adios2_IO *io, const char *name,
+                                    const adios2_open_mode open_mode,
+                                    MPI_Comm mpi_comm)
+{
+    auto &ioCpp = *reinterpret_cast<adios2::IO *>(io);
+    adios2_Engine *engine = new adios2_Engine;
+
+    switch (open_mode)
+    {
+
+    case adios2_open_mode_write:
+        engine->EngineCpp = ioCpp.Open(name, adios2::OpenMode::Write, mpi_comm);
+        break;
+
+    case adios2_open_mode_read:
+        engine->EngineCpp = ioCpp.Open(name, adios2::OpenMode::Read, mpi_comm);
+        break;
+
+    case adios2_open_mode_append:
+        engine->EngineCpp =
+            ioCpp.Open(name, adios2::OpenMode::Append, mpi_comm);
+        break;
+
+    case adios2_open_mode_read_write:
+        engine->EngineCpp =
+            ioCpp.Open(name, adios2::OpenMode::ReadWrite, mpi_comm);
+        break;
+
+    case adios2_open_mode_undefined:
+
+        break;
+    }
+
+    return engine;
+}
+
+void adios2_write(adios2_Engine *engine, adios2_Variable *variable,
+                  const void *values)
+{
+    auto &variableBase = *reinterpret_cast<adios2::VariableBase *>(variable);
+    adios2_write_by_name(engine, variableBase.m_Name.c_str(), values);
+}
+
+void adios2_write_by_name(adios2_Engine *engine, const char *variable_name,
+                          const void *values)
+{
+    engine->EngineCpp->Write(variable_name, values);
+}
+
+void adios2_advance(adios2_Engine *engine) { engine->EngineCpp->Advance(); }
+
+void adios2_close(adios2_Engine *engine)
+{
+    engine->EngineCpp->Close();
+    delete engine;
+}
+
+void adios2_close_by_index(adios2_Engine *engine,
+                           const unsigned int transport_index)
+{
+    engine->EngineCpp->Close(transport_index);
+}
+
+void adios2_finalize(adios2_ADIOS *adios)
+{
+    delete reinterpret_cast<adios2::ADIOS *>(adios);
+}
diff --git a/bindings/C/adios2_c.h b/bindings/C/adios2_c.h
new file mode 100644
index 0000000000000000000000000000000000000000..99a69112a673dd83bb79e64837dc507ebb96efce
--- /dev/null
+++ b/bindings/C/adios2_c.h
@@ -0,0 +1,215 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * adios2_c.h : ADIOS2 C bindings declarations
+ *
+ *  Created on: Mar 13, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#ifndef ADIOS2_BINDINGS_C_ADIOS2_C_H_
+#define ADIOS2_BINDINGS_C_ADIOS2_C_H_
+
+#include <stddef.h> //size_t
+
+#include "adios2/ADIOSConfig.h"
+#include "adios2/ADIOSMPICommOnly.h"
+#include "adios2/adios2_c_enums.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct adios2_ADIOS adios2_ADIOS;
+typedef struct adios2_IO adios2_IO;
+typedef struct adios2_Variable adios2_Variable;
+typedef struct adios2_Engine adios2_Engine;
+
+/**
+ * Create an ADIOS struct pointer handler using a runtime config file in MPI
+ * application.
+ * @param config_file runtime configuration file, XML format, future: JSON
+ * @param mpi_comm MPI communicator from application for ADIOS scope
+ * @param debug_mode adios2_debug_mode_on or adios2_debug_mode_off
+ * @return valid ADIOS* handler
+ */
+adios2_ADIOS *adios2_init_config(const char *config_file, MPI_Comm mpi_comm,
+                                 const adios2_debug_mode debug_mode);
+
+/**
+ * Create an ADIOS struct pointer in MPI application.
+ * @param mpi_comm MPI communicator from application for ADIOS scope
+ * @param debug_mode adios2_debug_mode_on or adios2_debug_mode_off
+ * @return valid ADIOS* handler
+ */
+adios2_ADIOS *adios2_init(MPI_Comm mpi_comm,
+                          const adios2_debug_mode debug_mode);
+
+/**
+ * Create an ADIOS struct pointer handler using a runtime config file in serial
+ * nonMPI
+ * application.
+ * @param config_file runtime configuration file, XML format, future: JSON
+ * @param debug_mode adios2_debug_mode_on or adios2_debug_mode_off
+ * @return valid ADIOS* handler
+ */
+adios2_ADIOS *adios2_init_config_nompi(const char *config_file,
+                                       const adios2_debug_mode debug_mode);
+
+/**
+ * Create an ADIOS struct pointer handler in serial nonMPI application.
+ * @param debug_mode adios2_debug_mode_on or adios2_debug_mode_off
+ * @return valid ADIOS* handler
+ */
+adios2_ADIOS *adios2_init_nompi(const adios2_debug_mode debug_mode);
+
+/**
+ * Create an IO struct pointer handler from ADIOS* handler
+ * @param adios ADIOS* handler that owns the IO* handler
+ * @param io_name unique name for the newly declared io handler
+ * @return valid IO* handler
+ */
+adios2_IO *adios2_declare_io(adios2_ADIOS *adios, const char *io_name);
+
+/**
+ * Sets engine type for current io handler
+ * @param io handler
+ * @param engine_type available engine type
+ */
+void adios2_set_engine(adios2_IO *io, const char *engine_type);
+
+/**
+ * Set a single engine parameter
+ * @param io handler
+ * @param key parameter key
+ * @param value parameter value
+ */
+void adios2_set_param(adios2_IO *io, const char *key, const char *value);
+
+/**
+ * Set a transport for the present io
+ * @param io handler
+ * @param transport_type "File", "WAN"
+ * @return transport_index handler used for setting transport parameters or at
+ * Close
+ */
+unsigned int adios2_add_transport(adios2_IO *io, const char *transport_type);
+
+/**
+ * Sets a single transport parameter using io and transport_index (from
+ * adios2_add_transport) handlers
+ * @param io handler
+ * @param transport_index handler from adios2_add_transport
+ * @param key parameter key
+ * @param value parameter value
+ */
+void adios2_set_transport_param(adios2_IO *io,
+                                const unsigned int transport_index,
+                                const char *key, const char *value);
+
+/**
+ *
+ * @param io handler that owns the variable
+ * @param name unique variable name inside IO handler
+ * @param type primitive type
+ * @param ndims number of dimensions
+ * @param shape total MPI dimensions
+ * @param start local MPI start (offset)
+ * @param count local MPI count
+ * @param constant_size adios2_constant_dims_true: shape, start and count are
+ * constant, or
+ * adios2_constant_size_false
+ * @return variable handler
+ */
+adios2_Variable *
+adios2_define_variable(adios2_IO *io, const char *name, const adios2_type type,
+                       const size_t ndims, const size_t *shape,
+                       const size_t *start, const size_t *count,
+                       const adios2_constant_dims constant_dims);
+
+/**
+ * Returns a handler to a previously defined variable identified by a unique
+ * name
+ * @param io handler to variable io owner
+ * @param name unique name input
+ * @return variable handler if found
+ */
+adios2_Variable *adios2_get_variable(adios2_IO *io, const char *name);
+
+/**
+ * Create an adios2_Engine, from adios2_IO, that executes all IO operations.
+ * Resuse MPI_Comm passed to adios2_ADIOS that created adios2_IO io
+ * @param io input that creates the adios2_Engine
+ * @param name engine name
+ * @param open_mode read, write, append use adios2_open_mode enum
+ * @return engine handler
+ */
+adios2_Engine *adios2_open(adios2_IO *io, const char *name,
+                           const adios2_open_mode open_mode);
+
+/**
+ * Create an adios2_Engine, from adios2_IO, that executes all IO operations.
+ * Allows passing a new communicator.
+ * @param io input that creates the adios2_Engine
+ * @param name engine name
+ * @param open_mode read, write, append use adios2_open_mode enum
+ * @param mpi_comm allows passing a new MPI communicator
+ * @return engine handler
+ */
+adios2_Engine *adios2_open_new_comm(adios2_IO *io, const char *name,
+                                    const adios2_open_mode open_mode,
+                                    MPI_Comm mpi_comm);
+
+/**
+ * Write a variable using a adios2_Variable handler
+ * @param engine handler for engine executing the write
+ * @param variable handler for variable from adios2_define_variable
+ * @param values application data to be written for this variable
+ */
+void adios2_write(adios2_Engine *engine, adios2_Variable *variable,
+                  const void *values);
+
+/**
+ * Write a variable using a variable name created from adios2_define_variable
+ * @param engine handler for engine executing the write
+ * @param variable_name unique variable name, within io that create the engine.
+ * @param values application data to be written for this variable
+ */
+void adios2_write_by_name(adios2_Engine *engine, const char *variable_name,
+                          const void *values);
+
+/**
+ * Advance time step for writes
+ * @param engine handler executing IO tasks
+ */
+void adios2_advance(adios2_Engine *engine);
+
+/**
+ * Close all transports in adios2_Engine
+ * @param engine handler containing all transports to
+ * be closed. engine Becomes NULL after this function is called.
+ */
+void adios2_close(adios2_Engine *engine);
+
+/**
+ * Close a particular transport from the index returned by adios2_add_transport
+ * @param engine handler containing all transports to
+ * be closed. NOTE: engine NEVER becomes NULL due to this function.
+ * @param transport_index handler from adios2_add_transport
+ */
+void adios2_close_by_index(adios2_Engine *engine,
+                           const unsigned int transport_index);
+
+/**
+ * Final point for adios2_ADIOS handler.
+ * Deallocate adios pointer. Required to avoid memory leaks.
+ * @param adios input to be deallocated
+ */
+void adios2_finalize(adios2_ADIOS *adios);
+
+#ifdef __cplusplus
+} // end extern C
+#endif
+
+#endif /* ADIOS2_BINDINGS_C_ADIOS2_C_H_ */
diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt
index c21b8b329a9fe124899f3629e3a76f423ef66338..c775e4e0c05eb384a7cb9393f1aa6e442ab3e7a6 100644
--- a/bindings/CMakeLists.txt
+++ b/bindings/CMakeLists.txt
@@ -1,3 +1,14 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+
 if(ADIOS2_HAVE_Python)
   add_subdirectory(python)
 endif()
+
+if(ADIOS2_HAVE_Fortran)
+  add_subdirectory(fortran)
+endif()
+
+add_subdirectory(C)
diff --git a/bindings/fortran/CMakeLists.txt b/bindings/fortran/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..89b2de128408772b3f1b9f48ebd680da6db64362
--- /dev/null
+++ b/bindings/fortran/CMakeLists.txt
@@ -0,0 +1,39 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+
+add_library(adios2_f adios2_mod.f90 adios2_params_mod.f90 adios2_adios_mod.f90 
+                     adios2_io_mod.f90 adios2_engine_mod.f90
+                     adios2_engine_write_mod.f90 adios2_functions_mod.f90
+                     adios2_f2c.cpp)
+
+if(ADIOS2_HAVE_MPI)
+  target_sources(adios2_f PRIVATE mpi/adios2_adios_init_mod.f90 
+                                  mpi/adios2_io_open_mod.f90)
+else()
+  target_sources(adios2_f PRIVATE nompi/adios2_adios_init_nompi_mod.f90 
+                                  nompi/adios2_io_open_nompi_mod.f90 )
+endif()  
+  
+target_link_libraries(adios2_f PRIVATE adios2)
+
+
+if(ADIOS2_HAVE_MPI)
+  target_compile_definitions(adios2_f PUBLIC ADIOS2_HAVE_MPI_F)
+  target_include_directories(adios2_f PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} 
+                                             ${MPI_C_INCLUDE_PATH} 
+                                             ${CMAKE_CURRENT_BINARY_DIR})
+  target_link_libraries(adios2_f PRIVATE ${MPI_C_LIBRARIES} 
+                                         ${MPI_Fortran_LIBRARIES})
+else()
+  target_include_directories(adios2_f PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} 
+                                             ${CMAKE_CURRENT_BINARY_DIR})
+endif()
+
+install(
+  TARGETS ${adios2_f} EXPORT adios2
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
diff --git a/bindings/fortran/adios2_adios_mod.f90 b/bindings/fortran/adios2_adios_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..296de00840a42cffa173dce4fd7f7cb1df4f679c
--- /dev/null
+++ b/bindings/fortran/adios2_adios_mod.f90
@@ -0,0 +1,29 @@
+
+module adios2_adios
+
+    use adios2_adios_init
+    implicit none
+
+contains
+
+    subroutine adios2_declare_io(io, adios, io_name, ierr)
+        integer(kind=8), intent(out) :: io
+        integer(kind=8), intent(in) :: adios
+        character*(*), intent(in)  :: io_name
+        integer, intent(out) :: ierr
+
+        call adios2_declare_io_f2c(io, adios, TRIM(ADJUSTL(io_name))//char(0), &
+        & ierr)
+
+    end
+
+
+    subroutine adios2_finalize(adios, ierr)
+        integer(kind=8), intent(in) :: adios
+        integer, intent(out) :: ierr
+
+        call adios2_finalize_f2c(adios, ierr)
+    end
+
+
+end module
diff --git a/bindings/fortran/adios2_engine_mod.f90 b/bindings/fortran/adios2_engine_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..71f972698ad16d25c048ffb98cbc4e0815ff40ac
--- /dev/null
+++ b/bindings/fortran/adios2_engine_mod.f90
@@ -0,0 +1,33 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_engine_mod.f90 : ADIOS2 Fortran bindings for Engine class
+!
+!   Created on: Aug 22, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+module adios2_engine
+    use adios2_engine_write
+    implicit none
+
+contains
+
+    subroutine adios2_advance(engine, ierr)
+        integer(kind=8), intent(in) :: engine
+        integer, intent(out) :: ierr
+
+        call adios2_advance_f2c(engine, ierr)
+
+    end subroutine
+
+
+    subroutine adios2_close(engine, ierr)
+        integer(kind=8), intent(in) :: engine
+        integer, intent(out) :: ierr
+
+        call adios2_close_f2c(engine, ierr)
+
+    end subroutine
+
+end module
diff --git a/bindings/fortran/adios2_engine_write_mod.f90 b/bindings/fortran/adios2_engine_write_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..735c83da309cd0407a3911e13d95b3ecd32f4844
--- /dev/null
+++ b/bindings/fortran/adios2_engine_write_mod.f90
@@ -0,0 +1,660 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_engine_write_mod.f90 : ADIOS2 Fortran bindings for Engine generic
+!                                Write functions
+!
+!   Created on: Aug 22, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+module adios2_engine_write
+
+    interface adios2_write
+
+        ! Single Value
+        module procedure adios2_write_integer
+        module procedure adios2_write_real
+        module procedure adios2_write_dp
+        module procedure adios2_write_complex
+        module procedure adios2_write_complex_dp
+        module procedure adios2_write_integer1
+        module procedure adios2_write_integer2
+        module procedure adios2_write_integer8
+
+        ! 1D Array
+        module procedure adios2_write_integer_1d
+        module procedure adios2_write_real_1d
+        module procedure adios2_write_dp_1d
+        module procedure adios2_write_complex_1d
+        module procedure adios2_write_complex_dp_1d
+        module procedure adios2_write_integer1_1d
+        module procedure adios2_write_integer2_1d
+        module procedure adios2_write_integer8_1d
+
+        ! 2D Array
+        module procedure adios2_write_integer_2d
+        module procedure adios2_write_real_2d
+        module procedure adios2_write_dp_2d
+        module procedure adios2_write_complex_2d
+        module procedure adios2_write_complex_dp_2d
+        module procedure adios2_write_integer1_2d
+        module procedure adios2_write_integer2_2d
+        module procedure adios2_write_integer8_2d
+
+        ! 3D Array
+        module procedure adios2_write_integer_3d
+        module procedure adios2_write_real_3d
+        module procedure adios2_write_dp_3d
+        module procedure adios2_write_complex_3d
+        module procedure adios2_write_complex_dp_3d
+        module procedure adios2_write_integer1_3d
+        module procedure adios2_write_integer2_3d
+        module procedure adios2_write_integer8_3d
+
+        ! 4D Array
+        module procedure adios2_write_integer_4d
+        module procedure adios2_write_real_4d
+        module procedure adios2_write_dp_4d
+        module procedure adios2_write_complex_4d
+        module procedure adios2_write_complex_dp_4d
+        module procedure adios2_write_integer1_4d
+        module procedure adios2_write_integer2_4d
+        module procedure adios2_write_integer8_4d
+
+        ! 5D Array
+        module procedure adios2_write_integer_5d
+        module procedure adios2_write_real_5d
+        module procedure adios2_write_dp_5d
+        module procedure adios2_write_complex_5d
+        module procedure adios2_write_complex_dp_5d
+        module procedure adios2_write_integer1_5d
+        module procedure adios2_write_integer2_5d
+        module procedure adios2_write_integer8_5d
+
+        ! 6D Array
+        module procedure adios2_write_integer_6d
+        module procedure adios2_write_real_6d
+        module procedure adios2_write_dp_6d
+        module procedure adios2_write_complex_6d
+        module procedure adios2_write_complex_dp_6d
+        module procedure adios2_write_integer1_6d
+        module procedure adios2_write_integer2_6d
+        module procedure adios2_write_integer8_6d
+
+    end interface
+
+contains
+
+    ! Single Value
+    subroutine adios2_write_integer( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer, intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_real( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real, intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_dp( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real(kind=8), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex, intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_dp( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex(kind=8), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer1( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=1), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer2( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=2), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+
+    subroutine adios2_write_integer8( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=8), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    ! 1D Array
+    subroutine adios2_write_integer_1d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer, dimension(:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_real_1d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real, dimension(:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_dp_1d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real(kind=8), dimension(:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_1d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex, dimension(:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_dp_1d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex(kind=8), dimension(:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer1_1d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=1), dimension(:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer2_1d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=2), dimension(:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer8_1d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=8), dimension(:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    ! 2D Array
+    subroutine adios2_write_integer_2d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer, dimension(:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_real_2d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real, dimension(:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_dp_2d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real(kind=8), dimension(:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_2d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex, dimension(:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_dp_2d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex(kind=8), dimension(:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer1_2d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=1), dimension(:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer2_2d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=2), dimension(:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer8_2d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=8), dimension(:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    ! 3D Array
+    subroutine adios2_write_integer_3d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer, dimension(:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_real_3d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real, dimension(:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_dp_3d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real(kind=8), dimension(:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_3d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex, dimension(:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_dp_3d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex(kind=8), dimension(:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer1_3d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=1), dimension(:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer2_3d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=2), dimension(:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer8_3d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=8), dimension(:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+
+    ! 4D Array
+    subroutine adios2_write_integer_4d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer, dimension(:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_real_4d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real, dimension(:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_dp_4d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real(kind=8), dimension(:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_4d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex, dimension(:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_dp_4d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex(kind=8), dimension(:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer1_4d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=1), dimension(:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer2_4d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=2), dimension(:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer8_4d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=8), dimension(:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+
+    ! 5D Array
+    subroutine adios2_write_integer_5d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer, dimension(:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_real_5d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real, dimension(:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_dp_5d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real(kind=8), dimension(:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_5d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex, dimension(:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_dp_5d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex(kind=8), dimension(:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer1_5d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=1), dimension(:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer2_5d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=2), dimension(:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer8_5d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=8), dimension(:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+
+    ! 6D Array
+    subroutine adios2_write_integer_6d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer, dimension(:,:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_real_6d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real, dimension(:,:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_dp_6d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        real(kind=8), dimension(:,:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_6d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex, dimension(:,:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_complex_dp_6d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        complex(kind=8), dimension(:,:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer1_6d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=1), dimension(:,:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer2_6d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=2), dimension(:,:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+    subroutine adios2_write_integer8_6d( engine, variable, values, ierr )
+        integer(kind=8), intent(in):: engine
+        integer(kind=8), intent(in):: variable
+        integer(kind=8), dimension(:,:,:,:,:,:), intent(in):: values
+        integer, intent(out):: ierr
+
+        call adios2_write_f2c(engine, variable, values, ierr)
+
+    end subroutine
+
+end module
diff --git a/bindings/fortran/adios2_f2c.cpp b/bindings/fortran/adios2_f2c.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2e09b1aa95bb853cc15c3360845eb0fbda760586
--- /dev/null
+++ b/bindings/fortran/adios2_f2c.cpp
@@ -0,0 +1,272 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * adios2_f.cpp :  C-header glue code implmentation for functions called from
+ * Fortran modules
+ *
+ *  Created on: Aug 14, 2017
+ *      Author: William F Godoy
+ */
+
+#include "adios2_f2c.h"
+
+#include <stdexcept>
+#include <type_traits> //std::static_assert
+#include <vector>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef ADIOS2_HAVE_MPI_F
+void adios2_init_f2c_(adios2_ADIOS **adios, MPI_Fint *comm,
+                      const int *debug_mode, int *ierr)
+{
+    adios2_init_config_f2c_(adios, "", comm, debug_mode, ierr);
+}
+
+void adios2_init_config_f2c_(adios2_ADIOS **adios, const char *config_file,
+                             MPI_Fint *comm, const int *debug_mode, int *ierr)
+{
+    *ierr = 0;
+    try
+    {
+        *adios =
+            adios2_init_config(config_file, MPI_Comm_f2c(*comm),
+                               static_cast<adios2_debug_mode>(*debug_mode));
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+#else
+void adios2_init_f2c_(adios2_ADIOS **adios, const int *debug_mode, int *ierr)
+{
+    adios2_init_config_f2c_(adios, "", debug_mode, ierr);
+}
+
+void adios2_init_config_f2c_(adios2_ADIOS **adios, const char *config_file,
+                             const int *debug_mode, int *ierr)
+{
+    *ierr = 0;
+    try
+    {
+        *adios = adios2_init_config_nompi(
+            config_file, static_cast<adios2_debug_mode>(*debug_mode));
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+#endif
+
+void adios2_declare_io_f2c_(adios2_IO **io, adios2_ADIOS **adios,
+                            const char *io_name, int *ierr)
+{
+    *ierr = 0;
+
+    try
+    {
+        *io = adios2_declare_io(*adios, io_name);
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+void adios2_set_param_f2c_(adios2_IO **io, const char *key, const char *value,
+                           int *ierr)
+{
+    *ierr = 0;
+
+    try
+    {
+        adios2_set_param(*io, key, value);
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+void adios2_add_transport_f2c_(int *transport_index, adios2_IO **io,
+                               const char *transport_type, int *ierr)
+{
+    *ierr = 0;
+    *transport_index = -1;
+
+    try
+    {
+        *transport_index =
+            static_cast<int>(adios2_add_transport(*io, transport_type));
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+
+    if (*transport_index == -1)
+    {
+        *ierr = 1;
+    }
+}
+
+void adios2_set_transport_param_f2c_(adios2_IO **io, const int *transport_index,
+                                     const char *key, const char *value,
+                                     int *ierr)
+{
+    *ierr = 0;
+
+    try
+    {
+        adios2_set_transport_param(
+            *io, static_cast<unsigned int>(*transport_index), key, value);
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+void adios2_define_variable_f2c_(adios2_Variable **variable, adios2_IO **io,
+                                 const char *variable_name, const int *type,
+                                 const int *ndims, const int *shape,
+                                 const int *start, const int *count,
+                                 const int *constant_dims, int *ierr)
+{
+    auto lf_IntToSizeT = [](const int *ndims,
+                            const int *dims) -> std::vector<std::size_t> {
+
+        std::vector<std::size_t> vecSizeT(*ndims);
+        for (unsigned int dim = 0; dim < *ndims; ++dim)
+        {
+            vecSizeT[dim] = dims[dim];
+        }
+        return vecSizeT;
+    };
+
+    *ierr = 0;
+
+    std::vector<std::size_t> shapeV, startV, countV;
+    if (shape != NULL)
+    {
+        shapeV = lf_IntToSizeT(ndims, shape);
+    }
+
+    if (start != NULL)
+    {
+        startV = lf_IntToSizeT(ndims, start);
+    }
+
+    if (count != NULL)
+    {
+        countV = lf_IntToSizeT(ndims, count);
+    }
+
+    try
+    {
+        *variable = adios2_define_variable(
+            *io, variable_name, static_cast<adios2_type>(*type), *ndims,
+            shapeV.data(), startV.data(), countV.data(),
+            static_cast<adios2_constant_dims>(*constant_dims));
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+void adios2_open_f2c_(adios2_Engine **engine, adios2_IO **io, const char *name,
+                      const int *open_mode, int *ierr)
+{
+    *ierr = 0;
+    try
+    {
+        *engine =
+            adios2_open(*io, name, static_cast<adios2_open_mode>(*open_mode));
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+#ifdef ADIOS2_HAVE_MPI_F
+void adios2_open_new_comm_f2c_(adios2_Engine **engine, adios2_IO **io,
+                               const char *name, const int *open_mode,
+                               MPI_Fint *comm, int *ierr)
+{
+    *ierr = 0;
+    try
+    {
+        *engine = adios2_open_new_comm(
+            *io, name, static_cast<adios2_open_mode>(*open_mode),
+            MPI_Comm_f2c(*comm));
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+#endif
+
+void adios2_write_f2c_(adios2_Engine **engine, adios2_Variable **variable,
+                       const void *values, int *ierr)
+{
+    *ierr = 0;
+    try
+    {
+        adios2_write(*engine, *variable, values);
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+void adios2_advance_f2c_(adios2_Engine **engine, int *ierr)
+{
+    *ierr = 0;
+    try
+    {
+        adios2_advance(*engine);
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+void adios2_close_f2c_(adios2_Engine **engine, int *ierr)
+{
+    *ierr = 0;
+    try
+    {
+        adios2_close(*engine);
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+void adios2_finalize_f2c_(adios2_ADIOS **adios, int *ierr)
+{
+    *ierr = 0;
+    try
+    {
+        adios2_finalize(*adios);
+    }
+    catch (std::exception &e)
+    {
+        *ierr = 1;
+    }
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/bindings/fortran/adios2_f2c.h b/bindings/fortran/adios2_f2c.h
new file mode 100644
index 0000000000000000000000000000000000000000..49c55ab5015730d43ba555cc584760bd85acaae6
--- /dev/null
+++ b/bindings/fortran/adios2_f2c.h
@@ -0,0 +1,80 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * adios2_f.h : C-header glue code for functions called from Fortran modules
+ *
+ *  Created on: Aug 15, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#ifndef BINDINGS_FORTRAN_ADIOS2_F2C_H_
+#define BINDINGS_FORTRAN_ADIOS2_F2C_H_
+
+#include <stddef.h>
+
+#ifdef ADIOS2_HAVE_MPI_F
+#include <mpi.h>
+#endif
+
+#include <adios2_c.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef ADIOS2_HAVE_MPI_F
+void adios2_init_f2c_(adios2_ADIOS **adios, MPI_Fint *comm,
+                      const int *debug_mode, int *ierr);
+
+void adios2_init_config_f2c_(adios2_ADIOS **adios, const char *config_file,
+                             MPI_Fint *comm, const int *debug_mode, int *ierr);
+#else
+void adios2_init_f2c_(adios2_ADIOS **adios, const int *debug_mode, int *ierr);
+
+void adios2_init_config_f2c_(adios2_ADIOS **adios, const char *config_file,
+                             const int *debug_mode, int *ierr);
+#endif
+
+void adios2_declare_io_f2c_(adios2_IO **io, adios2_ADIOS **adios,
+                            const char *io_name, int *ierr);
+
+void adios2_set_param_f2c_(adios2_IO **io, const char *key, const char *value,
+                           int *ierr);
+
+void adios2_add_transport_f2c_(int *transport_index, adios2_IO **io,
+                               const char *transport_type, int *ierr);
+
+void adios2_set_transport_param_f2c_(adios2_IO **io, const int *transport_index,
+                                     const char *key, const char *value,
+                                     int *ierr);
+
+void adios2_define_variable_f2c_(adios2_Variable **variable, adios2_IO **io,
+                                 const char *variable_name, const int *type,
+                                 const int *ndims, const int *shape,
+                                 const int *start, const int *count,
+                                 const int *constant_dims, int *ierr);
+
+void adios2_open_f2c_(adios2_Engine **engine, adios2_IO **io, const char *name,
+                      const int *open_mode, int *ierr);
+
+#ifdef ADIOS2_HAVE_MPI_F
+void adios2_open_new_comm_f2c_(adios2_Engine **engine, adios2_IO **io,
+                               const char *name, const int *open_mode,
+                               MPI_Fint *comm, int *ierr);
+#endif
+
+void adios2_write_f2c_(adios2_Engine **engine, adios2_Variable **variable,
+                       const void *values, int *ierr);
+
+void adios2_advance_f2c_(adios2_Engine **engine, int *ierr);
+
+void adios2_close_f2c_(adios2_Engine **engine, int *ierr);
+
+void adios2_finalize_f2c_(adios2_ADIOS **adios, int *ierr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BINDINGS_FORTRAN_ADIOS2_F2C_H_ */
diff --git a/bindings/fortran/adios2_functions_mod.f90 b/bindings/fortran/adios2_functions_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..82ccbd60880687cba8e7c2b65e455e700bd1f5ba
--- /dev/null
+++ b/bindings/fortran/adios2_functions_mod.f90
@@ -0,0 +1,16 @@
+module adios2_functions
+    implicit none
+
+contains
+
+    integer function adios2_LogicalToInt( logical_value )
+        logical, value, intent(in) :: logical_value
+
+        adios2_LogicalToInt = 0
+        if( logical_value ) then
+            adios2_LogicalToInt = 1
+        end if
+
+    end function
+
+end module
diff --git a/bindings/fortran/adios2_io_mod.f90 b/bindings/fortran/adios2_io_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..6a8d645f1547f095c02de1af123d4a02a23f4651
--- /dev/null
+++ b/bindings/fortran/adios2_io_mod.f90
@@ -0,0 +1,82 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_io.f90 : ADIOS2 Fortran bindings for IO class
+!
+!   Created on: Mar 13, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+module adios2_io
+
+    use adios2_io_open
+    use adios2_functions
+    implicit none
+
+contains
+
+    subroutine adios2_set_param(io, key, value, ierr)
+        integer(kind=8), intent(in) :: io
+        character*(*), intent(in) :: key
+        character*(*), intent(in) :: value
+        integer, intent(out) :: ierr
+
+        call adios2_set_param_f2c( io, TRIM(ADJUSTL(key))//char(0), &
+            & TRIM(ADJUSTL(value))//char(0) )
+
+    end subroutine
+
+
+   subroutine adios2_add_transport(transport_index, io, transport_type, ierr)
+        integer, intent(out):: transport_index
+        integer(kind=8), intent(in) :: io
+        character*(*), intent(in) :: transport_type
+        integer, intent(out) :: ierr
+
+        call adios2_add_transport_f2c( transport_index, io, &
+            & TRIM(ADJUSTL(transport_type))//char(0), ierr)
+
+    end subroutine
+
+
+    subroutine adios2_set_transport_param(io, transport_index, key, value, ierr)
+        integer(kind=8), intent(in):: io
+        integer, intent(in):: transport_index
+        character*(*), intent(in) :: key
+        character*(*), intent(in) :: value
+        integer, intent(out):: ierr
+
+        call adios2_set_transport_param_f2c(io, transport_index, &
+          & TRIM(ADJUSTL(key))//char(0), TRIM(ADJUSTL(value))//char(0), &
+          & ierr)
+
+    end subroutine
+
+
+    subroutine adios2_define_variable(variable, io, variable_name, &
+        & adios2_type, ndims, shape_dims, start_dims, count_dims, &
+        & adios2_constant_dims, ierr)
+        integer(kind=8), intent(out) :: variable
+        integer(kind=8), intent(in) :: io
+        character*(*), intent(in) :: variable_name
+        integer, intent(in) :: adios2_type
+        integer, intent(in) :: ndims
+        integer, dimension(:), intent(in) :: shape_dims
+        integer, dimension(:), intent(in) :: start_dims
+        integer, dimension(:), intent(in) :: count_dims
+        logical, intent(in) :: adios2_constant_dims
+        integer, intent(out) :: ierr
+
+        !local
+        integer constant_dims
+
+        constant_dims = adios2_LogicalToInt(adios2_constant_dims)
+
+        call adios2_define_variable_f2c(variable, io, &
+            & TRIM(ADJUSTL(variable_name))//char(0), adios2_type, ndims, &
+            & shape_dims, start_dims, count_dims, constant_dims, ierr)
+
+    end subroutine
+
+
+end module
diff --git a/bindings/fortran/adios2_mod.f90 b/bindings/fortran/adios2_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..9fd25ff3450fa585a04ef84275f6e6b915bc044c
--- /dev/null
+++ b/bindings/fortran/adios2_mod.f90
@@ -0,0 +1,19 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_mod.f90 : ADIOS2 Fortran bindings central module
+!
+!   Created on: Mar 13, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+
+module adios2
+
+    use adios2_params
+    use adios2_adios
+    use adios2_io
+!    use adios2_variable
+    use adios2_engine
+
+end module
diff --git a/bindings/fortran/adios2_params_mod.f90 b/bindings/fortran/adios2_params_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..59f934e3e0aa499b9f411f9500b266675dbb0df0
--- /dev/null
+++ b/bindings/fortran/adios2_params_mod.f90
@@ -0,0 +1,41 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_mod.f90 : ADIOS2 Fortran bindings central module
+!
+!   Created on: Mar 13, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+module adios2_params
+    implicit none
+
+    ! Debug mode
+    logical, parameter :: adios2_debug_mode_on = .true.
+    logical, parameter :: adios2_debug_mode_off = .false.
+
+    ! Type
+    integer, parameter :: adios2_type_character = 0
+    integer, parameter :: adios2_type_integer = 1
+    integer, parameter :: adios2_type_real = 2
+    integer, parameter :: adios2_type_dp = 3
+    integer, parameter :: adios2_type_complex = 4
+    integer, parameter :: adios2_type_complex_dp = 5
+
+    integer, parameter :: adios2_type_integer1 = 6
+    integer, parameter :: adios2_type_integer2 = 7
+    integer, parameter :: adios2_type_integer4 = 8
+    integer, parameter :: adios2_type_integer8 = 9
+
+    ! Constant dims
+    logical, parameter :: adios2_constant_dims_true = .true.
+    logical, parameter :: adios2_constant_dims_false = .false.
+
+    ! Open Mode
+    integer, parameter :: adios2_open_mode_undefined = 0
+    integer, parameter :: adios2_open_mode_write = 1
+    integer, parameter :: adios2_open_mode_read = 2
+    integer, parameter :: adios2_open_mode_append = 3
+    integer, parameter :: adios2_open_mode_read_write = 4
+
+end module
diff --git a/bindings/fortran/mpi/adios2_adios_init_mod.f90 b/bindings/fortran/mpi/adios2_adios_init_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..55638f4bbe723752062b848c96c45d784380ee2f
--- /dev/null
+++ b/bindings/fortran/mpi/adios2_adios_init_mod.f90
@@ -0,0 +1,45 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_adios_init_mod.f90 : ADIOS2 Fortran bindings for ADIOS class Init
+!                              functions
+!
+!   Created on: Mar 13, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+
+module adios2_adios_init
+
+    use adios2_functions
+    implicit none
+
+contains
+
+    subroutine adios2_init(adios, comm, adios2_debug_mode, ierr)
+        integer(kind=8), intent(out) :: adios
+        integer, intent(in) :: comm
+        logical, value, intent(in) :: adios2_debug_mode
+        integer, intent(out) :: ierr
+
+        call adios2_init_config(adios, char(0), comm, adios2_debug_mode, ierr)
+
+    end subroutine
+
+
+    subroutine adios2_init_config(adios, config_file, comm, adios2_debug_mode, &
+        & ierr)
+        integer(kind=8), intent(out) :: adios
+        character*(*), intent(in) :: config_file
+        integer, intent(in) :: comm
+        logical, value, intent(in) :: adios2_debug_mode
+        integer, intent(out) :: ierr
+        ! local
+        integer debug_mode
+
+        debug_mode = adios2_LogicalToInt(adios2_debug_mode)
+        call adios2_init_config_f2c(adios, config_file, comm, debug_mode, ierr)
+
+    end subroutine
+
+end module
diff --git a/bindings/fortran/mpi/adios2_io_open_mod.f90 b/bindings/fortran/mpi/adios2_io_open_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..05f6d37759a31d487b1e0245f06a7ca222dc426f
--- /dev/null
+++ b/bindings/fortran/mpi/adios2_io_open_mod.f90
@@ -0,0 +1,43 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_io_open_mod.f90 : ADIOS2 Fortran bindings for IO class open function
+!
+!   Created on: Mar 13, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+
+module adios2_io_open
+    implicit none
+
+contains
+
+    subroutine adios2_open(engine, io, name, adios2_open_mode, ierr)
+        integer(kind=8), intent(out) :: engine
+        integer(kind=8), intent(in) :: io
+        character*(*), intent(in) :: name
+        integer, intent(in) :: adios2_open_mode
+        integer, intent(out) :: ierr
+
+        call adios2_open_f2c(engine, io, TRIM(ADJUSTL(name))//char(0), &
+            & adios2_open_mode, ierr)
+
+    end subroutine
+
+
+    subroutine adios2_open_new_comm(engine, io, name, adios2_open_mode, comm, &
+        & ierr)
+        integer(kind=8), intent(out) :: engine
+        integer(kind=8), intent(in) :: io
+        character*(*), intent(in) :: name
+        integer, intent(in) :: adios2_open_mode
+        integer, intent(in) :: comm
+        integer, intent(out) :: ierr
+
+        call adios2_open_new_comm_f2c(engine, io, TRIM(ADJUSTL(name))//char(0),&
+            & adios2_open_mode, comm, ierr)
+
+    end subroutine
+
+end module
diff --git a/bindings/fortran/nompi/adios2_adios_init_nompi_mod.f90 b/bindings/fortran/nompi/adios2_adios_init_nompi_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..351174a02d19c45e1af34fe7a40c5c0dddebca75
--- /dev/null
+++ b/bindings/fortran/nompi/adios2_adios_init_nompi_mod.f90
@@ -0,0 +1,41 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_init_nompi_mod.f90 : ADIOS2 Fortran bindings nonMPI init functions
+!
+!   Created on: Mar 13, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+
+module adios2_adios_init
+
+    use adios2_functions
+    implicit none
+
+contains
+
+    subroutine adios2_init(adios, adios2_debug_mode, ierr)
+        integer(kind=8), intent(out) :: adios
+        logical, value, intent(in) :: adios2_debug_mode
+        integer, intent(out) :: ierr
+
+        call adios2_init_config(adios, "", adios2_debug_mode, ierr)
+
+    end subroutine
+
+
+    subroutine adios2_init_config(adios, config_file, adios2_debug_mode, ierr)
+        integer(kind=8), intent(out) :: adios
+        character*(*), intent(in) :: config_file
+        logical, value, intent(in) :: adios2_debug_mode
+        integer, intent(out) :: ierr
+        ! local
+        integer debug_mode
+
+        debug_mode = adios2_LogicalToInt(adios2_debug_mode)
+        call adios2_init_config_f2c(adios, config_file, debug_mode, ierr)
+
+    end subroutine
+
+end module
diff --git a/bindings/fortran/nompi/adios2_io_open_nompi_mod.f90 b/bindings/fortran/nompi/adios2_io_open_nompi_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..d0a86535eee4ebd852e0edeb872991a894c207e3
--- /dev/null
+++ b/bindings/fortran/nompi/adios2_io_open_nompi_mod.f90
@@ -0,0 +1,28 @@
+!
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+!  accompanying file Copyright.txt for details.
+!
+!  adios2_io_open_nompi_mod.f90 : ADIOS2 Fortran bindings for IO class open
+!                                 function
+!   Created on: Mar 13, 2017
+!       Author: William F Godoy godoywf@ornl.gov
+!
+
+module adios2_io_open
+    implicit none
+
+contains
+
+    subroutine adios2_open(engine, io, name, adios2_open_mode, ierr)
+        integer(kind=8), intent(out) :: engine
+        integer(kind=8), intent(in) :: io
+        character*(*), intent(in) :: name
+        integer, intent(in) :: adios2_open_mode
+        integer, intent(out) :: ierr
+
+        call adios2_open_f2c(engine, io, TRIM(ADJUSTL(name))//char(0), &
+            & adios2_open_mode, ierr)
+
+    end subroutine
+
+end module
diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt
index 90d030a55245ad5ee4e0d691a093d6e160567fa7..a6ced9fc20570ec0b41b0343ea705906cf1b284a 100644
--- a/bindings/python/CMakeLists.txt
+++ b/bindings/python/CMakeLists.txt
@@ -1,7 +1,3 @@
-set(Python_ADDITIONAL_VERSIONS 3 2.7)
-find_package(PythonInterp REQUIRED)
-find_package(PythonLibsNew REQUIRED)
-
 pybind11_add_module(adios2py MODULE
   ADIOSPy.h
   ADIOSPy.cpp
@@ -19,7 +15,6 @@ pybind11_add_module(adios2py MODULE
 )
 target_link_libraries(adios2py PRIVATE adios2)
 if(ADIOS2_HAVE_MPI)
-  find_package(PythonModule REQUIRED COMPONENTS mpi4py mpi4py/mpi4py.h)
   target_link_libraries(adios2py PRIVATE PythonModule::mpi4py)
 endif()
 
diff --git a/cmake/ADIOS2ConfigCommon.cmake.in b/cmake/ADIOS2ConfigCommon.cmake.in
index 945a97a9810c9c3c70364bf7d0ba8e218f4881cf..c32881dab5ee8a31b178133d38c16698277d8c19 100644
--- a/cmake/ADIOS2ConfigCommon.cmake.in
+++ b/cmake/ADIOS2ConfigCommon.cmake.in
@@ -2,6 +2,13 @@ cmake_minimum_required(VERSION 3.5)
 
 include(CMakeFindDependencyMacro)
 
+set(ADIOS2_HAVE_C @ADIOS2_HAVE_C@)
+if(ADIOS2_HAVE_C)
+  find_dependency(C)
+endif()
+
+set(ADIOS2_HAVE_Fortran @ADIOS2_HAVE_Fortran@)
+
 set(ADIOS2_HAVE_MPI @ADIOS2_HAVE_MPI@)
 if(ADIOS2_HAVE_MPI)
   find_dependency(MPI)
diff --git a/cmake/DetectOptions.cmake b/cmake/DetectOptions.cmake
index 5022c676d3c31fe1dbaa82907f0ec9b72417a614..2562e77a2b94180e0d3e5d2b397bdb3c326050c0 100644
--- a/cmake/DetectOptions.cmake
+++ b/cmake/DetectOptions.cmake
@@ -16,75 +16,86 @@ set(adios2_find_modules)
 # BZip2
 if(ADIOS2_USE_BZip2 STREQUAL AUTO)
   find_package(BZip2)
-  if(BZIP2_FOUND)
-    set(ADIOS2_HAVE_BZip2 TRUE)
-  endif()
 elseif(ADIOS2_USE_BZip2)
+  find_package(BZip2 REQUIRED)
+endif()
+if(BZIP2_FOUND)
   set(ADIOS2_HAVE_BZip2 TRUE)
 endif()
 
 # ZFP
 if(ADIOS2_USE_ZFP STREQUAL AUTO)
   find_package(ZFP)
-  if(ZFP_FOUND)
-    set(ADIOS2_HAVE_ZFP TRUE)
-  endif()
 elseif(ADIOS2_USE_ZFP)
+  find_package(ZFP REQUIRED)
+endif()
+if(ZFP_FOUND)
   set(ADIOS2_HAVE_ZFP TRUE)
 endif()
 
+set(mpi_find_components C)
+
+# Fortran
+if(ADIOS2_USE_Fortran STREQUAL AUTO)
+  # Currently auto-detection for language support does not work in CMake.  See
+  # documentation for the "enable_language" command
+  message(WARN "Auto-detection of Fortran is not currently supported; Disabling")
+  #enable_language(Fortran OPTIONAL)
+elseif(ADIOS2_USE_Fortran)
+  enable_language(Fortran)
+endif()
+if(CMAKE_Fortran_COMPILER_LOADED)
+  set(ADIOS2_HAVE_Fortran TRUE)
+  list(APPEND mpi_find_components Fortran)
+endif()
+
 # MPI
 if(ADIOS2_USE_MPI STREQUAL AUTO)
-  find_package(MPI COMPONENTS C)
-  if(MPI_FOUND)
-    set(ADIOS2_HAVE_MPI TRUE)
-  endif()
+  find_package(MPI COMPONENTS ${mpi_find_components})
 elseif(ADIOS2_USE_MPI)
+  find_package(MPI COMPONENTS ${mpi_find_components} REQUIRED)
+endif()
+if(MPI_FOUND)
   set(ADIOS2_HAVE_MPI TRUE)
 endif()
 
 # DataMan
-if(ADIOS2_USE_DataMan STREQUAL AUTO)
-  if(SHARED_LIBS_SUPPORTED AND NOT MSVC)
-    set(ADIOS2_HAVE_DataMan TRUE)
-  endif()
-elseif(ADIOS2_USE_DataMan)
+if(SHARED_LIBS_SUPPORTED AND NOT MSVC)
   set(ADIOS2_HAVE_DataMan TRUE)
 endif()
 
 # ZeroMQ
 if(ADIOS2_USE_ZeroMQ STREQUAL AUTO)
   find_package(ZeroMQ)
-  if(ZeroMQ_FOUND)
-    set(ADIOS2_HAVE_ZeroMQ TRUE)
-  endif()
 elseif(ADIOS2_USE_ZeroMQ)
+  find_package(ZeroMQ REQUIRED)
+endif()
+if(ZeroMQ_FOUND)
   set(ADIOS2_HAVE_ZeroMQ TRUE)
 endif()
 
 # HDF5
 if(ADIOS2_USE_HDF5 STREQUAL AUTO)
   find_package(HDF5 COMPONENTS C)
-  if(HDF5_FOUND AND
-     ((ADIOS2_HAVE_MPI AND HDF5_IS_PARALLEL) OR
-      NOT (ADIOS2_HAVE_MPI OR HDF5_IS_PARALLEL)))
-    set(ADIOS2_HAVE_HDF5 TRUE)
-  endif()
 elseif(ADIOS2_USE_HDF5)
+  find_package(HDF5 REQUIRED COMPONENTS C)
+endif()
+if(HDF5_FOUND AND
+   ((ADIOS2_HAVE_MPI AND HDF5_IS_PARALLEL) OR
+    NOT (ADIOS2_HAVE_MPI OR HDF5_IS_PARALLEL)))
   set(ADIOS2_HAVE_HDF5 TRUE)
 endif()
 
 # ADIOS1
+if(NOT ADIOS2_HAVE_MPI)
+  set(adios1_find_args COMPONENTS sequential)
+endif()
 if(ADIOS2_USE_ADIOS1 STREQUAL AUTO)
-  if(NOT ADIOS2_HAVE_MPI)
-    set(adios1_args COMPONENTS sequential)
-  endif()
-  find_package(ADIOS1 1.12.0 ${adios1_args})
-  unset(adios1_args)
-  if(ADIOS1_FOUND)
-    set(ADIOS2_HAVE_ADIOS1 TRUE)
-  endif()
+  find_package(ADIOS1 1.12.0 ${adios1_find_args})
 elseif(ADIOS2_USE_ADIOS1)
+  find_package(ADIOS1 1.12.0 REQUIRED ${adios1_find_args})
+endif()
+if(ADIOS1_FOUND)
   set(ADIOS2_HAVE_ADIOS1 TRUE)
 endif()
 
@@ -93,39 +104,35 @@ endif()
 list(INSERT CMAKE_MODULE_PATH 0
   "${ADIOS2_SOURCE_DIR}/thirdparty/pybind11/pybind11/tools"
 )
-if(ADIOS2_USE_Python STREQUAL AUTO)
+if(ADIOS2_USE_Python)
+  if(NOT (ADIOS2_USE_Python STREQUAL AUTO))
+    set(python_find_args REQUIRED)
+  endif()
   if(SHARED_LIBS_SUPPORTED AND ADIOS2_ENABLE_PIC)
     set(Python_ADDITIONAL_VERSIONS 3 2.7)
-    find_package(PythonInterp)
-    find_package(PythonLibsNew)
-    if(PYTHONLIBS_FOUND)
-      if(ADIOS2_HAVE_MPI)
-        find_package(PythonModule COMPONENTS mpi4py mpi4py/mpi4py.h)
-        if(PythonModule_mpi4py_FOUND)
-          set(ADIOS2_HAVE_Python TRUE)
-        endif()
-      else()
-        set(ADIOS2_HAVE_Python TRUE)
-      endif()
+    list(APPEND python_find_args COMPONENTS Interp Libs numpy)
+    if(ADIOS2_HAVE_MPI)
+      list(APPEND python_find_args "mpi4py\\\;mpi4py/mpi4py.h")
     endif()
+    find_package(PythonFull ${python_find_args})
   endif()
-elseif(ADIOS2_USE_Python)
-  set(ADIOS2_HAVE_Python TRUE)
+endif()
+if(PythonFull_FOUND)
+  set(ADIOS2_HAVE_Python ON)
 endif()
 
 #SysV IPC
-if(ADIOS2_USE_SysVShMem STREQUAL AUTO)
-  if(UNIX)
-    include(CheckSymbolExists)
-    CHECK_SYMBOL_EXISTS(shmget "sys/ipc.h;sys/shm.h" HAVE_shmget)
-    if(HAVE_shmget)
-      set(ADIOS2_HAVE_SysVShMem ON)
-    else()
-      set(ADIOS2_HAVE_SysVShMem OFF)
-    endif()
+if(UNIX)
+  include(CheckSymbolExists)
+  CHECK_SYMBOL_EXISTS(shmget "sys/ipc.h;sys/shm.h" HAVE_shmget)
+  if(HAVE_shmget)
+    set(ADIOS2_HAVE_SysVShMem ON)
   else()
     set(ADIOS2_HAVE_SysVShMem OFF)
   endif()
-elseif(ADIOS2_USE_SysVShMem)
-  set(ADIOS2_HAVE_SysVShMem TRUE)
+else()
+  set(ADIOS2_HAVE_SysVShMem OFF)
 endif()
+
+# Multithreading
+find_package(Threads REQUIRED)
diff --git a/cmake/FindHDF5.cmake b/cmake/FindHDF5.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..23d5043d75cf8466e6d8a3629f2b210db18d7779
--- /dev/null
+++ b/cmake/FindHDF5.cmake
@@ -0,0 +1,11 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+
+# This module is already included in new versions of CMake
+if(CMAKE_VERSION VERSION_LESS 3.9)
+  include(${CMAKE_CURRENT_LIST_DIR}/upstream/FindHDF5.cmake)
+else()
+  include(${CMAKE_ROOT}/Modules/FindHDF5.cmake)
+endif()
diff --git a/cmake/FindPythonFull.cmake b/cmake/FindPythonFull.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..6b70423eaaf0fef9254c22c1a772569267eeef0a
--- /dev/null
+++ b/cmake/FindPythonFull.cmake
@@ -0,0 +1,56 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+#
+# PythonFull
+# -----------
+#
+# Try to find various pieces needed for a full python environment including
+# python libs, interpreter, and modules
+#
+# This module defines the following variables:
+#
+#   PythonFull_FOUND
+#   PYTHONINTERP_FOUND
+#   PYTHON_EXECUTABLE
+#   PYTHONLIBS_FOUND
+#   PYTHON_LIBRARIES
+#   PythonModule_${module_NAME}_FOUND
+#   
+# This is intented to be called by specifying components for libraries,
+# interpreter, and modules.  So, for example, if you needed libraries, the
+# interpreter, mpi4py, and numpy then you would call:
+#
+#   find_package(PythonFull COMPONENTS Interp Libs mpi4py numpy)
+#
+
+include(CMakeFindDependencyMacro)
+set(_req_vars)
+foreach(comp IN LISTS PythonFull_FIND_COMPONENTS)
+  if(comp STREQUAL Interp)
+    find_package(PythonInterp)
+    set(PythonFull_${comp}_FOUND ${PYTHONINTERP_FOUND})
+    list(APPEND _req_vars PYTHON_EXECUTABLE)
+  elseif(comp STREQUAL Libs)
+    find_package(PythonLibsNew)
+    set(PythonFull_${comp}_FOUND ${PYTHONLIBS_FOUND})
+    list(APPEND _req_vars PYTHON_LIBRARIES)
+  else()
+    # Parse off any specified headers and libs for a given module
+    set(_comp ${comp})
+    list(GET _comp 0 _mod)
+    string(REGEX REPLACE "${_mod}\\\;[^;]*" "${_mod}"
+      PythonFull_FIND_COMPONENTS "${PythonFull_FIND_COMPONENTS}"
+    )
+    set(PythonFull_FIND_REQUIRED_${_mod} TRUE)
+    find_package(PythonModule COMPONENTS ${_comp})
+    set(PythonFull_${_mod}_FOUND ${PythonModule_${_mod}_FOUND})
+  endif()
+endforeach()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(PythonFull
+  REQUIRED_VARS ${_req_vars}
+  HANDLE_COMPONENTS
+)
diff --git a/cmake/FindPythonModule.cmake b/cmake/FindPythonModule.cmake
index 492d1d28f4205b856903fae195fa9d9a208f042d..c47f71dbc19c6ae8ddcb3ee8618c69b79487802f 100644
--- a/cmake/FindPythonModule.cmake
+++ b/cmake/FindPythonModule.cmake
@@ -119,8 +119,9 @@ set( PythonModule_${module_NAME}_PATH
   endif()
 
   include(FindPackageHandleStandardArgs)
-  find_package_handle_standard_args(PythonModule_${module_NAME} DEFAULT_MSG
-    ${required_vars}
+  find_package_handle_standard_args(PythonModule_${module_NAME}
+    FOUND_VAR PythonModule_${module_NAME}_FOUND
+    REQUIRED_VARS ${required_vars}
   )
 endif()
 
diff --git a/cmake/upstream/FindHDF5.cmake b/cmake/upstream/FindHDF5.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..aef46cb611f5cbaf0cbc488514a411250f48163b
--- /dev/null
+++ b/cmake/upstream/FindHDF5.cmake
@@ -0,0 +1,938 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# FindHDF5
+# --------
+#
+# Find HDF5, a library for reading and writing self describing array data.
+#
+#
+#
+# This module invokes the HDF5 wrapper compiler that should be installed
+# alongside HDF5.  Depending upon the HDF5 Configuration, the wrapper
+# compiler is called either h5cc or h5pcc.  If this succeeds, the module
+# will then call the compiler with the -show argument to see what flags
+# are used when compiling an HDF5 client application.
+#
+# The module will optionally accept the COMPONENTS argument.  If no
+# COMPONENTS are specified, then the find module will default to finding
+# only the HDF5 C library.  If one or more COMPONENTS are specified, the
+# module will attempt to find the language bindings for the specified
+# components.  The only valid components are C, CXX, Fortran, HL, and
+# Fortran_HL.  If the COMPONENTS argument is not given, the module will
+# attempt to find only the C bindings.
+#
+# This module will read the variable
+# HDF5_USE_STATIC_LIBRARIES to determine whether or not to prefer a
+# static link to a dynamic link for HDF5 and all of it's dependencies.
+# To use this feature, make sure that the HDF5_USE_STATIC_LIBRARIES
+# variable is set before the call to find_package.
+#
+# To provide the module with a hint about where to find your HDF5
+# installation, you can set the environment variable HDF5_ROOT.  The
+# Find module will then look in this path when searching for HDF5
+# executables, paths, and libraries.
+#
+# Both the serial and parallel HDF5 wrappers are considered and the first
+# directory to contain either one will be used.  In the event that both appear
+# in the same directory the serial version is preferentially selected. This
+# behavior can be reversed by setting the variable HDF5_PREFER_PARALLEL to
+# true.
+#
+# In addition to finding the includes and libraries required to compile
+# an HDF5 client application, this module also makes an effort to find
+# tools that come with the HDF5 distribution that may be useful for
+# regression testing.
+#
+# This module will define the following variables:
+#
+# ::
+#
+#   HDF5_FOUND - true if HDF5 was found on the system
+#   HDF5_VERSION - HDF5 version in format Major.Minor.Release
+#   HDF5_INCLUDE_DIRS - Location of the hdf5 includes
+#   HDF5_INCLUDE_DIR - Location of the hdf5 includes (deprecated)
+#   HDF5_DEFINITIONS - Required compiler definitions for HDF5
+#   HDF5_LIBRARIES - Required libraries for all requested bindings
+#   HDF5_HL_LIBRARIES - Required libraries for the HDF5 high level API for all
+#                       bindings, if the HL component is enabled
+#
+# Available components are: C CXX Fortran and HL.  For each enabled language
+# binding, a corresponding HDF5_${LANG}_LIBRARIES variable, and potentially
+# HDF5_${LANG}_DEFINITIONS, will be defined.
+# If the HL component is enabled, then an HDF5_${LANG}_HL_LIBRARIES will
+# also be defined.  With all components enabled, the following variables will be defined:
+#
+# ::
+#
+#   HDF5_C_DEFINITIONS -- Required compiler definitions for HDF5 C bindings
+#   HDF5_CXX_DEFINITIONS -- Required compiler definitions for HDF5 C++ bindings
+#   HDF5_Fortran_DEFINITIONS -- Required compiler definitions for HDF5 Fortran bindings
+#   HDF5_C_INCLUDE_DIRS -- Required include directories for HDF5 C bindings
+#   HDF5_CXX_INCLUDE_DIRS -- Required include directories for HDF5 C++ bindings
+#   HDF5_Fortran_INCLUDE_DIRS -- Required include directories for HDF5 Fortran bindings
+#   HDF5_C_LIBRARIES - Required libraries for the HDF5 C bindings
+#   HDF5_CXX_LIBRARIES - Required libraries for the HDF5 C++ bindings
+#   HDF5_Fortran_LIBRARIES - Required libraries for the HDF5 Fortran bindings
+#   HDF5_C_HL_LIBRARIES - Required libraries for the high level C bindings
+#   HDF5_CXX_HL_LIBRARIES - Required libraries for the high level C++ bindings
+#   HDF5_Fortran_HL_LIBRARIES - Required libraries for the high level Fortran
+#                               bindings.
+#
+#   HDF5_IS_PARALLEL - Whether or not HDF5 was found with parallel IO support
+#   HDF5_C_COMPILER_EXECUTABLE - the path to the HDF5 C wrapper compiler
+#   HDF5_CXX_COMPILER_EXECUTABLE - the path to the HDF5 C++ wrapper compiler
+#   HDF5_Fortran_COMPILER_EXECUTABLE - the path to the HDF5 Fortran wrapper compiler
+#   HDF5_C_COMPILER_EXECUTABLE_NO_INTERROGATE - path to the primary C compiler
+#                                               which is also the HDF5 wrapper
+#   HDF5_CXX_COMPILER_EXECUTABLE_NO_INTERROGATE - path to the primary C++
+#                                                 compiler which is also
+#                                                 the HDF5 wrapper
+#   HDF5_Fortran_COMPILER_EXECUTABLE_NO_INTERROGATE - path to the primary
+#                                                     Fortran compiler which
+#                                                     is also the HDF5 wrapper
+#   HDF5_DIFF_EXECUTABLE - the path to the HDF5 dataset comparison tool
+#
+# The following variable can be set to guide the search for HDF5 libraries and includes:
+#
+# ``HDF5_ROOT``
+#   Specify the path to the HDF5 installation to use.
+#
+# ``HDF5_FIND_DEBUG``
+#   Set to a true value to get some extra debugging output.
+#
+# ``HDF5_NO_FIND_PACKAGE_CONFIG_FILE``
+#   Set to a true value to skip trying to find ``hdf5-config.cmake``.
+
+# This module is maintained by Will Dicharry <wdicharry@stellarscience.com>.
+
+include(${CMAKE_ROOT}/Modules/SelectLibraryConfigurations.cmake)
+include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
+
+# List of the valid HDF5 components
+set(HDF5_VALID_LANGUAGE_BINDINGS C CXX Fortran)
+
+# Validate the list of find components.
+if(NOT HDF5_FIND_COMPONENTS)
+  set(HDF5_LANGUAGE_BINDINGS "C")
+else()
+  set(HDF5_LANGUAGE_BINDINGS)
+  # add the extra specified components, ensuring that they are valid.
+  set(FIND_HL OFF)
+  foreach(component IN LISTS HDF5_FIND_COMPONENTS)
+    list(FIND HDF5_VALID_LANGUAGE_BINDINGS ${component} component_location)
+    if(NOT component_location EQUAL -1)
+      list(APPEND HDF5_LANGUAGE_BINDINGS ${component})
+    elseif(component STREQUAL "HL")
+      set(FIND_HL ON)
+    elseif(component STREQUAL "Fortran_HL") # only for compatibility
+      list(APPEND HDF5_LANGUAGE_BINDINGS Fortran)
+      set(FIND_HL ON)
+      set(HDF5_FIND_REQUIRED_Fortran_HL False)
+      set(HDF5_FIND_REQUIRED_Fortran True)
+      set(HDF5_FIND_REQUIRED_HL True)
+    else()
+      message(FATAL_ERROR "${component} is not a valid HDF5 component.")
+    endif()
+  endforeach()
+  if(NOT HDF5_LANGUAGE_BINDINGS)
+    get_property(__langs GLOBAL PROPERTY ENABLED_LANGUAGES)
+    foreach(__lang IN LISTS __langs)
+      if(__lang MATCHES "^(C|CXX|Fortran)$")
+        list(APPEND HDF5_LANGUAGE_BINDINGS ${__lang})
+      endif()
+    endforeach()
+  endif()
+  list(REMOVE_ITEM HDF5_FIND_COMPONENTS Fortran_HL) # replaced by Fortran and HL
+  list(REMOVE_DUPLICATES HDF5_LANGUAGE_BINDINGS)
+endif()
+
+# Determine whether to search for serial or parallel executable first
+if(HDF5_PREFER_PARALLEL)
+  set(HDF5_C_COMPILER_NAMES h5pcc h5cc)
+  set(HDF5_CXX_COMPILER_NAMES h5pc++ h5c++)
+  set(HDF5_Fortran_COMPILER_NAMES h5pfc h5fc)
+else()
+  set(HDF5_C_COMPILER_NAMES h5cc h5pcc)
+  set(HDF5_CXX_COMPILER_NAMES h5c++ h5pc++)
+  set(HDF5_Fortran_COMPILER_NAMES h5fc h5pfc)
+endif()
+
+# We may have picked up some duplicates in various lists during the above
+# process for the language bindings (both the C and C++ bindings depend on
+# libz for example).  Remove the duplicates. It appears that the default
+# CMake behavior is to remove duplicates from the end of a list. However,
+# for link lines, this is incorrect since unresolved symbols are searched
+# for down the link line. Therefore, we reverse the list, remove the
+# duplicates, and then reverse it again to get the duplicates removed from
+# the beginning.
+macro(_HDF5_remove_duplicates_from_beginning _list_name)
+  if(${_list_name})
+    list(REVERSE ${_list_name})
+    list(REMOVE_DUPLICATES ${_list_name})
+    list(REVERSE ${_list_name})
+  endif()
+endmacro()
+
+
+# Test first if the current compilers automatically wrap HDF5
+
+function(_HDF5_test_regular_compiler_C success version is_parallel)
+  set(scratch_directory
+    ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hdf5)
+  if(NOT ${success} OR
+     NOT EXISTS ${scratch_directory}/compiler_has_h5_c)
+    set(test_file ${scratch_directory}/cmake_hdf5_test.c)
+    file(WRITE ${test_file}
+      "#include <hdf5.h>\n"
+      "#include <hdf5_hl.h>\n"
+      "const char* info_ver = \"INFO\" \":\" H5_VERSION;\n"
+      "#ifdef H5_HAVE_PARALLEL\n"
+      "const char* info_parallel = \"INFO\" \":\" \"PARALLEL\";\n"
+      "#endif\n"
+      "int main(int argc, char **argv) {\n"
+      "  int require = 0;\n"
+      "  require += info_ver[argc];\n"
+      "#ifdef H5_HAVE_PARALLEL\n"
+      "  require += info_parallel[argc];\n"
+      "#endif\n"
+      "  hid_t fid;\n"
+      "  fid = H5Fcreate(\"foo.h5\",H5F_ACC_TRUNC,H5P_DEFAULT,H5P_DEFAULT);\n"
+      "  return 0;\n"
+      "}")
+    try_compile(${success} ${scratch_directory} ${test_file}
+      COPY_FILE ${scratch_directory}/compiler_has_h5_c
+    )
+  endif()
+  if(${success})
+    file(STRINGS ${scratch_directory}/compiler_has_h5_c INFO_STRINGS
+      REGEX "^INFO:"
+    )
+    string(REGEX MATCH "^INFO:([0-9]+\\.[0-9]+\\.[0-9]+)(-patch([0-9]+))?"
+      INFO_VER "${INFO_STRINGS}"
+    )
+    set(${version} ${CMAKE_MATCH_1})
+    if(CMAKE_MATCH_3)
+      set(${version} ${HDF5_CXX_VERSION}.${CMAKE_MATCH_3})
+    endif()
+    set(${version} ${${version}} PARENT_SCOPE)
+
+    if(INFO_STRINGS MATCHES "INFO:PARALLEL")
+      set(${is_parallel} TRUE PARENT_SCOPE)
+    else()
+      set(${is_parallel} FALSE PARENT_SCOPE)
+    endif()
+  endif()
+endfunction()
+
+function(_HDF5_test_regular_compiler_CXX success version is_parallel)
+  set(scratch_directory ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hdf5)
+  if(NOT ${success} OR
+     NOT EXISTS ${scratch_directory}/compiler_has_h5_cxx)
+    set(test_file ${scratch_directory}/cmake_hdf5_test.cxx)
+    file(WRITE ${test_file}
+      "#include <H5Cpp.h>\n"
+      "#ifndef H5_NO_NAMESPACE\n"
+      "using namespace H5;\n"
+      "#endif\n"
+      "const char* info_ver = \"INFO\" \":\" H5_VERSION;\n"
+      "#ifdef H5_HAVE_PARALLEL\n"
+      "const char* info_parallel = \"INFO\" \":\" \"PARALLEL\";\n"
+      "#endif\n"
+      "int main(int argc, char **argv) {\n"
+      "  int require = 0;\n"
+      "  require += info_ver[argc];\n"
+      "#ifdef H5_HAVE_PARALLEL\n"
+      "  require += info_parallel[argc];\n"
+      "#endif\n"
+      "  H5File file(\"foo.h5\", H5F_ACC_TRUNC);\n"
+      "  return 0;\n"
+      "}")
+    try_compile(${success} ${scratch_directory} ${test_file}
+      COPY_FILE ${scratch_directory}/compiler_has_h5_cxx
+    )
+  endif()
+  if(${success})
+    file(STRINGS ${scratch_directory}/compiler_has_h5_cxx INFO_STRINGS
+      REGEX "^INFO:"
+    )
+    string(REGEX MATCH "^INFO:([0-9]+\\.[0-9]+\\.[0-9]+)(-patch([0-9]+))?"
+      INFO_VER "${INFO_STRINGS}"
+    )
+    set(${version} ${CMAKE_MATCH_1})
+    if(CMAKE_MATCH_3)
+      set(${version} ${HDF5_CXX_VERSION}.${CMAKE_MATCH_3})
+    endif()
+    set(${version} ${${version}} PARENT_SCOPE)
+
+    if(INFO_STRINGS MATCHES "INFO:PARALLEL")
+      set(${is_parallel} TRUE PARENT_SCOPE)
+    else()
+      set(${is_parallel} FALSE PARENT_SCOPE)
+    endif()
+  endif()
+endfunction()
+
+function(_HDF5_test_regular_compiler_Fortran success is_parallel)
+  if(NOT ${success})
+    set(scratch_directory
+      ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hdf5)
+    set(test_file ${scratch_directory}/cmake_hdf5_test.f90)
+    file(WRITE ${test_file}
+      "program hdf5_hello\n"
+      "  use hdf5\n"
+      "  use h5lt\n"
+      "  use h5ds\n"
+      "  integer error\n"
+      "  call h5open_f(error)\n"
+      "  call h5close_f(error)\n"
+      "end\n")
+    try_compile(${success} ${scratch_directory} ${test_file})
+    if(${success})
+      execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -showconfig
+        OUTPUT_VARIABLE config_output
+        ERROR_VARIABLE config_error
+        RESULT_VARIABLE config_result
+        )
+      if(config_output MATCHES "Parallel HDF5: yes")
+        set(${is_parallel} TRUE PARENT_SCOPE)
+      else()
+        set(${is_parallel} FALSE PARENT_SCOPE)
+      endif()
+    endif()
+  endif()
+endfunction()
+
+# Invoke the HDF5 wrapper compiler.  The compiler return value is stored to the
+# return_value argument, the text output is stored to the output variable.
+macro( _HDF5_invoke_compiler language output return_value version is_parallel)
+    set(${version})
+    if(HDF5_USE_STATIC_LIBRARIES)
+        set(lib_type_args -noshlib)
+    else()
+        set(lib_type_args -shlib)
+    endif()
+    set(scratch_dir ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hdf5)
+    if("${language}" STREQUAL "C")
+        set(test_file ${scratch_dir}/cmake_hdf5_test.c)
+    elseif("${language}" STREQUAL "CXX")
+        set(test_file ${scratch_dir}/cmake_hdf5_test.cxx)
+    elseif("${language}" STREQUAL "Fortran")
+        set(test_file ${scratch_dir}/cmake_hdf5_test.f90)
+    endif()
+    exec_program( ${HDF5_${language}_COMPILER_EXECUTABLE}
+        ARGS -show ${lib_type_args} ${test_file}
+        OUTPUT_VARIABLE ${output}
+        RETURN_VALUE ${return_value}
+    )
+    if(NOT ${${return_value}} EQUAL 0)
+        message(STATUS
+          "Unable to determine HDF5 ${language} flags from HDF5 wrapper.")
+    endif()
+    exec_program( ${HDF5_${language}_COMPILER_EXECUTABLE}
+        ARGS -showconfig
+        OUTPUT_VARIABLE config_output
+        RETURN_VALUE config_return
+    )
+    if(NOT ${return_value} EQUAL 0)
+        message( STATUS
+          "Unable to determine HDF5 ${language} version from HDF5 wrapper.")
+    endif()
+    string(REGEX MATCH "HDF5 Version: ([a-zA-Z0-9\\.\\-]*)" version_match "${config_output}")
+    if(version_match)
+        string(REPLACE "HDF5 Version: " "" ${version} "${version_match}")
+        string(REPLACE "-patch" "." ${version} "${${version}}")
+    endif()
+    if(config_output MATCHES "Parallel HDF5: yes")
+      set(${is_parallel} TRUE)
+    else()
+      set(${is_parallel} FALSE)
+    endif()
+endmacro()
+
+# Parse a compile line for definitions, includes, library paths, and libraries.
+macro( _HDF5_parse_compile_line
+    compile_line_var
+    include_paths
+    definitions
+    library_paths
+    libraries
+    libraries_hl)
+
+  if(UNIX)
+    separate_arguments(_HDF5_COMPILE_ARGS UNIX_COMMAND "${${compile_line_var}}")
+  else()
+    separate_arguments(_HDF5_COMPILE_ARGS WINDOWS_COMMAND "${${compile_line_var}}")
+  endif()
+
+  foreach(arg IN LISTS _HDF5_COMPILE_ARGS)
+    if("${arg}" MATCHES "^-I(.*)$")
+      # include directory
+      list(APPEND ${include_paths} "${CMAKE_MATCH_1}")
+    elseif("${arg}" MATCHES "^-D(.*)$")
+      # compile definition
+      list(APPEND ${definitions} "-D${CMAKE_MATCH_1}")
+    elseif("${arg}" MATCHES "^-L(.*)$")
+      # library search path
+      list(APPEND ${library_paths} "${CMAKE_MATCH_1}")
+    elseif("${arg}" MATCHES "^-l(hdf5.*hl.*)$")
+      # library name (hl)
+      list(APPEND ${libraries_hl} "${CMAKE_MATCH_1}")
+    elseif("${arg}" MATCHES "^-l(.*)$")
+      # library name
+      list(APPEND ${libraries} "${CMAKE_MATCH_1}")
+    elseif("${arg}" MATCHES "^(.:)?[/\\].*\\.(a|so|dylib|sl|lib)$")
+      # library file
+      if(NOT EXISTS "${arg}")
+        continue()
+      endif()
+      get_filename_component(_HDF5_LPATH "${arg}" DIRECTORY)
+      get_filename_component(_HDF5_LNAME "${arg}" NAME_WE)
+      string(REGEX REPLACE "^lib" "" _HDF5_LNAME "${_HDF5_LNAME}")
+      list(APPEND ${library_paths} "${_HDF5_LPATH}")
+      if(_HDF5_LNAME MATCHES "hdf5.*hl")
+        list(APPEND ${libraries_hl} "${_HDF5_LNAME}")
+      else()
+        list(APPEND ${libraries} "${_HDF5_LNAME}")
+      endif()
+    endif()
+  endforeach()
+endmacro()
+
+# Select a preferred imported configuration from a target
+function(_HDF5_select_imported_config target imported_conf)
+    # We will first assign the value to a local variable _imported_conf, then assign
+    # it to the function argument at the end.
+    get_target_property(_imported_conf ${target} MAP_IMPORTED_CONFIG_${CMAKE_BUILD_TYPE})
+    if (NOT _imported_conf)
+        # Get available imported configurations by examining target properties
+        get_target_property(_imported_conf ${target} IMPORTED_CONFIGURATIONS)
+        if(HDF5_FIND_DEBUG)
+            message(STATUS "Found imported configurations: ${_imported_conf}")
+        endif()
+        # Find the imported configuration that we prefer.
+        # We do this by making list of configurations in order of preference,
+        # starting with ${CMAKE_BUILD_TYPE} and ending with the first imported_conf
+        set(_preferred_confs ${CMAKE_BUILD_TYPE})
+        list(GET _imported_conf 0 _fallback_conf)
+        list(APPEND _preferred_confs RELWITHDEBINFO RELEASE DEBUG ${_fallback_conf})
+        if(HDF5_FIND_DEBUG)
+            message(STATUS "Start search through imported configurations in the following order: ${_preferred_confs}")
+        endif()
+        # Now find the first of these that is present in imported_conf
+        cmake_policy(PUSH)
+        cmake_policy(SET CMP0057 NEW) # support IN_LISTS
+        foreach (_conf IN LISTS _preferred_confs)
+            if (${_conf} IN_LIST _imported_conf)
+               set(_imported_conf ${_conf})
+               break()
+            endif()
+        endforeach()
+        cmake_policy(POP)
+    endif()
+    if(HDF5_FIND_DEBUG)
+        message(STATUS "Selected imported configuration: ${_imported_conf}")
+    endif()
+    # assign value to function argument
+    set(${imported_conf} ${_imported_conf} PARENT_SCOPE)
+endfunction()
+
+
+if(NOT HDF5_ROOT)
+    set(HDF5_ROOT $ENV{HDF5_ROOT})
+endif()
+if(HDF5_ROOT)
+    set(_HDF5_SEARCH_OPTS NO_DEFAULT_PATH)
+else()
+    set(_HDF5_SEARCH_OPTS)
+endif()
+
+# Try to find HDF5 using an installed hdf5-config.cmake
+if(NOT HDF5_FOUND AND NOT HDF5_NO_FIND_PACKAGE_CONFIG_FILE)
+    find_package(HDF5 QUIET NO_MODULE
+      HINTS ${HDF5_ROOT}
+      ${_HDF5_SEARCH_OPTS}
+      )
+    if( HDF5_FOUND)
+        if(HDF5_FIND_DEBUG)
+            message(STATUS "Found HDF5 at ${HDF5_DIR} via NO_MODULE. Now trying to extract locations etc.")
+        endif()
+        set(HDF5_IS_PARALLEL ${HDF5_ENABLE_PARALLEL})
+        set(HDF5_INCLUDE_DIRS ${HDF5_INCLUDE_DIR})
+        set(HDF5_LIBRARIES)
+        if (NOT TARGET hdf5 AND NOT TARGET hdf5-static AND NOT TARGET hdf5-shared)
+            # Some HDF5 versions (e.g. 1.8.18) used hdf5::hdf5 etc
+            set(_target_prefix "hdf5::")
+        endif()
+        set(HDF5_C_TARGET ${_target_prefix}hdf5)
+        set(HDF5_C_HL_TARGET ${_target_prefix}hdf5_hl)
+        set(HDF5_CXX_TARGET ${_target_prefix}hdf5_cpp)
+        set(HDF5_CXX_HL_TARGET ${_target_prefix}hdf5_hl_cpp)
+        set(HDF5_Fortran_TARGET ${_target_prefix}hdf5_fortran)
+        set(HDF5_Fortran_HL_TARGET ${_target_prefix}hdf5_hl_fortran)
+        set(HDF5_DEFINITIONS "")
+        if(HDF5_USE_STATIC_LIBRARIES)
+            set(_suffix "-static")
+        else()
+            set(_suffix "-shared")
+        endif()
+        foreach(_lang ${HDF5_LANGUAGE_BINDINGS})
+
+            #Older versions of hdf5 don't have a static/shared suffix so
+            #if we detect that occurrence clear the suffix
+            if(_suffix AND NOT TARGET ${HDF5_${_lang}_TARGET}${_suffix})
+              if(NOT TARGET ${HDF5_${_lang}_TARGET})
+                #cant find this component with or without the suffix
+                #so bail out, and let the following locate HDF5
+                set(HDF5_FOUND FALSE)
+                break()
+              endif()
+              set(_suffix "")
+            endif()
+
+            if(HDF5_FIND_DEBUG)
+                message(STATUS "Trying to get properties of target ${HDF5_${_lang}_TARGET}${_suffix}")
+            endif()
+            # Find library for this target. Complicated as on Windows with a DLL, we need to search for the import-lib.
+            _HDF5_select_imported_config(${HDF5_${_lang}_TARGET}${_suffix} _hdf5_imported_conf)
+            get_target_property(_hdf5_lang_location ${HDF5_${_lang}_TARGET}${_suffix} IMPORTED_IMPLIB_${_hdf5_imported_conf} )
+            if (NOT _hdf5_lang_location)
+                # no import lib, just try LOCATION
+                get_target_property(_hdf5_lang_location ${HDF5_${_lang}_TARGET}${_suffix} LOCATION_${_hdf5_imported_conf})
+                if (NOT _hdf5_lang_location)
+                    get_target_property(_hdf5_lang_location ${HDF5_${_lang}_TARGET}${_suffix} LOCATION)
+                endif()
+            endif()
+            if( _hdf5_lang_location )
+                set(HDF5_${_lang}_LIBRARY ${_hdf5_lang_location})
+                list(APPEND HDF5_LIBRARIES ${HDF5_${_lang}_TARGET}${_suffix})
+                set(HDF5_${_lang}_LIBRARIES ${HDF5_${_lang}_TARGET}${_suffix})
+                set(HDF5_${_lang}_FOUND True)
+            endif()
+            if(FIND_HL)
+                get_target_property(__lang_hl_location ${HDF5_${_lang}_HL_TARGET}${_suffix} IMPORTED_IMPLIB_${_hdf5_imported_conf} )
+                if (NOT _hdf5_lang_hl_location)
+                    get_target_property(_hdf5_lang_hl_location ${HDF5_${_lang}_HL_TARGET}${_suffix} LOCATION_${_hdf5_imported_conf})
+                    if (NOT _hdf5_hl_lang_location)
+                        get_target_property(_hdf5_hl_lang_location ${HDF5_${_lang}_HL_TARGET}${_suffix} LOCATION)
+                    endif()
+                endif()
+                if( _hdf5_lang_hl_location )
+                    set(HDF5_${_lang}_HL_LIBRARY ${_hdf5_lang_hl_location})
+                    list(APPEND HDF5_HL_LIBRARIES ${HDF5_${_lang}_HL_TARGET}${_suffix})
+                    set(HDF5_${_lang}_HL_LIBRARIES ${HDF5_${_lang}_HL_TARGET}${_suffix})
+                    set(HDF5_HL_FOUND True)
+                endif()
+                unset(_hdf5_lang_hl_location)
+            endif()
+            unset(_hdf5_imported_conf)
+            unset(_hdf5_lang_location)
+        endforeach()
+    endif()
+endif()
+
+if(NOT HDF5_FOUND)
+  set(_HDF5_NEED_TO_SEARCH False)
+  set(HDF5_COMPILER_NO_INTERROGATE True)
+  # Only search for languages we've enabled
+  foreach(__lang IN LISTS HDF5_LANGUAGE_BINDINGS)
+    # First check to see if our regular compiler is one of wrappers
+    if(__lang STREQUAL "C")
+      _HDF5_test_regular_compiler_C(
+        HDF5_${__lang}_COMPILER_NO_INTERROGATE
+        HDF5_${__lang}_VERSION
+        HDF5_${__lang}_IS_PARALLEL)
+    elseif(__lang STREQUAL "CXX")
+      _HDF5_test_regular_compiler_CXX(
+        HDF5_${__lang}_COMPILER_NO_INTERROGATE
+        HDF5_${__lang}_VERSION
+        HDF5_${__lang}_IS_PARALLEL)
+    elseif(__lang STREQUAL "Fortran")
+      _HDF5_test_regular_compiler_Fortran(
+        HDF5_${__lang}_COMPILER_NO_INTERROGATE
+        HDF5_${__lang}_IS_PARALLEL)
+    else()
+      continue()
+    endif()
+    if(HDF5_${__lang}_COMPILER_NO_INTERROGATE)
+      message(STATUS "HDF5: Using hdf5 compiler wrapper for all ${__lang} compiling")
+      set(HDF5_${__lang}_FOUND True)
+      set(HDF5_${__lang}_COMPILER_EXECUTABLE_NO_INTERROGATE
+          "${CMAKE_${__lang}_COMPILER}"
+          CACHE FILEPATH "HDF5 ${__lang} compiler wrapper")
+      set(HDF5_${__lang}_DEFINITIONS)
+      set(HDF5_${__lang}_INCLUDE_DIRS)
+      set(HDF5_${__lang}_LIBRARIES)
+      set(HDF5_${__lang}_HL_LIBRARIES)
+
+      mark_as_advanced(HDF5_${__lang}_COMPILER_EXECUTABLE_NO_INTERROGATE)
+
+      set(HDF5_${__lang}_FOUND True)
+      set(HDF5_HL_FOUND True)
+    else()
+      set(HDF5_COMPILER_NO_INTERROGATE False)
+      # If this language isn't using the wrapper, then try to seed the
+      # search options with the wrapper
+      find_program(HDF5_${__lang}_COMPILER_EXECUTABLE
+        NAMES ${HDF5_${__lang}_COMPILER_NAMES} NAMES_PER_DIR
+        HINTS ${HDF5_ROOT}
+        PATH_SUFFIXES bin Bin
+        DOC "HDF5 ${__lang} Wrapper compiler.  Used only to detect HDF5 compile flags."
+        ${_HDF5_SEARCH_OPTS}
+      )
+      mark_as_advanced( HDF5_${__lang}_COMPILER_EXECUTABLE )
+      unset(HDF5_${__lang}_COMPILER_NAMES)
+
+      if(HDF5_${__lang}_COMPILER_EXECUTABLE)
+        _HDF5_invoke_compiler(${__lang} HDF5_${__lang}_COMPILE_LINE
+          HDF5_${__lang}_RETURN_VALUE HDF5_${__lang}_VERSION HDF5_${__lang}_IS_PARALLEL)
+        if(HDF5_${__lang}_RETURN_VALUE EQUAL 0)
+          message(STATUS "HDF5: Using hdf5 compiler wrapper to determine ${__lang} configuration")
+          _HDF5_parse_compile_line( HDF5_${__lang}_COMPILE_LINE
+            HDF5_${__lang}_INCLUDE_DIRS
+            HDF5_${__lang}_DEFINITIONS
+            HDF5_${__lang}_LIBRARY_DIRS
+            HDF5_${__lang}_LIBRARY_NAMES
+            HDF5_${__lang}_HL_LIBRARY_NAMES
+          )
+          set(HDF5_${__lang}_LIBRARIES)
+
+          foreach(L IN LISTS HDF5_${__lang}_LIBRARY_NAMES)
+            set(_HDF5_SEARCH_NAMES_LOCAL)
+            if("x${L}" MATCHES "hdf5")
+              # hdf5 library
+              set(_HDF5_SEARCH_OPTS_LOCAL ${_HDF5_SEARCH_OPTS})
+              if(HDF5_USE_STATIC_LIBRARIES)
+                if(WIN32)
+                  set(_HDF5_SEARCH_NAMES_LOCAL lib${L})
+                else()
+                  set(_HDF5_SEARCH_NAMES_LOCAL lib${L}.a)
+                endif()
+              endif()
+            else()
+              # external library
+              set(_HDF5_SEARCH_OPTS_LOCAL)
+            endif()
+            find_library(HDF5_${__lang}_LIBRARY_${L}
+              NAMES ${_HDF5_SEARCH_NAMES_LOCAL} ${L} NAMES_PER_DIR
+              HINTS ${HDF5_${__lang}_LIBRARY_DIRS}
+                    ${HDF5_ROOT}
+              ${_HDF5_SEARCH_OPTS_LOCAL}
+              )
+            unset(_HDF5_SEARCH_OPTS_LOCAL)
+            unset(_HDF5_SEARCH_NAMES_LOCAL)
+            if(HDF5_${__lang}_LIBRARY_${L})
+              list(APPEND HDF5_${__lang}_LIBRARIES ${HDF5_${__lang}_LIBRARY_${L}})
+            else()
+              list(APPEND HDF5_${__lang}_LIBRARIES ${L})
+            endif()
+          endforeach()
+          if(FIND_HL)
+            set(HDF5_${__lang}_HL_LIBRARIES)
+            foreach(L IN LISTS HDF5_${__lang}_HL_LIBRARY_NAMES)
+              set(_HDF5_SEARCH_NAMES_LOCAL)
+              if("x${L}" MATCHES "hdf5")
+                # hdf5 library
+                set(_HDF5_SEARCH_OPTS_LOCAL ${_HDF5_SEARCH_OPTS})
+                if(HDF5_USE_STATIC_LIBRARIES)
+                  if(WIN32)
+                    set(_HDF5_SEARCH_NAMES_LOCAL lib${L})
+                  else()
+                    set(_HDF5_SEARCH_NAMES_LOCAL lib${L}.a)
+                  endif()
+                endif()
+              else()
+                # external library
+                set(_HDF5_SEARCH_OPTS_LOCAL)
+              endif()
+              find_library(HDF5_${__lang}_LIBRARY_${L}
+                NAMES ${_HDF5_SEARCH_NAMES_LOCAL} ${L} NAMES_PER_DIR
+                HINTS ${HDF5_${__lang}_LIBRARY_DIRS}
+                      ${HDF5_ROOT}
+                ${_HDF5_SEARCH_OPTS_LOCAL}
+                )
+              unset(_HDF5_SEARCH_OPTS_LOCAL)
+              unset(_HDF5_SEARCH_NAMES_LOCAL)
+              if(HDF5_${__lang}_LIBRARY_${L})
+                list(APPEND HDF5_${__lang}_HL_LIBRARIES ${HDF5_${__lang}_LIBRARY_${L}})
+              else()
+                list(APPEND HDF5_${__lang}_HL_LIBRARIES ${L})
+              endif()
+            endforeach()
+            set(HDF5_HL_FOUND True)
+          endif()
+
+          set(HDF5_${__lang}_FOUND True)
+          _HDF5_remove_duplicates_from_beginning(HDF5_${__lang}_DEFINITIONS)
+          _HDF5_remove_duplicates_from_beginning(HDF5_${__lang}_INCLUDE_DIRS)
+          _HDF5_remove_duplicates_from_beginning(HDF5_${__lang}_LIBRARIES)
+          _HDF5_remove_duplicates_from_beginning(HDF5_${__lang}_HL_LIBRARIES)
+        else()
+          set(_HDF5_NEED_TO_SEARCH True)
+        endif()
+      else()
+        set(_HDF5_NEED_TO_SEARCH True)
+      endif()
+    endif()
+    if(HDF5_${__lang}_VERSION)
+      if(NOT HDF5_VERSION)
+        set(HDF5_VERSION ${HDF5_${__lang}_VERSION})
+      elseif(NOT HDF5_VERSION VERSION_EQUAL HDF5_${__lang}_VERSION)
+        message(WARNING "HDF5 Version found for language ${__lang}, ${HDF5_${__lang}_VERSION} is different than previously found version ${HDF5_VERSION}")
+      endif()
+    endif()
+    if(DEFINED HDF5_${__lang}_IS_PARALLEL)
+      if(NOT DEFINED HDF5_IS_PARALLEL)
+        set(HDF5_IS_PARALLEL ${HDF5_${__lang}_IS_PARALLEL})
+      elseif(NOT HDF5_IS_PARALLEL AND HDF5_${__lang}_IS_PARALLEL)
+        message(WARNING "HDF5 found for language ${__lang} is parallel but previously found language is not parallel.")
+      elseif(HDF5_IS_PARALLEL AND NOT HDF5_${__lang}_IS_PARALLEL)
+        message(WARNING "HDF5 found for language ${__lang} is not parallel but previously found language is parallel.")
+      endif()
+    endif()
+  endforeach()
+else()
+  set(_HDF5_NEED_TO_SEARCH True)
+endif()
+
+if(NOT HDF5_FOUND AND HDF5_COMPILER_NO_INTERROGATE)
+  # No arguments necessary, all languages can use the compiler wrappers
+  set(HDF5_FOUND True)
+  set(HDF5_METHOD "Included by compiler wrappers")
+  set(HDF5_REQUIRED_VARS HDF5_METHOD)
+elseif(NOT HDF5_FOUND AND NOT _HDF5_NEED_TO_SEARCH)
+  # Compiler wrappers aren't being used by the build but were found and used
+  # to determine necessary include and library flags
+  set(HDF5_INCLUDE_DIRS)
+  set(HDF5_LIBRARIES)
+  set(HDF5_HL_LIBRARIES)
+  foreach(__lang IN LISTS HDF5_LANGUAGE_BINDINGS)
+    if(HDF5_${__lang}_FOUND)
+      if(NOT HDF5_${__lang}_COMPILER_NO_INTERROGATE)
+        list(APPEND HDF5_DEFINITIONS ${HDF5_${__lang}_DEFINITIONS})
+        list(APPEND HDF5_INCLUDE_DIRS ${HDF5_${__lang}_INCLUDE_DIRS})
+        list(APPEND HDF5_LIBRARIES ${HDF5_${__lang}_LIBRARIES})
+        if(FIND_HL)
+          list(APPEND HDF5_HL_LIBRARIES ${HDF5_${__lang}_HL_LIBRARIES})
+        endif()
+      endif()
+    endif()
+  endforeach()
+  _HDF5_remove_duplicates_from_beginning(HDF5_DEFINITIONS)
+  _HDF5_remove_duplicates_from_beginning(HDF5_INCLUDE_DIRS)
+  _HDF5_remove_duplicates_from_beginning(HDF5_LIBRARIES)
+  _HDF5_remove_duplicates_from_beginning(HDF5_HL_LIBRARIES)
+  set(HDF5_FOUND True)
+  set(HDF5_REQUIRED_VARS HDF5_LIBRARIES)
+  if(FIND_HL)
+    list(APPEND HDF5_REQUIRED_VARS HDF5_HL_LIBRARIES)
+  endif()
+endif()
+
+find_program( HDF5_DIFF_EXECUTABLE
+    NAMES h5diff
+    HINTS ${HDF5_ROOT}
+    PATH_SUFFIXES bin Bin
+    ${_HDF5_SEARCH_OPTS}
+    DOC "HDF5 file differencing tool." )
+mark_as_advanced( HDF5_DIFF_EXECUTABLE )
+
+if( NOT HDF5_FOUND )
+    # seed the initial lists of libraries to find with items we know we need
+    set(HDF5_C_LIBRARY_NAMES          hdf5)
+    set(HDF5_C_HL_LIBRARY_NAMES       hdf5_hl)
+
+    set(HDF5_CXX_LIBRARY_NAMES        hdf5_cpp    ${HDF5_C_LIBRARY_NAMES})
+    set(HDF5_CXX_HL_LIBRARY_NAMES     hdf5_hl_cpp ${HDF5_C_HL_LIBRARY_NAMES} ${HDF5_CXX_LIBRARY_NAMES})
+
+    set(HDF5_Fortran_LIBRARY_NAMES    hdf5_fortran   ${HDF5_C_LIBRARY_NAMES})
+    set(HDF5_Fortran_HL_LIBRARY_NAMES hdf5hl_fortran ${HDF5_C_HL_LIBRARY_NAMES} ${HDF5_Fortran_LIBRARY_NAMES})
+
+    foreach(__lang IN LISTS HDF5_LANGUAGE_BINDINGS)
+        # find the HDF5 include directories
+        if("${__lang}" STREQUAL "Fortran")
+            set(HDF5_INCLUDE_FILENAME hdf5.mod)
+        elseif("${__lang}" STREQUAL "CXX")
+            set(HDF5_INCLUDE_FILENAME H5Cpp.h)
+        else()
+            set(HDF5_INCLUDE_FILENAME hdf5.h)
+        endif()
+
+        find_path(HDF5_${__lang}_INCLUDE_DIR ${HDF5_INCLUDE_FILENAME}
+            HINTS ${HDF5_ROOT}
+            PATHS $ENV{HOME}/.local/include
+            PATH_SUFFIXES include Include
+            ${_HDF5_SEARCH_OPTS}
+        )
+        mark_as_advanced(HDF5_${__lang}_INCLUDE_DIR)
+        # set the _DIRS variable as this is what the user will normally use
+        set(HDF5_${__lang}_INCLUDE_DIRS ${HDF5_${__lang}_INCLUDE_DIR})
+        list(APPEND HDF5_INCLUDE_DIRS ${HDF5_${__lang}_INCLUDE_DIR})
+
+        # find the HDF5 libraries
+        foreach(LIB IN LISTS HDF5_${__lang}_LIBRARY_NAMES)
+            if(HDF5_USE_STATIC_LIBRARIES)
+                # According to bug 1643 on the CMake bug tracker, this is the
+                # preferred method for searching for a static library.
+                # See https://gitlab.kitware.com/cmake/cmake/issues/1643.  We search
+                # first for the full static library name, but fall back to a
+                # generic search on the name if the static search fails.
+                set( THIS_LIBRARY_SEARCH_DEBUG
+                    lib${LIB}d.a lib${LIB}_debug.a lib${LIB}d lib${LIB}_D lib${LIB}_debug
+                    lib${LIB}d-static.a lib${LIB}_debug-static.a ${LIB}d-static ${LIB}_D-static ${LIB}_debug-static )
+                set( THIS_LIBRARY_SEARCH_RELEASE lib${LIB}.a lib${LIB} lib${LIB}-static.a ${LIB}-static)
+            else()
+                set( THIS_LIBRARY_SEARCH_DEBUG ${LIB}d ${LIB}_D ${LIB}_debug ${LIB}d-shared ${LIB}_D-shared ${LIB}_debug-shared)
+                set( THIS_LIBRARY_SEARCH_RELEASE ${LIB} ${LIB}-shared)
+                if(WIN32)
+                  list(APPEND HDF5_DEFINITIONS "-DH5_BUILT_AS_DYNAMIC_LIB")
+                endif()
+            endif()
+            find_library(HDF5_${LIB}_LIBRARY_DEBUG
+                NAMES ${THIS_LIBRARY_SEARCH_DEBUG}
+                HINTS ${HDF5_ROOT} PATH_SUFFIXES lib Lib
+                ${_HDF5_SEARCH_OPTS}
+            )
+            find_library( HDF5_${LIB}_LIBRARY_RELEASE
+                NAMES ${THIS_LIBRARY_SEARCH_RELEASE}
+                HINTS ${HDF5_ROOT} PATH_SUFFIXES lib Lib
+                ${_HDF5_SEARCH_OPTS}
+            )
+            select_library_configurations( HDF5_${LIB} )
+            list(APPEND HDF5_${__lang}_LIBRARIES ${HDF5_${LIB}_LIBRARY})
+        endforeach()
+        if(HDF5_${__lang}_LIBRARIES)
+            set(HDF5_${__lang}_FOUND True)
+        endif()
+
+        # Append the libraries for this language binding to the list of all
+        # required libraries.
+        list(APPEND HDF5_LIBRARIES ${HDF5_${__lang}_LIBRARIES})
+
+        if(FIND_HL)
+            foreach(LIB IN LISTS HDF5_${__lang}_HL_LIBRARY_NAMES)
+                if(HDF5_USE_STATIC_LIBRARIES)
+                    # According to bug 1643 on the CMake bug tracker, this is the
+                    # preferred method for searching for a static library.
+                    # See https://gitlab.kitware.com/cmake/cmake/issues/1643.  We search
+                    # first for the full static library name, but fall back to a
+                    # generic search on the name if the static search fails.
+                    set( THIS_LIBRARY_SEARCH_DEBUG
+                        lib${LIB}d.a lib${LIB}_debug.a lib${LIB}d lib${LIB}_D lib${LIB}_debug
+                        lib${LIB}d-static.a lib${LIB}_debug-static.a lib${LIB}d-static lib${LIB}_D-static lib${LIB}_debug-static )
+                    set( THIS_LIBRARY_SEARCH_RELEASE lib${LIB}.a ${LIB} lib${LIB}-static.a lib${LIB}-static)
+                else()
+                    set( THIS_LIBRARY_SEARCH_DEBUG ${LIB}d ${LIB}_D ${LIB}_debug ${LIB}d-shared ${LIB}_D-shared ${LIB}_debug-shared)
+                    set( THIS_LIBRARY_SEARCH_RELEASE ${LIB} ${LIB}-shared)
+                endif()
+                find_library(HDF5_${LIB}_LIBRARY_DEBUG
+                    NAMES ${THIS_LIBRARY_SEARCH_DEBUG}
+                    HINTS ${HDF5_ROOT} PATH_SUFFIXES lib Lib
+                    ${_HDF5_SEARCH_OPTS}
+                )
+                find_library( HDF5_${LIB}_LIBRARY_RELEASE
+                    NAMES ${THIS_LIBRARY_SEARCH_RELEASE}
+                    HINTS ${HDF5_ROOT} PATH_SUFFIXES lib Lib
+                    ${_HDF5_SEARCH_OPTS}
+                )
+                select_library_configurations( HDF5_${LIB} )
+                list(APPEND HDF5_${__lang}_HL_LIBRARIES ${HDF5_${LIB}_LIBRARY})
+            endforeach()
+
+            # Append the libraries for this language binding to the list of all
+            # required libraries.
+            list(APPEND HDF5_HL_LIBRARIES ${HDF5_${__lang}_HL_LIBRARIES})
+        endif()
+    endforeach()
+    if(FIND_HL AND HDF5_HL_LIBRARIES)
+        set(HDF5_HL_FOUND True)
+    endif()
+
+    _HDF5_remove_duplicates_from_beginning(HDF5_DEFINITIONS)
+    _HDF5_remove_duplicates_from_beginning(HDF5_INCLUDE_DIRS)
+    _HDF5_remove_duplicates_from_beginning(HDF5_LIBRARIES)
+    _HDF5_remove_duplicates_from_beginning(HDF5_HL_LIBRARIES)
+
+    # If the HDF5 include directory was found, open H5pubconf.h to determine if
+    # HDF5 was compiled with parallel IO support
+    set( HDF5_IS_PARALLEL FALSE )
+    set( HDF5_VERSION "" )
+    foreach( _dir IN LISTS HDF5_INCLUDE_DIRS )
+      foreach(_hdr "${_dir}/H5pubconf.h" "${_dir}/H5pubconf-64.h" "${_dir}/H5pubconf-32.h")
+        if( EXISTS "${_hdr}" )
+            file( STRINGS "${_hdr}"
+                HDF5_HAVE_PARALLEL_DEFINE
+                REGEX "HAVE_PARALLEL 1" )
+            if( HDF5_HAVE_PARALLEL_DEFINE )
+                set( HDF5_IS_PARALLEL TRUE )
+            endif()
+            unset(HDF5_HAVE_PARALLEL_DEFINE)
+
+            file( STRINGS "${_hdr}"
+                HDF5_VERSION_DEFINE
+                REGEX "^[ \t]*#[ \t]*define[ \t]+H5_VERSION[ \t]+" )
+            if( "${HDF5_VERSION_DEFINE}" MATCHES
+                "H5_VERSION[ \t]+\"([0-9]+\\.[0-9]+\\.[0-9]+)(-patch([0-9]+))?\"" )
+                set( HDF5_VERSION "${CMAKE_MATCH_1}" )
+                if( CMAKE_MATCH_3 )
+                  set( HDF5_VERSION ${HDF5_VERSION}.${CMAKE_MATCH_3})
+                endif()
+            endif()
+            unset(HDF5_VERSION_DEFINE)
+        endif()
+      endforeach()
+    endforeach()
+    set( HDF5_IS_PARALLEL ${HDF5_IS_PARALLEL} CACHE BOOL
+        "HDF5 library compiled with parallel IO support" )
+    mark_as_advanced( HDF5_IS_PARALLEL )
+
+    set(HDF5_REQUIRED_VARS HDF5_LIBRARIES HDF5_INCLUDE_DIRS)
+    if(FIND_HL)
+        list(APPEND HDF5_REQUIRED_VARS HDF5_HL_LIBRARIES)
+    endif()
+endif()
+
+# For backwards compatibility we set HDF5_INCLUDE_DIR to the value of
+# HDF5_INCLUDE_DIRS
+if( HDF5_INCLUDE_DIRS )
+  set( HDF5_INCLUDE_DIR "${HDF5_INCLUDE_DIRS}" )
+endif()
+
+# If HDF5_REQUIRED_VARS is empty at this point, then it's likely that
+# something external is trying to explicitly pass already found
+# locations
+if(NOT HDF5_REQUIRED_VARS)
+    set(HDF5_REQUIRED_VARS HDF5_LIBRARIES HDF5_INCLUDE_DIRS)
+endif()
+
+find_package_handle_standard_args(HDF5
+    REQUIRED_VARS ${HDF5_REQUIRED_VARS}
+    VERSION_VAR   HDF5_VERSION
+    HANDLE_COMPONENTS
+)
+
+unset(_HDF5_SEARCH_OPTS)
+
+if( HDF5_FOUND AND NOT HDF5_DIR)
+  # hide HDF5_DIR for the non-advanced user to avoid confusion with
+  # HDF5_DIR-NOT_FOUND while HDF5 was found.
+  mark_as_advanced(HDF5_DIR)
+endif()
+
+if (HDF5_FIND_DEBUG)
+  message(STATUS "HDF5_DIR: ${HDF5_DIR}")
+  message(STATUS "HDF5_DEFINITIONS: ${HDF5_DEFINITIONS}")
+  message(STATUS "HDF5_INCLUDE_DIRS: ${HDF5_INCLUDE_DIRS}")
+  message(STATUS "HDF5_LIBRARIES: ${HDF5_LIBRARIES}")
+  message(STATUS "HDF5_HL_LIBRARIES: ${HDF5_HL_LIBRARIES}")
+  foreach(__lang IN LISTS HDF5_LANGUAGE_BINDINGS)
+    message(STATUS "HDF5_${__lang}_DEFINITIONS: ${HDF5_${__lang}_DEFINITIONS}")
+    message(STATUS "HDF5_${__lang}_INCLUDE_DIR: ${HDF5_${__lang}_INCLUDE_DIR}")
+    message(STATUS "HDF5_${__lang}_INCLUDE_DIRS: ${HDF5_${__lang}_INCLUDE_DIRS}")
+    message(STATUS "HDF5_${__lang}_LIBRARY: ${HDF5_${__lang}_LIBRARY}")
+    message(STATUS "HDF5_${__lang}_LIBRARIES: ${HDF5_${__lang}_LIBRARIES}")
+    message(STATUS "HDF5_${__lang}_HL_LIBRARY: ${HDF5_${__lang}_HL_LIBRARY}")
+    message(STATUS "HDF5_${__lang}_HL_LIBRARIES: ${HDF5_${__lang}_HL_LIBRARIES}")
+  endforeach()
+endif()
diff --git a/cmake/upstream/GoogleTest.cmake b/cmake/upstream/GoogleTest.cmake
index 944070ae16e2e37837893881d8be32bbdd2b8dc1..ba09bf6b278fd2cb4527a4fb249d569f9b2a8de3 100644
--- a/cmake/upstream/GoogleTest.cmake
+++ b/cmake/upstream/GoogleTest.cmake
@@ -14,6 +14,7 @@ This module defines functions to help use the Google Test infrastructure.
 
     gtest_add_tests(TARGET target
                     [SOURCES src1...]
+                    [EXEC_WRAPPER wrapper1...]
                     [EXTRA_ARGS arg1...]
                     [WORKING_DIRECTORY dir]
                     [TEST_PREFIX prefix]
@@ -31,6 +32,10 @@ This module defines functions to help use the Google Test infrastructure.
     this option is not given, the :prop_tgt:`SOURCES` property of the
     specified ``target`` will be used to obtain the list of sources.
 
+  ``EXEC_WRAPPER wrapper1...``
+    Any extra arguments to pass on the command line before each test case. This
+    can be userful when the test case should be run in parallel.
+
   ``EXTRA_ARGS arg1...``
     Any extra arguments to pass on the command line to each test case.
 
@@ -66,6 +71,7 @@ This module defines functions to help use the Google Test infrastructure.
     include(GoogleTest)
     add_executable(FooTest FooUnitTest.cxx)
     gtest_add_tests(TARGET      FooTest
+                    EXEC_WRAPPER mpirun -n 2
                     TEST_SUFFIX .noArgs
                     TEST_LIST   noArgsTests
     )
@@ -119,6 +125,7 @@ function(gtest_add_tests)
   )
   set(multiValueArgs
       SOURCES
+      EXEC_WRAPPER
       EXTRA_ARGS
   )
   set(allKeywords ${options} ${oneValueArgs} ${multiValueArgs})
@@ -182,6 +189,13 @@ function(gtest_add_tests)
         continue()
       endif()
 
+      # Wrap the test executable in another command if necessary
+      if (ARGS_EXEC_WRAPPER)
+        set(ctest_test_command ${ARGS_EXEC_WRAPPER} $<TARGET_FILE:${ARGS_TARGET}>)
+      else()
+        set(ctest_test_command ${ARGS_TARGET})
+      endif()
+
       # Make sure tests disabled in GTest get disabled in CTest
       if(gtest_test_name MATCHES "(^|\\.)DISABLED_")
         # Add the disabled test if CMake is new enough
@@ -198,7 +212,7 @@ function(gtest_add_tests)
           )
           add_test(NAME ${ctest_test_name}
                    ${workDir}
-                   COMMAND ${ARGS_TARGET}
+                   COMMAND ${ctest_test_command}
                      --gtest_also_run_disabled_tests
                      --gtest_filter=${gtest_test_name}
                      ${ARGS_EXTRA_ARGS}
@@ -210,7 +224,7 @@ function(gtest_add_tests)
         set(ctest_test_name ${ARGS_TEST_PREFIX}${gtest_test_name}${ARGS_TEST_SUFFIX})
         add_test(NAME ${ctest_test_name}
                  ${workDir}
-                 COMMAND ${ARGS_TARGET}
+                 COMMAND ${ctest_test_command}
                    --gtest_filter=${gtest_test_name}
                    ${ARGS_EXTRA_ARGS}
         )
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 7f606ea7fa4baf827cb634a1a8a63cd710ee4b26..978bc0d91a1c894ac29673554651ba46eb0669a7 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -6,6 +6,7 @@
 add_subdirectory(basics)
 add_subdirectory(hello)
 add_subdirectory(heatTransfer)
+add_subdirectory(plugins)
 
 if(ADIOS2_BUILD_EXAMPLES_EXPERIMENTAL)
   add_subdirectory(experimental)
diff --git a/examples/basics/globalArray/CMakeLists.txt b/examples/basics/globalArray/CMakeLists.txt
index f9a9b07dd5bbc1c0361f0fe4930f2b0efcf76280..62192593245e554fb315dc3d56c18994d4661905 100644
--- a/examples/basics/globalArray/CMakeLists.txt
+++ b/examples/basics/globalArray/CMakeLists.txt
@@ -7,7 +7,6 @@ add_executable(globalArray_write globalArray_write.cpp)
 target_link_libraries(globalArray_write adios2)
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
   target_include_directories(globalArray_write PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(globalArray_write ${MPI_C_LIBRARIES})
 endif()
diff --git a/examples/basics/joinedArray/CMakeLists.txt b/examples/basics/joinedArray/CMakeLists.txt
index f8d775e84b3e6d09f3fb8aa2a8f8d67fc82e5c94..a0a7b06413911738666e6b968653cdb831ff9e26 100644
--- a/examples/basics/joinedArray/CMakeLists.txt
+++ b/examples/basics/joinedArray/CMakeLists.txt
@@ -7,7 +7,6 @@ add_executable(joinedArray_write joinedArray_write.cpp)
 target_link_libraries(joinedArray_write adios2)
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
   target_include_directories(joinedArray_write PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(joinedArray_write ${MPI_C_LIBRARIES})
 endif()
diff --git a/examples/basics/localArray/CMakeLists.txt b/examples/basics/localArray/CMakeLists.txt
index 3f46669a629802cc351bded5f4b4deb5df5d2bed..a42506de02aa74c4f4fef3c99c5c1e50e49ad19a 100644
--- a/examples/basics/localArray/CMakeLists.txt
+++ b/examples/basics/localArray/CMakeLists.txt
@@ -7,7 +7,6 @@ add_executable(localArray_write localArray_write.cpp)
 target_link_libraries(localArray_write adios2)
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
   target_include_directories(localArray_write PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(localArray_write ${MPI_C_LIBRARIES})
 endif()
diff --git a/examples/basics/values/CMakeLists.txt b/examples/basics/values/CMakeLists.txt
index 619fdc326200801d35c065ab42966996308600d3..79e27ea6077d0e3e04400245a5e3e13409ddb09b 100644
--- a/examples/basics/values/CMakeLists.txt
+++ b/examples/basics/values/CMakeLists.txt
@@ -7,7 +7,6 @@ add_executable(values_write values_write.cpp)
 target_link_libraries(values_write adios2)
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
   target_include_directories(values_write PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(values_write ${MPI_C_LIBRARIES})
 endif()
diff --git a/examples/experimental/multistep/CMakeLists.txt b/examples/experimental/multistep/CMakeLists.txt
index b5f75bb22f3288c69952fbe19fdb49be2f1ef53e..546074f51d6dc87c55e5ba822778e792953ae093 100644
--- a/examples/experimental/multistep/CMakeLists.txt
+++ b/examples/experimental/multistep/CMakeLists.txt
@@ -6,19 +6,12 @@
 add_executable(writer_multistep writer_multistep.cpp)
 add_executable(reader_stepping reader_stepping.cpp)
 add_executable(reader_allsteps reader_allsteps.cpp)
-target_link_libraries(writer_multistep adios2)
-target_link_libraries(reader_stepping adios2)
-target_link_libraries(reader_allsteps adios2)
 
-if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
+foreach(tgt IN ITEMS writer_multistep reader_stepping reader_allsteps)
+  target_link_libraries(${tgt} adios2)
 
-  target_include_directories(writer_multistep PRIVATE ${MPI_C_INCLUDE_PATH})
-  target_link_libraries(writer_multistep ${MPI_C_LIBRARIES})
-
-  target_include_directories(reader_stepping PRIVATE ${MPI_C_INCLUDE_PATH})
-  target_link_libraries(reader_stepping ${MPI_C_LIBRARIES})
-
-  target_include_directories(reader_allsteps PRIVATE ${MPI_C_INCLUDE_PATH})
-  target_link_libraries(reader_allsteps ${MPI_C_LIBRARIES})
-endif()
+  if(ADIOS2_HAVE_MPI)
+    target_include_directories(${tgt} PRIVATE ${MPI_C_INCLUDE_PATH})
+    target_link_libraries(${tgt} ${MPI_C_LIBRARIES})
+  endif()
+endforeach()
diff --git a/examples/experimental/runtimeconfig/hello/CMakeLists.txt b/examples/experimental/runtimeconfig/hello/CMakeLists.txt
index 28adedce753ea338bbb3d6bea81987ab84f2de09..9770b299c4d377fdf6f0e8177c776db8dee817c5 100644
--- a/examples/experimental/runtimeconfig/hello/CMakeLists.txt
+++ b/examples/experimental/runtimeconfig/hello/CMakeLists.txt
@@ -1,6 +1,4 @@
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(helloBPWriterXML helloBPWriterXML.cpp)
   target_include_directories(helloBPWriterXML PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(helloBPWriterXML ${MPI_C_LIBRARIES})
diff --git a/examples/heatTransfer/read/CMakeLists.txt b/examples/heatTransfer/read/CMakeLists.txt
index 5691e81b41f61d55ba77f41243a350d6bc7c4f47..30797b43a01f63f8af4808f25d97ab50cfbeb772 100644
--- a/examples/heatTransfer/read/CMakeLists.txt
+++ b/examples/heatTransfer/read/CMakeLists.txt
@@ -4,21 +4,14 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
+  add_executable(heatTransfer_read_adios2 heatRead_adios2.cpp PrintData.h)
+  target_include_directories(heatTransfer_read_adios2
+    PRIVATE ${MPI_C_INCLUDE_PATH}
+  )
+  target_link_libraries(heatTransfer_read_adios2 adios2 ${MPI_C_LIBRARIES})
 
   if(ADIOS2_HAVE_ADIOS1)
-  
-    find_package(MPI COMPONENTS C REQUIRED)
-    find_package(ADIOS1 REQUIRED)
-    
-    add_executable(heatTransfer_read_adios2 heatRead_adios2.cpp PrintData.cpp)
-    target_include_directories(heatTransfer_read_adios2
-      PRIVATE ${MPI_C_INCLUDE_PATH}
-    )
-    target_link_libraries(heatTransfer_read_adios2
-      adios2 ${MPI_C_LIBRARIES}
-    )
-
-    add_executable(heatTransfer_read_adios1 heatRead_adios1.cpp PrintData.cpp)
+    add_executable(heatTransfer_read_adios1 heatRead_adios1.cpp PrintData.h)
     target_include_directories(heatTransfer_read_adios1
       PRIVATE ${MPI_C_INCLUDE_PATH}
     )
@@ -26,5 +19,4 @@ if(ADIOS2_HAVE_MPI)
       adios1::adios ${MPI_C_LIBRARIES}
     )
   endif()
-  
-endif()
\ No newline at end of file
+endif()
diff --git a/examples/heatTransfer/read/PrintData.cpp b/examples/heatTransfer/read/PrintData.cpp
deleted file mode 100644
index edbc6688fba93569b4d3eb9b74190735d9b21be7..0000000000000000000000000000000000000000
--- a/examples/heatTransfer/read/PrintData.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Distributed under the OSI-approved Apache License, Version 2.0.  See
- * accompanying file Copyright.txt for details.
- *
- * PrintData.cpp
- *
- *  Created on: Apr 2017
- *      Author: Norbert Podhorszki
- */
-
-#include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <string>
-
-#include "PrintData.h"
-
-void printData(double *xy, size_t *size, size_t *offset, int rank, int steps)
-{
-    std::ofstream myfile;
-    std::string filename = "data." + std::to_string(rank);
-    myfile.open(filename);
-    double *data = xy;
-    uint64_t nelems = size[0] * size[1];
-    for (int step = 0; step < steps; step++)
-    {
-        myfile << "rank=" << rank << " size=" << size[0] << "x" << size[1]
-               << " offsets=" << offset[0] << ":" << offset[1]
-               << " step=" << step << std::endl;
-
-        myfile << " time   row   columns " << offset[1] << "..."
-               << offset[1] + size[1] - 1 << std::endl;
-        myfile << "        ";
-        for (int j = 0; j < size[1]; j++)
-        {
-            myfile << std::setw(9) << offset[1] + j;
-        }
-        myfile << std::endl;
-        myfile << "------------------------------------------------------------"
-                  "--\n";
-        for (int i = 0; i < size[0]; i++)
-        {
-            myfile << std::setw(5) << step << std::setw(5) << offset[0] + i;
-            for (int j = 0; j < size[1]; j++)
-            {
-                myfile << std::setw(9) << std::setprecision(2)
-                       << data[i * size[1] + j];
-            }
-            myfile << std::endl;
-        }
-        data += nelems;
-    }
-    myfile.close();
-}
-
-void printData(double *xy, uint64_t *size, uint64_t *offset, int rank,
-               int steps)
-{
-    std::ofstream myfile;
-    std::string filename = "data." + std::to_string(rank);
-    myfile.open(filename);
-    double *data = xy;
-    uint64_t nelems = size[0] * size[1];
-    for (int step = 0; step < steps; step++)
-    {
-        myfile << "rank=" << rank << " size=" << size[0] << "x" << size[1]
-               << " offsets=" << offset[0] << ":" << offset[1]
-               << " step=" << step << std::endl;
-
-        myfile << " time   row   columns " << offset[1] << "..."
-               << offset[1] + size[1] - 1 << std::endl;
-        myfile << "        ";
-        for (int j = 0; j < size[1]; j++)
-        {
-            myfile << std::setw(9) << offset[1] + j;
-        }
-        myfile << std::endl;
-        myfile << "------------------------------------------------------------"
-                  "--\n";
-        for (int i = 0; i < size[0]; i++)
-        {
-            myfile << std::setw(5) << step << std::setw(5) << offset[0] + i;
-            for (int j = 0; j < size[1]; j++)
-            {
-                myfile << std::setw(9) << std::setprecision(2)
-                       << data[i * size[1] + j];
-            }
-            myfile << std::endl;
-        }
-        data += nelems;
-    }
-    myfile.close();
-}
diff --git a/examples/heatTransfer/read/PrintData.h b/examples/heatTransfer/read/PrintData.h
index 13e1e527ce1a27036d0f65c540be48a459b3b3eb..f18948af619cae90f15eabdc0073d46612e37577 100644
--- a/examples/heatTransfer/read/PrintData.h
+++ b/examples/heatTransfer/read/PrintData.h
@@ -12,9 +12,48 @@
 #define PRINTDATA_H_
 
 #include <cstdint>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <string>
 
-void printData(double *xy, size_t *size, size_t *offset, int rank, int steps);
-void printData(double *xy, uint64_t *size, uint64_t *offset, int rank,
-               int steps);
+template <class T>
+void printData(double *xy, T *size, T *offset, int rank, int steps)
+{
+    std::ofstream myfile;
+    std::string filename = "data." + std::to_string(rank);
+    myfile.open(filename);
+    double *data = xy;
+    uint64_t nelems = size[0] * size[1];
+    for (int step = 0; step < steps; step++)
+    {
+        myfile << "rank=" << rank << " size=" << size[0] << "x" << size[1]
+               << " offsets=" << offset[0] << ":" << offset[1]
+               << " step=" << step << std::endl;
+
+        myfile << " time   row   columns " << offset[1] << "..."
+               << offset[1] + size[1] - 1 << std::endl;
+        myfile << "        ";
+        for (int j = 0; j < size[1]; j++)
+        {
+            myfile << std::setw(9) << offset[1] + j;
+        }
+        myfile << std::endl;
+        myfile << "------------------------------------------------------------"
+                  "--\n";
+        for (int i = 0; i < size[0]; i++)
+        {
+            myfile << std::setw(5) << step << std::setw(5) << offset[0] + i;
+            for (int j = 0; j < size[1]; j++)
+            {
+                myfile << std::setw(9) << std::setprecision(2)
+                       << data[i * size[1] + j];
+            }
+            myfile << std::endl;
+        }
+        data += nelems;
+    }
+    myfile.close();
+}
 
 #endif /* PRINTDATA_H_ */
diff --git a/examples/heatTransfer/write/CMakeLists.txt b/examples/heatTransfer/write/CMakeLists.txt
index a40186bb05e6d8225d73d5d58fea4fc380de47b6..a6c459bca092e6f0c706f982e7b17a36f04caeab 100644
--- a/examples/heatTransfer/write/CMakeLists.txt
+++ b/examples/heatTransfer/write/CMakeLists.txt
@@ -4,9 +4,6 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-  find_package(Threads REQUIRED)
-
   add_executable(heatTransfer_write_adios2
     main.cpp
     HeatTransfer.cpp
@@ -16,15 +13,14 @@ if(ADIOS2_HAVE_MPI)
   target_include_directories(heatTransfer_write_adios2
     PRIVATE ${MPI_C_INCLUDE_PATH}
   )
-  target_link_libraries(heatTransfer_write_adios2 adios2 ${MPI_C_LIBRARIES} 
-                        ${CMAKE_THREAD_LIBS_INIT})
+  target_link_libraries(heatTransfer_write_adios2
+    adios2 ${MPI_C_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+  )
   target_compile_definitions(heatTransfer_write_adios2 PRIVATE
-   -DDEFAULT_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/config.xml)
+   -DDEFAULT_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/config.xml
+  )
 
   if(ADIOS2_HAVE_ADIOS1)
-    find_package(ADIOS1 REQUIRED)
-    find_package(MPI COMPONENTS C REQUIRED)
-
     add_executable(heatTransfer_write_adios1
       main.cpp
       HeatTransfer.cpp
@@ -40,9 +36,6 @@ if(ADIOS2_HAVE_MPI)
   endif()
 
   if(ADIOS2_HAVE_HDF5)
-    find_package(HDF5 REQUIRED)
-    find_package(MPI COMPONENTS C REQUIRED)
-
     add_executable(heatTransfer_write_hdf5
       main.cpp
       HeatTransfer.cpp
@@ -53,14 +46,8 @@ if(ADIOS2_HAVE_MPI)
       PRIVATE ${MPI_C_INCLUDE_PATH} ${HDF5_C_INCLUDE_DIRS}
     )
     target_link_libraries(heatTransfer_write_hdf5
-      ${MPI_C_LIBRARIES} ${HDF5_C_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+      ${HDF5_C_LIBRARIES} ${MPI_C_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
     )
-  endif()
-
-
-  if(ADIOS2_HAVE_HDF5)
-    find_package(HDF5 REQUIRED)
-    find_package(MPI COMPONENTS C REQUIRED)
 
     add_executable(heatTransfer_write_ph5
       main.cpp
@@ -72,15 +59,10 @@ if(ADIOS2_HAVE_MPI)
       PRIVATE ${MPI_C_INCLUDE_PATH} ${HDF5_C_INCLUDE_DIRS}
     )
     target_link_libraries(heatTransfer_write_ph5
-      ${MPI_C_LIBRARIES} ${HDF5_C_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+      ${HDF5_C_LIBRARIES} ${MPI_C_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
     )
-  endif()
-
-
-  if(ADIOS2_HAVE_HDF5)
-     find_package(MPI COMPONENTS C REQUIRED)
 
-     add_executable(heatTransfer_write_a2h5
+    add_executable(heatTransfer_write_a2h5
       main.cpp
       HeatTransfer.cpp
       Settings.cpp
@@ -90,12 +72,8 @@ if(ADIOS2_HAVE_MPI)
     target_include_directories(heatTransfer_write_a2h5
       PRIVATE ${MPI_C_INCLUDE_PATH}
     )
-    #target_link_libraries(heatTransfer_write_a2h5
-    #  ${MPI_C_LIBRARIES} 
-    #)
-    target_link_libraries(heatTransfer_write_a2h5 PUBLIC adios2 
-                          ${CMAKE_THREAD_LIBS_INIT})
-
+    target_link_libraries(heatTransfer_write_a2h5
+      adios2 ${MPI_C_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+    )
   endif()
-
 endif()
diff --git a/examples/hello/CMakeLists.txt b/examples/hello/CMakeLists.txt
index 2850407da804fce34afa2c23bdfce603865e4795..e01daa485f2ba9171b37d39f652613552a6bbcd8 100644
--- a/examples/hello/CMakeLists.txt
+++ b/examples/hello/CMakeLists.txt
@@ -6,6 +6,7 @@
 add_subdirectory(bpWriter)
 add_subdirectory(bpTimeWriter)
 add_subdirectory(bpFlushWriter)
+add_subdirectory(bpAttributeWriter)
 
 if(ADIOS2_HAVE_ADIOS1)
   add_subdirectory(adios1Writer)
diff --git a/examples/hello/adios1Writer/CMakeLists.txt b/examples/hello/adios1Writer/CMakeLists.txt
index f8e52ae9d22e385cf061ed92d8352f301e1815e0..3b5c016b66047fb21944652f17969120c67420c4 100644
--- a/examples/hello/adios1Writer/CMakeLists.txt
+++ b/examples/hello/adios1Writer/CMakeLists.txt
@@ -4,8 +4,6 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(hello_adios1Writer helloADIOS1Writer.cpp)
   target_include_directories(hello_adios1Writer PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_adios1Writer ${MPI_C_LIBRARIES})
diff --git a/examples/hello/bpAttributeWriter/CMakeLists.txt b/examples/hello/bpAttributeWriter/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..30faba4aff8d91e37bd65cb2e6792876200636d3
--- /dev/null
+++ b/examples/hello/bpAttributeWriter/CMakeLists.txt
@@ -0,0 +1,18 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+
+if(ADIOS2_HAVE_MPI)
+  find_package(MPI COMPONENTS C REQUIRED)
+
+  add_executable(hello_bpAttributeWriter helloBPAttributeWriter.cpp)
+  target_include_directories(hello_bpAttributeWriter PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(hello_bpAttributeWriter ${MPI_C_LIBRARIES})
+  
+else()
+  add_executable(hello_bpAttributeWriter helloBPAttributeWriter_nompi.cpp)
+  
+endif()
+
+target_link_libraries(hello_bpAttributeWriter adios2)
diff --git a/examples/hello/bpAttributeWriter/helloBPAttributeWriter.cpp b/examples/hello/bpAttributeWriter/helloBPAttributeWriter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7061f1db3a7ffdc19c96bb5afe47e4c2f3c28288
--- /dev/null
+++ b/examples/hello/bpAttributeWriter/helloBPAttributeWriter.cpp
@@ -0,0 +1,95 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * helloBPWriter.cpp: Simple self-descriptive example of how to write a variable
+ * to a BP File that lives in several MPI processes.
+ *
+ *  Created on: Feb 16, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include <ios>      //std::ios_base::failure
+#include <iostream> //std::cout
+#include <mpi.h>
+#include <stdexcept> //std::invalid_argument std::exception
+#include <string>
+#include <vector>
+
+#include <adios2.h>
+
+int main(int argc, char *argv[])
+{
+    MPI_Init(&argc, &argv);
+    int rank, size;
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+    MPI_Comm_size(MPI_COMM_WORLD, &size);
+
+    /** Application variable */
+    std::vector<float> myFloats = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    const std::size_t Nx = myFloats.size();
+
+    try
+    {
+        /** ADIOS class factory of IO class objects, DebugON is recommended */
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+
+        /*** IO class object: settings and factory of Settings: Variables,
+         * Parameters, Transports, and Execution: Engines */
+        adios2::IO &bpIO = adios.DeclareIO("BPFile_N2N");
+
+        /** global array : name, { shape (total) }, { start (local) }, { count
+         * (local) }, all are constant dimensions */
+        adios2::Variable<float> &bpFloats = bpIO.DefineVariable<float>(
+            "bpFloats", {size * Nx}, {rank * Nx}, {Nx}, adios2::ConstantDims);
+
+        bpIO.DefineAttribute<std::string>("Single_String",
+                                          "File generated with ADIOS2");
+
+        std::vector<std::string> myStrings = {"one", "two", "three"};
+        bpIO.DefineAttribute<std::string>("Array_of_Strings", myStrings.data(),
+                                          myStrings.size());
+
+        bpIO.DefineAttribute<double>("Attr_Double", 0.f);
+        std::vector<double> myDoubles = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        bpIO.DefineAttribute<double>("Array_of_Doubles", myDoubles.data(),
+                                     myDoubles.size());
+
+        /** Engine derived class, spawned to start IO operations */
+        auto bpWriter = bpIO.Open("myVector.bp", adios2::OpenMode::Write);
+
+        if (!bpWriter)
+        {
+            throw std::ios_base::failure(
+                "ERROR: bpWriter not created at Open\n");
+        }
+
+        /** Write variable for buffering */
+        bpWriter->Write<float>(bpFloats, myFloats.data());
+
+        /** Create bp file, engine becomes unreachable after this*/
+        bpWriter->Close();
+    }
+    catch (std::invalid_argument &e)
+    {
+        std::cout << "Invalid argument exception, STOPPING PROGRAM from rank "
+                  << rank << "\n";
+        std::cout << e.what() << "\n";
+    }
+    catch (std::ios_base::failure &e)
+    {
+        std::cout
+            << "IO System base failure exception, STOPPING PROGRAM from rank "
+            << rank << "\n";
+        std::cout << e.what() << "\n";
+    }
+    catch (std::exception &e)
+    {
+        std::cout << "Exception, STOPPING PROGRAM from rank " << rank << "\n";
+        std::cout << e.what() << "\n";
+    }
+
+    MPI_Finalize();
+
+    return 0;
+}
diff --git a/examples/hello/bpAttributeWriter/helloBPAttributeWriter_nompi.cpp b/examples/hello/bpAttributeWriter/helloBPAttributeWriter_nompi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..850d907ea325fa9348c29c910c1b074030c36716
--- /dev/null
+++ b/examples/hello/bpAttributeWriter/helloBPAttributeWriter_nompi.cpp
@@ -0,0 +1,83 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * helloBPWriter_nompi.cpp sequential non-mpi version of helloBPWriter
+ *
+ *  Created on: Jan 9, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include <ios>       //std::ios_base::failure
+#include <iostream>  //std::cout
+#include <stdexcept> //std::invalid_argument std::exception
+#include <string>
+#include <vector>
+
+#include <adios2.h>
+
+int main(int argc, char *argv[])
+{
+    /** Application variable */
+    std::vector<float> myFloats = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    const std::size_t Nx = myFloats.size();
+
+    try
+    {
+        /** ADIOS class factory of IO class objects, DebugON is recommended */
+        adios2::ADIOS adios(adios2::DebugON);
+
+        /*** IO class object: settings and factory of Settings: Variables,
+         * Parameters, Transports, and Execution: Engines */
+        adios2::IO &bpIO = adios.DeclareIO("BPFile_N2N");
+
+        /** global array: name, { shape (total dimensions) }, { start (local) },
+         * { count (local) }, all are constant dimensions */
+        adios2::Variable<float> &bpFloats = bpIO.DefineVariable<float>(
+            "bpFloats", {}, {}, {Nx}, adios2::ConstantDims);
+
+        bpIO.DefineAttribute<std::string>("Single_String",
+                                          "File generated with ADIOS2");
+
+        std::vector<std::string> myStrings = {"one", "two", "three"};
+        bpIO.DefineAttribute<std::string>("Array_of_Strings", myStrings.data(),
+                                          myStrings.size());
+
+        bpIO.DefineAttribute<double>("Attr_Double", 0.f);
+        std::vector<double> myDoubles = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        bpIO.DefineAttribute<double>("Array_of_Doubles", myDoubles.data(),
+                                     myDoubles.size());
+
+        /** Engine derived class, spawned to start IO operations */
+        auto bpWriter = bpIO.Open("myVector.bp", adios2::OpenMode::Write);
+
+        if (!bpWriter)
+        {
+            throw std::ios_base::failure(
+                "ERROR: bpWriter not created at Open\n");
+        }
+
+        /** Write variable for buffering */
+        bpWriter->Write<float>(bpFloats, myFloats.data());
+
+        /** Create bp file, engine becomes unreachable after this*/
+        bpWriter->Close();
+    }
+    catch (std::invalid_argument &e)
+    {
+        std::cout << "Invalid argument exception, STOPPING PROGRAM\n";
+        std::cout << e.what() << "\n";
+    }
+    catch (std::ios_base::failure &e)
+    {
+        std::cout << "IO System base failure exception, STOPPING PROGRAM\n";
+        std::cout << e.what() << "\n";
+    }
+    catch (std::exception &e)
+    {
+        std::cout << "Exception, STOPPING PROGRAM from rank\n";
+        std::cout << e.what() << "\n";
+    }
+
+    return 0;
+}
diff --git a/examples/hello/bpBZip2Wrapper/CMakeLists.txt b/examples/hello/bpBZip2Wrapper/CMakeLists.txt
index 3803a998a7103673cdee602633c71a03a049da24..702d5aaeb3383508227210f076aec4f43b7b117b 100644
--- a/examples/hello/bpBZip2Wrapper/CMakeLists.txt
+++ b/examples/hello/bpBZip2Wrapper/CMakeLists.txt
@@ -4,15 +4,11 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(hello_bpBZip2Wrapper helloBPBZip2Wrapper.cpp)
   target_include_directories(hello_bpBZip2Wrapper PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_bpBZip2Wrapper ${MPI_C_LIBRARIES})
-  
 else()
   add_executable(hello_bpBZip2Wrapper helloBPBZip2Wrapper_nompi.cpp)
-  
 endif()
 
 target_link_libraries(hello_bpBZip2Wrapper adios2)
diff --git a/examples/hello/bpFlushWriter/CMakeLists.txt b/examples/hello/bpFlushWriter/CMakeLists.txt
index ed554a4b4b9abfe17688da507e286371b10535b0..777704ef21ea820ac6d928dedb869fd323ea495c 100644
--- a/examples/hello/bpFlushWriter/CMakeLists.txt
+++ b/examples/hello/bpFlushWriter/CMakeLists.txt
@@ -4,8 +4,6 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(hello_bpFlushWriter helloBPFlushWriter.cpp)
   target_include_directories(hello_bpFlushWriter PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_bpFlushWriter ${MPI_C_LIBRARIES})
diff --git a/examples/hello/bpTimeWriter/CMakeLists.txt b/examples/hello/bpTimeWriter/CMakeLists.txt
index 30551c498bc783e88054ed85c1e90ef5acd51e19..68834e2887d52954186e6f5e6b9d20b70ed38ec9 100644
--- a/examples/hello/bpTimeWriter/CMakeLists.txt
+++ b/examples/hello/bpTimeWriter/CMakeLists.txt
@@ -4,8 +4,6 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(hello_bpTimeWriter helloBPTimeWriter.cpp)
   target_include_directories(hello_bpTimeWriter PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_bpTimeWriter ${MPI_C_LIBRARIES})
diff --git a/examples/hello/bpWriter/CMakeLists.txt b/examples/hello/bpWriter/CMakeLists.txt
index c252a25e6409a13177f7172b21962f93d33e0677..8c834597ac2748dc398d3323c4cac038a179d674 100644
--- a/examples/hello/bpWriter/CMakeLists.txt
+++ b/examples/hello/bpWriter/CMakeLists.txt
@@ -4,15 +4,37 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(hello_bpWriter helloBPWriter.cpp)
   target_include_directories(hello_bpWriter PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_bpWriter ${MPI_C_LIBRARIES})
   
+  add_executable(hello_bpWriter_c helloBPWriter.c)
+  target_include_directories(hello_bpWriter_c PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(hello_bpWriter_c ${MPI_C_LIBRARIES})
+  
+  if(ADIOS2_HAVE_Fortran)
+    add_executable(hello_bpWriter_f helloBPWriter.f90)
+    target_include_directories(hello_bpWriter_f 
+                               PRIVATE ${MPI_Fortran_INCLUDE_PATH} 
+                                       ${MPI_C_INCLUDE_PATH})
+                                               
+    target_link_libraries(hello_bpWriter_f PRIVATE ${MPI_Fortran_LIBRARIES} 
+                                                   ${MPI_C_LIBRARIES})
+  endif()
+  
 else()
   add_executable(hello_bpWriter helloBPWriter_nompi.cpp)
+  add_executable(hello_bpWriter_c helloBPWriter_nompi.c)
+
+  if(ADIOS2_HAVE_Fortran)
+    add_executable(hello_bpWriter_f helloBPWriter_nompi.f90)
+  endif()
   
 endif()
 
 target_link_libraries(hello_bpWriter adios2)
+target_link_libraries(hello_bpWriter_c adios2)
+
+if(ADIOS2_HAVE_Fortran)
+  target_link_libraries(hello_bpWriter_f PRIVATE adios2_f)
+endif()
diff --git a/examples/hello/bpWriter/helloBPWriter.c b/examples/hello/bpWriter/helloBPWriter.c
new file mode 100644
index 0000000000000000000000000000000000000000..6bc7f45d3b25ce48244d6e24006b44d330f3246e
--- /dev/null
+++ b/examples/hello/bpWriter/helloBPWriter.c
@@ -0,0 +1,61 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * helloBPWriter.c : C bindings version of C++11 helloBPWriter.cpp
+ *
+ *  Created on: Aug 8, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+#include <mpi.h>
+#include <stdlib.h> // malloc, free
+
+#include <adios2_c.h>
+
+int main(int argc, char *argv[])
+{
+    MPI_Init(&argc, &argv);
+    int rank, size;
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+    MPI_Comm_size(MPI_COMM_WORLD, &size);
+
+    // application input, data in heap
+    const size_t Nx = 10;
+    float *myFloats;
+    myFloats = malloc(sizeof(float) * Nx);
+
+    unsigned int i;
+    for (i = 0; i < Nx; ++i)
+    {
+        myFloats[i] = i;
+    }
+
+    adios2_ADIOS *adiosH = adios2_init(MPI_COMM_WORLD, adios2_debug_mode_on);
+    adios2_IO *ioH = adios2_declare_io(adiosH, "BPFile_N2N");
+
+    // dims are allocated in stack
+    size_t shape[1];
+    shape[0] = (size_t)size * Nx;
+
+    size_t start[1];
+    start[0] = (size_t)rank * Nx;
+
+    size_t count[1];
+    count[0] = Nx;
+
+    adios2_Variable *variableH =
+        adios2_define_variable(ioH, "bpFloats", adios2_type_float, 1, shape,
+                               start, count, adios2_constant_dims_true);
+    adios2_Engine *engineH =
+        adios2_open(ioH, "myVector_c.bp", adios2_open_mode_write);
+
+    adios2_write(engineH, variableH, myFloats);
+    adios2_close(engineH);
+    adios2_finalize(adiosH);
+
+    free(myFloats);
+
+    MPI_Finalize();
+
+    return 0;
+}
diff --git a/examples/hello/bpWriter/helloBPWriter.f90 b/examples/hello/bpWriter/helloBPWriter.f90
new file mode 100644
index 0000000000000000000000000000000000000000..5ae00add8615f36f772cb9071581ed1efa4d2207
--- /dev/null
+++ b/examples/hello/bpWriter/helloBPWriter.f90
@@ -0,0 +1,56 @@
+program helloBPWriter
+    use mpi
+    use adios2
+
+    implicit none
+
+    integer, dimension(1) :: shape_dims, start_dims, count_dims
+    real, dimension(:), allocatable :: myArray
+    integer :: inx, irank, isize, ierr, i
+    integer(kind=8) :: adios, io, var1, engine1
+
+    ! Launch MPI
+    call MPI_Init(ierr)
+    call MPI_Comm_rank(MPI_COMM_WORLD, irank, ierr)
+    call MPI_Comm_size(MPI_COMM_WORLD, isize, ierr)
+
+    ! Application variables
+    inx = 10
+    allocate( myArray(inx) )
+
+    do i=1,inx
+        myArray(i) = i-1
+    end do
+
+    ! Variable dimensions
+    shape_dims(1) = isize * inx
+    start_dims(1) = irank * inx
+    count_dims(1) = inx
+
+    ! Create adios handler passing the communicator, debug mode and error flag
+    call adios2_init(adios, MPI_COMM_WORLD, adios2_debug_mode_on, ierr)
+
+    ! Declare an IO process configuration inside adios
+    call adios2_declare_io(io, adios, "bpIO", ierr)
+
+    ! Defines a variable to be written in bp format
+    call adios2_define_variable(var1, io, "myArray", adios2_type_real, 1, &
+        & shape_dims, start_dims, count_dims, adios2_constant_dims_true, ierr)
+
+    ! Open myVector_f.bp in write mode, this launches an engine
+    call adios2_open(engine1, io, "myVector_f.bp", adios2_open_mode_write, ierr)
+
+    ! Write myArray contents to bp buffer, based on var1 metadata
+    call adios2_write(engine1, var1, myArray, ierr)
+
+    ! Closes engine1 and deallocates it, becomes unreachable
+    call adios2_close(engine1, ierr)
+
+    ! Deallocates adios and calls its destructor
+    call adios2_finalize(adios, ierr)
+
+    deallocate(myArray)
+
+    call MPI_Finalize(ierr)
+
+end program helloBPWriter
diff --git a/examples/hello/bpWriter/helloBPWriter_nompi.c b/examples/hello/bpWriter/helloBPWriter_nompi.c
new file mode 100644
index 0000000000000000000000000000000000000000..c86e78be613f4e9c2a59b7fbb69cb48fa442f896
--- /dev/null
+++ b/examples/hello/bpWriter/helloBPWriter_nompi.c
@@ -0,0 +1,48 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * helloBPWriter_nompi.c : C bindings version of C++11 helloBPWriter_nompi.cpp
+ *
+ *  Created on: Aug 8, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include <stdlib.h> //malloc, free
+
+#include <adios2_c.h>
+
+int main(int argc, char *argv[])
+{
+    // application input, data in heap
+    const size_t Nx = 10;
+    float *myFloats;
+    myFloats = malloc(sizeof(float) * Nx);
+
+    unsigned int i;
+    for (i = 0; i < Nx; ++i)
+    {
+        myFloats[i] = i;
+    }
+
+    adios2_ADIOS *adiosH = adios2_init_nompi(adios2_debug_mode_on);
+    adios2_IO *ioH = adios2_declare_io(adiosH, "BPFile_N2N");
+
+    // count dims are allocated in stack
+    size_t count[1];
+    count[0] = Nx;
+
+    adios2_Variable *variableH =
+        adios2_define_variable(ioH, "bpFloats", adios2_type_float, 1, NULL,
+                               NULL, count, adios2_constant_dims_true);
+    adios2_Engine *engineH =
+        adios2_open(ioH, "myVector_c.bp", adios2_open_mode_write);
+
+    adios2_write(engineH, variableH, myFloats);
+    adios2_close(engineH);
+    adios2_finalize(adiosH);
+
+    free(myFloats);
+
+    return 0;
+}
diff --git a/examples/hello/bpWriter/helloBPWriter_nompi.f90 b/examples/hello/bpWriter/helloBPWriter_nompi.f90
new file mode 100644
index 0000000000000000000000000000000000000000..22268da83bfeae474ea2ea1922e2f78065e71f35
--- /dev/null
+++ b/examples/hello/bpWriter/helloBPWriter_nompi.f90
@@ -0,0 +1,48 @@
+program helloBPWriter
+
+    use adios2
+
+    implicit none
+
+    integer :: inx, i, ierr
+    integer(kind=8) :: adios, io, var1, engine1
+    real, dimension(:), allocatable :: myArray
+    integer, dimension(1) :: shape_dims, start_dims, count_dims
+
+    !application variables
+    inx = 10
+    allocate( myArray(inx) )
+
+    do i=1,inx
+        myArray(i) = i-1
+    end do
+
+    shape_dims(1) = inx
+    start_dims(1) = 0
+    count_dims(1) = inx
+
+    ! Initialize adios handler
+    call adios2_init(adios, adios2_debug_mode_on, ierr)
+
+    ! Declare an IO configuration inside adios
+    call adios2_declare_io(io, adios, "bpIO", ierr)
+
+    ! Defines variable metadata to be written in bp file
+    call adios2_define_variable(var1, io, "myArray", adios2_type_real, 1, &
+        & shape_dims, start_dims, count_dims, adios2_constant_dims_true, ierr)
+
+    ! Open myVector_f.bp in write mode
+    call adios2_open(engine1, io, "myVector_f.bp", adios2_open_mode_write, ierr)
+
+    ! Write myArray contents to bp buffer, based on var1 metadata
+    call adios2_write(engine1, var1, myArray, ierr)
+
+    ! Closes engine1 and deallocates it, becomes unreachable
+    call adios2_close(engine1, ierr)
+
+    ! Deallocates adios and calls its destructor
+    call adios2_finalize(adios, ierr)
+
+    deallocate(myArray)
+
+end program helloBPWriter
diff --git a/examples/hello/bpZfpWrapper/CMakeLists.txt b/examples/hello/bpZfpWrapper/CMakeLists.txt
index c5cb52b74f20dc3bdced1ea0e63a5a5373a33329..c6802384e091d450d900ddb90599ab707dde5a9c 100644
--- a/examples/hello/bpZfpWrapper/CMakeLists.txt
+++ b/examples/hello/bpZfpWrapper/CMakeLists.txt
@@ -4,15 +4,12 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(hello_bpZfpWrapper helloBPZfpWrapper.cpp)
   target_include_directories(hello_bpZfpWrapper PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_bpZfpWrapper ${MPI_C_LIBRARIES})
   
 else()
   add_executable(hello_bpZfpWrapper helloBPZfpWrapper_nompi.cpp)
-  
 endif()
 
 target_link_libraries(hello_bpZfpWrapper adios2)
diff --git a/examples/hello/datamanReader/CMakeLists.txt b/examples/hello/datamanReader/CMakeLists.txt
index f180c9062b4c74d2b9a3cd21098c539cf1d3293f..1c1baa846fe64e3357d0a82b90354cca50cccde7 100644
--- a/examples/hello/datamanReader/CMakeLists.txt
+++ b/examples/hello/datamanReader/CMakeLists.txt
@@ -4,8 +4,6 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(hello_datamanReader helloDataManReader.cpp)
   target_include_directories(hello_datamanReader PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_datamanReader ${MPI_C_LIBRARIES})
diff --git a/examples/hello/datamanWriter/CMakeLists.txt b/examples/hello/datamanWriter/CMakeLists.txt
index 3e5c955837a67d596a6c515577ec52a6af466154..4a41f3e730d4032f05c09dffe7c381cf6d7cc917 100644
--- a/examples/hello/datamanWriter/CMakeLists.txt
+++ b/examples/hello/datamanWriter/CMakeLists.txt
@@ -4,8 +4,6 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-  
   add_executable(hello_datamanWriter helloDataManWriter.cpp)
   target_include_directories(hello_datamanWriter PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_datamanWriter ${MPI_C_LIBRARIES})
diff --git a/examples/hello/hdf5Writer/CMakeLists.txt b/examples/hello/hdf5Writer/CMakeLists.txt
index a2f7e6abd8d2bb83c1eca0f4e0bc9219e9b99dc2..d5d4311250ddd6cd76a2909f88d53ea17da3eb81 100644
--- a/examples/hello/hdf5Writer/CMakeLists.txt
+++ b/examples/hello/hdf5Writer/CMakeLists.txt
@@ -4,8 +4,6 @@
 #------------------------------------------------------------------------------#
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
-
   add_executable(hello_hdf5Writer helloHDF5Writer.cpp)
   target_include_directories(hello_hdf5Writer PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_hdf5Writer ${MPI_C_LIBRARIES})
diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bd9914576866a2c9a4c1c4a6d7766e3d65acde19
--- /dev/null
+++ b/examples/plugins/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(engine)
diff --git a/examples/plugins/engine/CMakeLists.txt b/examples/plugins/engine/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..37b3215da513cf1d15f56679ce14174ecd764d8c
--- /dev/null
+++ b/examples/plugins/engine/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(examplePluginEngine_class
+  examplePluginEngine_class.cpp
+  ExampleEnginePlugin.h ExampleEnginePlugin.cpp
+)
+target_link_libraries(examplePluginEngine_class adios2)
diff --git a/examples/plugins/engine/ExampleEnginePlugin.cpp b/examples/plugins/engine/ExampleEnginePlugin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2fdbf85b04035e9cd9c0f29712ec952f9825b000
--- /dev/null
+++ b/examples/plugins/engine/ExampleEnginePlugin.cpp
@@ -0,0 +1,106 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * ExampleEnginePlugin.cpp This plugin does nothing but write API calls out to a
+ * log file.
+ *
+ *  Created on: Jul 31, 2017
+ *      Author: Chuck Atkins <chuck.atkins@kitware.com>
+ */
+
+#include "ExampleEnginePlugin.h"
+
+#include <cstdio>
+#include <cstring>
+#include <ctime>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+
+namespace
+{
+std::string now()
+{
+    tm *timeInfo;
+#ifdef _WIN32
+    time_t rawTime;
+    std::time(&rawTime);
+    timeInfo = std::localtime(&rawTime);
+#else
+    timeval curTime;
+    gettimeofday(&curTime, nullptr);
+    timeInfo = std::localtime(&curTime.tv_sec);
+#endif
+
+    char timeBuf[80];
+    std::memset(timeBuf, 0, sizeof(timeBuf));
+    std::strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%dT%H:%M:%S", timeInfo);
+
+#ifdef _WIN32
+    return std::string(timeBuf);
+#else
+    double subSec = curTime.tv_usec / 1e6;
+    char subSecBuf[9];
+    std::memset(subSecBuf, 0, sizeof(subSecBuf));
+    std::sprintf(subSecBuf, "%1.6f", subSec);
+    return std::string(timeBuf) + std::string(&subSecBuf[1]);
+#endif
+}
+}
+
+namespace adios2
+{
+
+ExampleEnginePlugin::ExampleEnginePlugin(IO &io, const std::string &name,
+                                         const OpenMode openMode,
+                                         MPI_Comm mpiComm)
+: adios2::PluginEngineInterface(io, name, openMode, mpiComm)
+{
+    Init();
+}
+
+ExampleEnginePlugin::~ExampleEnginePlugin() { m_Log.close(); }
+
+void ExampleEnginePlugin::Close(const int transportIndex)
+{
+    m_Log << now() << " Close with transportIndex " << transportIndex
+          << std::endl;
+}
+
+void ExampleEnginePlugin::Init()
+{
+    std::string logName = "ExamplePlugin.log";
+    auto paramLogNameIt = m_IO.m_Parameters.find("LogName");
+    if (paramLogNameIt != m_IO.m_Parameters.end())
+    {
+        logName = paramLogNameIt->second;
+    }
+
+    m_Log.open(logName);
+    if (!m_Log)
+    {
+        throw std::ios_base::failure(
+            "ExampleEnginePlugin: Failed to open log file " + logName);
+    }
+
+    m_Log << now() << " Init" << std::endl;
+}
+
+#define define(T)                                                              \
+    void ExampleEnginePlugin::DoWrite(Variable<T> &variable, const T *values)  \
+    {                                                                          \
+        m_Log << now() << " Writing variable \"" << variable.m_Name << "\""    \
+              << std::endl;                                                    \
+    }
+ADIOS2_FOREACH_TYPE_1ARG(define)
+#undef define
+void ExampleEnginePlugin::DoWrite(VariableCompound &variable,
+                                  const void *values)
+{
+    m_Log << now() << " Writing variable \"" << variable.m_Name << "\""
+          << std::endl;
+}
+
+} // end namespace adios2
diff --git a/examples/plugins/engine/ExampleEnginePlugin.h b/examples/plugins/engine/ExampleEnginePlugin.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa81d0a1aa1c9e906d0dcaeb4ca91a7ab710e446
--- /dev/null
+++ b/examples/plugins/engine/ExampleEnginePlugin.h
@@ -0,0 +1,51 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * ExampleEnginePlugin.h This plugin does nothing but write API calls out to a
+ * log file.
+ *
+ *  Created on: Jul 31, 2017
+ *      Author: Chuck Atkins <chuck.atkins@kitware.com>
+ */
+
+#ifndef EXAMPLEENGINEPLUGIN_H_
+#define EXAMPLEENGINEPLUGIN_H_
+
+#include <fstream>
+#include <string>
+
+#include <adios2/ADIOSMPICommOnly.h>
+#include <adios2/ADIOSMacros.h>
+#include <adios2/ADIOSTypes.h>
+#include <adios2/core/IO.h>
+#include <adios2/engine/plugin/PluginEngineInterface.h>
+
+namespace adios2
+{
+
+/** An engine interface to be used aby the plugin infrastructure */
+class ExampleEnginePlugin : public PluginEngineInterface
+{
+public:
+    ExampleEnginePlugin(IO &io, const std::string &name,
+                        const OpenMode openMode, MPI_Comm mpiComm);
+    virtual ~ExampleEnginePlugin();
+
+    void Close(const int transportIndex = -1) override;
+
+protected:
+    void Init() override;
+
+#define declare(T)                                                             \
+    void DoWrite(Variable<T> &variable, const T *values) override;
+    ADIOS2_FOREACH_TYPE_1ARG(declare)
+#undef declare
+    void DoWrite(VariableCompound &variable, const void *values) override;
+
+private:
+    std::ofstream m_Log;
+};
+
+} // end namespace adios2
+#endif /* EXAMPLEENGINEPLUGIN_H_ */
diff --git a/examples/plugins/engine/examplePluginEngine_class.cpp b/examples/plugins/engine/examplePluginEngine_class.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8a65905dd72e905b3284e07c3f3919558fb1a73
--- /dev/null
+++ b/examples/plugins/engine/examplePluginEngine_class.cpp
@@ -0,0 +1,77 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * helloBPWriter_nompi.cpp sequential non-mpi version of helloBPWriter
+ *
+ *  Created on: Jan 9, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include <ios>       //std::ios_base::failure
+#include <iostream>  //std::cout
+#include <stdexcept> //std::invalid_argument std::exception
+#include <vector>
+
+#include <adios2.h>
+#include <adios2/engine/plugin/PluginEngine.h>
+
+#include "ExampleEnginePlugin.h"
+
+int main(int argc, char *argv[])
+{
+    /** Application variable */
+    std::vector<float> myFloats = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    const std::size_t Nx = myFloats.size();
+
+    adios2::PluginEngine::RegisterPlugin<adios2::ExampleEnginePlugin>(
+        "MyPlugin");
+
+    try
+    {
+        /** ADIOS class factory of IO class objects, DebugON is recommended */
+        adios2::ADIOS adios(adios2::DebugON);
+
+        /*** IO class object: settings and factory of Settings: Variables,
+         * Parameters, Transports, and Execution: Engines */
+        adios2::IO &io = adios.DeclareIO("PluginIO");
+
+        /** global array: name, { shape (total dimensions) }, { start (local) },
+         * { count (local) }, all are constant dimensions */
+        adios2::Variable<float> &var = io.DefineVariable<float>(
+            "data", {}, {}, {Nx}, adios2::ConstantDims);
+
+        /** Engine derived class, spawned to start IO operations */
+        io.SetEngine("PluginEngine");
+        io.SetParameters({{"PluginName", "MyPlugin"}});
+        auto writer = io.Open("TestPlugin", adios2::OpenMode::Write);
+
+        if (!writer)
+        {
+            throw std::ios_base::failure("ERROR: writer not created at Open\n");
+        }
+
+        /** Write variable for buffering */
+        writer->Write<float>(var, myFloats.data());
+
+        /** Create bp file, engine becomes unreachable after this*/
+        writer->Close();
+    }
+    catch (std::invalid_argument &e)
+    {
+        std::cout << "Invalid argument exception, STOPPING PROGRAM\n";
+        std::cout << e.what() << "\n";
+    }
+    catch (std::ios_base::failure &e)
+    {
+        std::cout << "IO System base failure exception, STOPPING PROGRAM\n";
+        std::cout << e.what() << "\n";
+    }
+    catch (std::exception &e)
+    {
+        std::cout << "Exception, STOPPING PROGRAM from rank\n";
+        std::cout << e.what() << "\n";
+    }
+
+    return 0;
+}
diff --git a/scripts/appveyor/av_default.cmake b/scripts/appveyor/av_default.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..81ad4fbbb1c51f6eb0649f3b939f0bdbe54afd2d
--- /dev/null
+++ b/scripts/appveyor/av_default.cmake
@@ -0,0 +1,31 @@
+# Client maintainer: chuck.atkins@kitware.com
+set(CTEST_SITE "AppVeyor")
+set(CTEST_BUILD_CONFIGURATION Release)
+set(CTEST_CONFIGURATION_TYPE Release)
+set(CTEST_CMAKE_GENERATOR "Visual Studio 14 2015 Win64")
+set(CTEST_TEST_ARGS PARALLEL_LEVEL 4)
+
+message("av_default.cmake, CTEST_BUILD_NAME=${CTEST_BUILD_NAME}, push build notes is ADIOS_CTEST_SUBMIT_NOTES=${ADIOS_CTEST_SUBMIT_NOTES}")
+
+set(dashboard_model Experimental)
+set(dashboard_binary_name "build_visual-studio")
+set(dashboard_track "Continuous Integration")
+
+set(CTEST_GIT_COMMAND "git.exe")
+set(CTEST_UPDATE_VERSION_ONLY TRUE)
+set(CTEST_SOURCE_DIRECTORY "$ENV{APPVEYOR_BUILD_FOLDER}")
+set(CTEST_DASHBOARD_ROOT "C:/projects/adios2build")
+
+set(dashboard_cache "
+ADIOS2_USE_ADIOS1:STRING=OFF
+ADIOS2_USE_BZip2:STRING=OFF
+ADIOS2_USE_DataMan:STRING=OFF
+ADIOS2_USE_Fortran:STRING=OFF
+ADIOS2_USE_HDF5:STRING=OFF
+ADIOS2_USE_MPI:STRING=OFF
+ADIOS2_USE_Python:STRING=OFF
+ADIOS2_USE_ZFP:STRING=OFF
+ADIOS2_USE_ZeroMQ:STRING=OFF
+")
+
+include(${CMAKE_CURRENT_LIST_DIR}/../dashboard/adios_common.cmake)
diff --git a/scripts/circle/.dockerignore b/scripts/circle/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..92649f08b17c0351ac49985400dfb9b16e013293
--- /dev/null
+++ b/scripts/circle/.dockerignore
@@ -0,0 +1,4 @@
+runOnCircle.sh
+postComment.sh
+*.cmake
+findStatus.py
diff --git a/scripts/circle/Dockerfile b/scripts/circle/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..d4201627d4f95b55c5797a223cd5a06882044718
--- /dev/null
+++ b/scripts/circle/Dockerfile
@@ -0,0 +1,87 @@
+FROM centos:centos7
+
+# Install extra repos
+RUN yum -y install epel-release https://github.com/openhpc/ohpc/releases/download/v1.3.GA/ohpc-release-1.3-1.el7.x86_64.rpm
+
+# Install core dev packages
+RUN yum -y install \
+      curl file \
+      gcc gcc-c++ gcc-gfortran \
+      git make autoconf automake libtool\
+      bzip2-devel zeromq-devel hdf5-devel \
+      python-devel numpy
+
+# Install gcc7 OpenHPC packages
+RUN yum -y install \
+      lmod-ohpc gnu7-compilers-ohpc openmpi-gnu7-ohpc phdf5-gnu7-openmpi-ohpc
+
+# Cleanup headers and packages
+RUN yum clean all
+
+# Install the CMake binary
+WORKDIR /opt/cmake/3.6.0
+RUN curl -L https://cmake.org/files/v3.6/cmake-3.6.0-Linux-x86_64.tar.gz | \
+      tar --strip-components=1 -xzv
+
+# ZFP
+WORKDIR /opt/zfp
+RUN mkdir build install && \
+    git clone https://github.com/LLNL/zfp.git src && \
+    cd build && \
+    /opt/cmake/3.6.0/bin/cmake \
+      -DCMAKE_INSTALL_PREFIX=/opt/zfp/install \
+      ../src && \
+    make -j8 install && \
+    cd ../ && \
+    rm -rf build src
+
+# ADIOS1
+
+# Source setup
+WORKDIR /opt/adios1/1.12.0
+RUN mkdir source && \
+    curl -L https://github.com/ornladios/ADIOS/archive/v1.12.0.tar.gz | \
+      tar -C source --strip-components=1 -xzv && \
+    cd source && ./autogen.sh
+
+# GCC 4.8
+RUN cd source && \
+    CFLAGS="-O2 -fPIC" CXXFLAGS="-O2 -fPIC" FCFLAGS="-O2 -fPIC" \
+      ./configure --prefix=/opt/adios1/1.12.0/gnu48 --without-mpi && \
+    make -j8 install && \
+    make distclean
+
+# GCC 7
+RUN cd source && \
+    . /etc/profile && \
+    module load gnu7 openmpi phdf5 && \
+    CFLAGS="-O2 -fPIC" CXXFLAGS="-O2 -fPIC" FCFLAGS="-O2 -fPIC" \
+      ./configure --prefix=/opt/adios1/1.12.0/gnu7_openmpi && \
+    make -j8 install
+
+# Cleanup
+RUN rm -rf /opt/adios1/1.12.0/source
+
+# mpi4py
+WORKDIR /opt
+RUN . /etc/profile && \
+    module purge && \
+    module load gnu7 openmpi && \
+    curl -L https://bitbucket.org/mpi4py/mpi4py/downloads/mpi4py-2.0.0.tar.gz | \
+      tar -xzv && \
+    cd mpi4py-2.0.0 && \
+    python setup.py build && \
+    python setup.py install && \
+    cd ../ && rm -rf mpi4py-2.0.0
+
+# Create a non-root user to run the builds/tests
+RUN export uid=1001 gid=1001 && \
+    mkdir -p /home/adios2 && \
+    echo "adios2:x:${uid}:${gid}:adios2,,,:/home/adios2:/bin/bash" >> /etc/passwd && \
+    echo "adios2:x:${uid}:" >> /etc/group && \
+    chown ${uid}:${gid} -R /home/adios2
+
+USER adios2
+ENV HOME /home/adios2
+WORKDIR /home/adios2
+CMD /bin/bash
diff --git a/scripts/circle/EnvironmentModules.cmake b/scripts/circle/EnvironmentModules.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..60328e38b6bad7b7bfa5b86854b3805444866efc
--- /dev/null
+++ b/scripts/circle/EnvironmentModules.cmake
@@ -0,0 +1,207 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# EnvironmentModules
+# ------------------
+#
+# Make environment module commands available to CMake scripts.  This module
+# is compatible with both Lua based Lmod and TCL based EnvironmentModules
+#
+# Module Command
+# ^^^^^^^^^^^^^^
+#
+# This module searched for the module command in the following variable:
+#
+# ::
+#
+#   MODULE_COMMAND - The low level module command to use.  Currently supported
+#                    are implementations are the Lua based Lmod and TCL based
+#                    EnvironmentModules.  The ENV{MODULESHOME} variable,
+#                    usually set by the module environment, is used as a hint
+#                    to locate the command.
+#
+# Provided Functions
+# ^^^^^^^^^^^^^^^^^^
+#
+# This module defines the following functions:
+#
+# ::
+#
+#   module(...)                 - Execute an arbitry module command
+#   module_swap(out_mod in_mod) - Swap out one currently loaded module for
+#                                 another
+#   module_list(out_var)        - Retrieve the currently loaded modules,
+#                                 making the output available as a properly
+#                                 formatted CMake ;list variable.
+#   module_avail(out_var)       - Retrieve the availabe modules that can be
+#                                 loaded, making the output available as a
+#                                 properly formatted CMake ;-seperated list
+#                                 variable.
+
+# Execute an aribitrary module command.  Usage:
+#   module(cmd arg1 ... argN)
+#     Process the given command and arguments as if they were passed
+#     directly to the module command in your shell environment.
+#   module(
+#     COMMAND cmd arg1 .. argN
+#     [OUTPUT_VARIABLE out_var]
+#     [RESULT_VARIABLE ret_var]
+#   )
+function(module)
+  if(NOT MODULE_COMMAND)
+    message(ERROR "Failed to process module command.  MODULE_COMMAND not found")
+    return()
+  endif()
+
+  set(options)
+  set(oneValueArgs OUTPUT_VARIABLE RESULT_VARIABLE)
+  set(multiValueArgs COMMAND)
+  cmake_parse_arguments(MOD_ARGS
+    "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGV}
+  )
+  if(NOT MOD_ARGS_COMMAND)
+    # If no explicit command argument was given, then treat the calling syntax
+    # as: module(cmd args...)
+    set(exec_cmd ${ARGV})
+  else()
+    set(exec_cmd ${MOD_ARGS_COMMAND})
+  endif()
+
+  if(MOD_ARGS_OUTPUT_VARIABLE)
+    set(err_var_args ERROR_VARIABLE err_var)
+   endif()
+
+  execute_process(
+    COMMAND mktemp -t module.cmake.XXXXXXXXXXXX
+    OUTPUT_VARIABLE tempfile_name
+  )
+  string(STRIP "${tempfile_name}" tempfile_name)
+
+  # If the $MODULESHOME/init/cmake file exists then assume that the CMake
+  # "shell" functionality exits
+  if(EXISTS "$ENV{MODULESHOME}/init/cmake")
+    execute_process(
+      COMMAND ${MODULE_COMMAND} cmake ${exec_cmd}
+      OUTPUT_FILE ${tempfile_name}
+      ${err_var_args}
+      RESULT_VARIABLE ret_var
+    )
+  else() # fallback to the sh shell and manually convert to CMake
+    execute_process(
+      COMMAND ${MODULE_COMMAND} sh ${exec_cmd}
+      OUTPUT_VARIABLE out_var
+      ${err_var_args}
+      RESULT_VARIABLE ret_var
+    )
+  endif()
+
+  # If we executed successfully then process and cleanup the temp file
+  if("${ret_var}" EQUAL 0)
+    # No CMake shell so we need to process the sh output into CMake code
+    if(NOT EXISTS "$ENV{MODULESHOME}/init/cmake")
+      file(WRITE ${tempfile_name} "")
+      string(REPLACE "\n" ";" out_var "${out_var}")
+      foreach(sh_cmd IN LISTS out_var)
+        if(sh_cmd MATCHES "^ *unset *([^ ]*)")
+          set(cmake_cmd "unset(ENV{${CMAKE_MATCH_1}})")
+        elseif(sh_cmd MATCHES "^ *export *([^ ]*)")
+          set(cmake_cmd "set(ENV{${CMAKE_MATCH_1}} \"\${${CMAKE_MATCH_1}}\")")
+        elseif(sh_cmd MATCHES " *([^ =]*) *= *(.*)")
+          set(var_name "${CMAKE_MATCH_1}")
+          set(var_value "${CMAKE_MATCH_2}")
+          if(var_value MATCHES "^\"(.*[^\\])\"")
+            # If it's in quotes, take the value as is
+            set(var_value "${CMAKE_MATCH_1}")
+          else()
+            # Otherwise, strip trailing spaces
+            string(REGEX REPLACE "([^\\])? +$" "\\1" var_value "${var_value}")
+          endif()
+          string(REPLACE "\\ " " " var_value "${var_value}")
+          set(cmake_cmd "set(${var_name} \"${var_value}\")")
+        else()
+          continue()
+        endif()
+        file(APPEND ${tempfile_name} "${cmake_cmd}\n")
+      endforeach()
+    endif()
+
+    # Process the change in environment variables
+    include(${tempfile_name})
+    file(REMOVE ${tempfile_name})
+  endif()
+
+  # Push the output back out to the calling scope
+  if(MOD_ARGS_OUTPUT_VARIABLE)
+    set(${MOD_ARGS_OUTPUT_VARIABLE} "${err_var}" PARENT_SCOPE)
+  endif()
+  if(MOD_ARGS_RESULT_VARIABLE)
+    set(${MOD_ARGS_RESULT_VARIABLE} ${ret_var} PARENT_SCOPE)
+  endif()
+endfunction(module)
+
+# Swap one module for another
+function(module_swap out_mod in_mod)
+  module(COMMAND -t swap ${out_mod} ${in_mod} OUTPUT_VARIABLE tmp_out)
+endfunction()
+
+# Retrieve the currently loaded modules
+function(module_list out_var)
+  cmake_policy(SET CMP0007 NEW)
+  module(COMMAND -t list OUTPUT_VARIABLE tmp_out)
+
+  # Convert output into a CMake list
+  string(REPLACE "\n" ";" ${out_var} "${tmp_out}")
+
+  # Remove title headers and empty entries
+  list(REMOVE_ITEM ${out_var} "No modules loaded")
+  if(${out_var})
+    list(FILTER ${out_var} EXCLUDE REGEX "^(.*:)?$")
+  endif()
+  list(FILTER ${out_var} EXCLUDE REGEX "^(.*:)?$")
+
+  set(${out_var} ${${out_var}} PARENT_SCOPE)
+endfunction()
+
+# Retrieve the list of available modules
+function(module_avail out_var)
+  cmake_policy(SET CMP0007 NEW)
+  module(COMMAND -t avail OUTPUT_VARIABLE tmp_out)
+
+  # Convert output into a CMake list
+  string(REPLACE "\n" ";" tmp_out "${tmp_out}")
+
+  set(${out_var})
+  foreach(MOD IN LISTS tmp_out)
+    # Remove directory entries and empty values
+    if(MOD MATCHES "^(.*:)?$")
+      continue()
+    endif()
+
+    # Convert default modules
+    if(MOD MATCHES "^(.*)/$" ) # "foo/"
+      list(APPEND ${out_var} ${CMAKE_MATCH_1})
+    elseif(MOD MATCHES "^((.*)/.*)\\(default\\)$") # "foo/1.2.3(default)"
+      list(APPEND ${out_var} ${CMAKE_MATCH_2})
+      list(APPEND ${out_var} ${CMAKE_MATCH_1})
+    else()
+      list(APPEND ${out_var} ${MOD})
+    endif()
+  endforeach()
+
+  set(${out_var} ${${out_var}} PARENT_SCOPE)
+endfunction()
+
+# Make sure our CMake is new enough
+if(CMAKE_VERSION VERSION_LESS 3.6)
+  message(FATAL_ERROR
+    "The EnvironmentModules interface requires at least CMake v3.6"
+  )
+endif()
+
+# Make sure we know where the underlying module command is
+find_program(MODULE_COMMAND
+  NAMES lmod modulecmd
+  HINTS ENV MODULESHOME
+  PATH_SUFFIXES libexec
+)
diff --git a/scripts/circle/circle_el7-gcc48.cmake b/scripts/circle/circle_el7-gcc48.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..3efe1284b1c499b506ae2ef7ac508739ccd4796d
--- /dev/null
+++ b/scripts/circle/circle_el7-gcc48.cmake
@@ -0,0 +1,38 @@
+# Client maintainer: chuck.atkins@kitware.com
+set(CTEST_SITE "CircleCI")
+set(CTEST_BUILD_CONFIGURATION Release)
+set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
+set(CTEST_BUILD_FLAGS "-k -j4")
+set(CTEST_TEST_ARGS PARALLEL_LEVEL 4)
+
+set(dashboard_model Experimental)
+set(dashboard_binary_name "build_$ENV{CIRCLE_JOB}")
+set(dashboard_track "Continuous Integration")
+
+set(CTEST_GIT_COMMAND "/usr/bin/git")
+set(CTEST_UPDATE_VERSION_ONLY TRUE)
+set(CTEST_SOURCE_DIRECTORY "$ENV{CIRCLE_WORKING_DIRECTORY}/source")
+set(CTEST_DASHBOARD_ROOT "$ENV{HOME}")
+
+include(${CMAKE_CURRENT_LIST_DIR}/EnvironmentModules.cmake)
+module(purge)
+
+set(ENV{CC}  gcc)
+set(ENV{CXX} g++)
+set(ENV{FC}  gfortran)
+
+set(dashboard_cache "
+ADIOS2_USE_ADIOS1:STRING=ON
+ADIOS2_USE_BZip2:STRING=ON
+ADIOS2_USE_DataMan:STRING=ON
+ADIOS2_USE_Fortran:STRING=ON
+ADIOS2_USE_HDF5:STRING=ON
+ADIOS2_USE_MPI:STRING=OFF
+ADIOS2_USE_Python:STRING=ON
+ADIOS2_USE_ZFP:STRING=ON
+ADIOS2_USE_ZeroMQ:STRING=ON
+ZFP_ROOT_DIR:PATH=/opt/zfp/install
+ADIOS1_ROOT:PATH=/opt/adios1/1.12.0/gnu48
+")
+
+include(${CMAKE_CURRENT_LIST_DIR}/../dashboard/adios_common.cmake)
diff --git a/scripts/circle/circle_el7-gcc7-openmpi.cmake b/scripts/circle/circle_el7-gcc7-openmpi.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..e337585db7bcb3bdea6e3c3fba1ff666b696b173
--- /dev/null
+++ b/scripts/circle/circle_el7-gcc7-openmpi.cmake
@@ -0,0 +1,41 @@
+# Client maintainer: chuck.atkins@kitware.com
+set(CTEST_SITE "CircleCI")
+set(CTEST_BUILD_CONFIGURATION Release)
+set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
+set(CTEST_BUILD_FLAGS "-k -j4")
+set(CTEST_TEST_ARGS PARALLEL_LEVEL 4)
+
+set(dashboard_model Experimental)
+set(dashboard_binary_name "build_$ENV{CIRCLE_JOB}")
+set(dashboard_track "Continuous Integration")
+
+set(CTEST_GIT_COMMAND "/usr/bin/git")
+set(CTEST_UPDATE_VERSION_ONLY TRUE)
+set(CTEST_SOURCE_DIRECTORY "$ENV{CIRCLE_WORKING_DIRECTORY}/source")
+set(CTEST_DASHBOARD_ROOT "$ENV{HOME}")
+
+include(${CMAKE_CURRENT_LIST_DIR}/EnvironmentModules.cmake)
+module(purge)
+module(load gnu7)
+module(load openmpi)
+module(load phdf5)
+
+set(ENV{CC}  gcc)
+set(ENV{CXX} g++)
+set(ENV{FC}  gfortran)
+
+set(dashboard_cache "
+ADIOS2_USE_ADIOS1:STRING=ON
+ADIOS2_USE_BZip2:STRING=ON
+ADIOS2_USE_DataMan:STRING=ON
+ADIOS2_USE_Fortran:STRING=ON
+ADIOS2_USE_HDF5:STRING=ON
+ADIOS2_USE_MPI:STRING=ON
+ADIOS2_USE_Python:STRING=ON
+ADIOS2_USE_ZFP:STRING=ON
+ADIOS2_USE_ZeroMQ:STRING=ON
+ZFP_ROOT_DIR:PATH=/opt/zfp/install
+ADIOS1_ROOT:PATH=/opt/adios1/1.12.0/gnu7_openmpi
+")
+
+include(${CMAKE_CURRENT_LIST_DIR}/../dashboard/adios_common.cmake)
diff --git a/scripts/circle/findStatus.py b/scripts/circle/findStatus.py
new file mode 100644
index 0000000000000000000000000000000000000000..44592ab848910dae22a9fa5cd57846039c07dd97
--- /dev/null
+++ b/scripts/circle/findStatus.py
@@ -0,0 +1,25 @@
+import sys
+import json
+import argparse
+
+
+def searchForContext(context):
+    print('Searching for a status with context: %s' % context)
+    statuses = json.load(sys.stdin)
+
+    for stat in statuses:
+        if 'context' in stat and stat['context'] == context:
+            sys.exit(0)
+
+    sys.exit(1)
+
+
+# =============================================================================
+# Main
+# =============================================================================
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser("Parse github api status list")
+    parser.add_argument("--context", default=None, help="context of interest")
+    args = parser.parse_args()
+    searchForContext(args.context)
diff --git a/scripts/circle/postComment.sh b/scripts/circle/postComment.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ca1f847fb5d90d5fb0c6307804c0489311897136
--- /dev/null
+++ b/scripts/circle/postComment.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/bash
+
+PULLREQUESTS=${CIRCLE_PULL_REQUESTS}   # comma-separated list of pr urls
+REPO=${CIRCLE_PROJECT_REPONAME}        # repo name
+OWNER=${CIRCLE_PROJECT_USERNAME}       # organization or user the repo lives under
+TOKEN=${GITHUB_API_KEY}                # added to env via CircleCI project settings
+COMMIT=${CIRCLE_SHA1}                  # sha being tested
+COMPAREURL=${CIRCLE_COMPARE_URL}       # CircleCI provides url for comparing this sha with previous
+
+SHASNIP=$(echo $COMMIT | cut -c1-7)    # Grab first 7 characters of the sha to use as link text
+CDASHPROJECT="ADIOS"
+CDASHURL="https://open.cdash.org/index.php?compare1=61&filtercount=1&field1=revision&project=${CDASHPROJECT}&showfilters=0&limit=100&value1=${COMMIT}&showfeed=0"
+COMMENTBODY="View build and test results for change [${SHASNIP}](${COMPAREURL}) on [CDash](${CDASHURL})"
+APIBASEURL="https://api.github.com/repos"
+
+get_post_data()
+{
+  cat <<EOF
+{
+  "body": "$COMMENTBODY"
+}
+EOF
+}
+
+postBody="$(get_post_data)"
+
+IFS=',' read -ra ADDR <<< "$PULLREQUESTS"
+for i in "${ADDR[@]}"; do
+    # For each pr url, find the pr number
+    if [[ "$i" =~ ADIOS2/pull/([[:digit:]]+) ]];
+    then
+      prNum=${BASH_REMATCH[1]}
+      postUrl="${APIBASEURL}/${OWNER}/${REPO}/issues/${prNum}/comments"
+
+      curl -u "${OWNER}:${TOKEN}" "${postUrl}" -H "Content-Type: application/json" -H "Accept: application/vnd.github.v3+json" -d "${postBody}"
+    else
+      echo "Unable to find PR number in ${i}"
+    fi
+done
+
diff --git a/scripts/circle/runOnCircle.sh b/scripts/circle/runOnCircle.sh
new file mode 100755
index 0000000000000000000000000000000000000000..20f0fbfabd0615dc41f719013aaf296b08b17a5f
--- /dev/null
+++ b/scripts/circle/runOnCircle.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+API_BASE="https://api.github.com/repos/ornladios/adios2"
+USER=${STATUS_ROBOT_NAME}
+TOKEN=${STATUS_ROBOT_KEY}
+COMMIT=${CIRCLE_SHA1}
+CDASH_STATUS_CONTEXT="cdash"
+
+build_status_body() {
+  cat <<EOF
+{
+  "state": "success",
+  "target_url": "https://open.cdash.org/index.php?compare1=61&filtercount=1&field1=revision&project=ADIOS&showfilters=0&limit=100&value1=${COMMIT}&showfeed=0",
+  "description": "Build and test results available on CDash",
+  "context": "${CDASH_STATUS_CONTEXT}"
+}
+EOF
+}
+
+check_and_post_status() {
+  PYTHON_SCRIPT="${SOURCE_DIR}/scripts/circle/findStatus.py"
+  curl -u "${STATUS_ROBOT_NAME}:${STATUS_ROBOT_KEY}" "${API_BASE}/commits/${COMMIT}/statuses" | python ${PYTHON_SCRIPT} --context ${CDASH_STATUS_CONTEXT}
+  if [ $? -ne 0 ]
+  then
+    echo "Need to post a status for context ${CDASH_STATUS_CONTEXT}"
+    postBody="$(build_status_body)"
+    postUrl="${API_BASE}/statuses/${COMMIT}"
+    curl -u "${OWNER}:${TOKEN}" "${postUrl}" -H "Content-Type: application/json" -H "Accept: application/vnd.github.v3+json" -d "${postBody}"
+  fi
+}
+
+get_real_branch_name() {
+  APIURL="${API_BASE}/pulls/${CIRCLE_PR_NUMBER}"
+  RESULT=`curl -s ${APIURL} | python -c "import sys, json; print(json.load(sys.stdin)['head']['ref'])" 2> /dev/null`
+
+  if [ $? -eq 0 ]
+  then
+    REALBRANCH=$RESULT
+  else
+    REALBRANCH=$CIRCLE_BRANCH
+  fi
+}
+
+check_var() {
+  if [ -z "$1" ]
+  then
+    echo "Error: The $1 environment variable is undefined"
+    exit 1
+  fi
+}
+
+check_var CIRCLE_WORKING_DIRECTORY
+check_var CIRCLE_BRANCH
+check_var CIRCLE_JOB
+check_var CIRCLE_BUILD_NUM
+check_var CIRCLE_PR_NUMBER
+
+if [ ! "${CUSTOM_BUILD_NAME}" ]
+then
+  get_real_branch_name
+
+  LINETOSAVE="export CUSTOM_BUILD_NAME=${REALBRANCH}_${CIRCLE_BUILD_NUM}_${CIRCLE_JOB}"
+
+  # Set the custom build name for this step
+  eval $LINETOSAVE
+
+  # Also make sure it will get set for the following steps
+  echo "${LINETOSAVE}" >> $BASH_ENV
+fi
+
+SOURCE_DIR=${CIRCLE_WORKING_DIRECTORY}/source
+CTEST_SCRIPT="${SOURCE_DIR}/scripts/circle/circle_${CIRCLE_JOB}.cmake"
+
+if [ ! -f "${CTEST_SCRIPT}" ]
+then
+  echo "Unable to find CTest script $(basename ${CTEST_SCRIPT})"
+  exit 2
+fi
+
+case "$1" in
+  update|configure|build|test)
+    STEP=$1
+    if [ "$STEP" == "update" ]
+    then
+      check_and_post_status
+    fi
+    ;;
+  *)
+    echo "Usage: $0 (update|configure|build|test)"
+    exit 3
+    ;;
+esac
+
+# Manually source the bash env setup, freeing up $BASH_ENV used by circleci
+. /etc/profile >/dev/null
+
+/opt/cmake/3.6.0/bin/ctest -VV -S ${CTEST_SCRIPT} -Ddashboard_full=OFF -Ddashboard_do_${STEP}=TRUE -DCTEST_BUILD_NAME=${CUSTOM_BUILD_NAME}
diff --git a/scripts/dashboard/adios_common.cmake b/scripts/dashboard/adios_common.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..4cd0b3ab8a085fdf63f2795ebdbf6de13ce88728
--- /dev/null
+++ b/scripts/dashboard/adios_common.cmake
@@ -0,0 +1,81 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+#
+# ADIOS Common Dashboard Script
+#
+# This script contains basic dashboard driver code common to all
+# clients.
+#
+#   # Client maintainer: me@mydomain.net
+#   set(CTEST_SITE "machine.site")
+#   set(CTEST_BUILD_NAME "Platform-Compiler")
+#   set(CTEST_CONFIGURATION_TYPE Debug)
+#   set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
+#   include(${CTEST_SCRIPT_DIRECTORY}/adios_common.cmake)
+#
+# Then run a scheduled task (cron job) with a command line such as
+#
+#   ctest -S ~/Dashboards/Scripts/my_dashboard.cmake -V
+#
+# By default the source and build trees will be placed in the path
+# "../My Tests/" relative to your script location.
+#
+# The following variables may be set before including this script
+# to configure it:
+#
+#   dashboard_model           = Nightly | Experimental
+#   dashboard_root_name       = Change name of "MyTests" directory
+#   dashboard_source_name     = Name of source directory (adios)
+#   dashboard_binary_name     = Name of binary directory (adios-build)
+#   dashboard_cache           = Initial CMakeCache.txt file content
+
+#   dashboard_do_checkout  = True to enable source checkout via git
+#   dashboard_do_update    = True to enable source update
+#   dashboard_do_configure = True to enable the Configure step
+#   dashboard_do_build     = True to enable the Build step
+#   dashboard_do_test      = True to enable the Test step
+#   dashboard_do_coverage  = True to enable coverage (ex: gcov)
+#   dashboard_do_memcheck  = True to enable memcheck (ex: valgrind)
+
+#   CTEST_GIT_COMMAND     = path to git command-line client
+#   CTEST_BUILD_FLAGS     = build tool arguments (ex: -j2)
+#   CTEST_DASHBOARD_ROOT  = Where to put source and build trees
+#   CTEST_TEST_CTEST      = Whether to run long CTestTest* tests
+#   CTEST_TEST_TIMEOUT    = Per-test timeout length
+#   CTEST_TEST_ARGS       = ctest_test args (ex: PARALLEL_LEVEL 4)
+#   CMAKE_MAKE_PROGRAM    = Path to "make" tool to use
+#
+# Options to configure Git:
+#   dashboard_git_url      = Custom git clone url
+#   dashboard_git_branch   = Custom remote branch to track
+#   dashboard_git_crlf     = Value of core.autocrlf for repository
+#
+
+# For Makefile generators the script may be executed from an
+# environment already configured to use the desired compilers.
+# Alternatively the environment may be set at the top of the script:
+#
+#   set(ENV{CC}  /path/to/cc)   # C compiler
+#   set(ENV{CXX} /path/to/cxx)  # C++ compiler
+#   set(ENV{FC}  /path/to/fc)   # Fortran compiler (optional)
+#   set(ENV{LD_LIBRARY_PATH} /path/to/vendor/lib) # (if necessary)
+
+set(CTEST_PROJECT_NAME "ADIOS2")
+set(CTEST_DROP_SITE "open.cdash.org")
+set(dashboard_git_url "https://github.com/ornladios/ADIOS2.git")
+
+if(NOT ADIOS_CTEST_SUBMIT_NOTES)
+  set(ADIOS_CTEST_SUBMIT_NOTES TRUE)
+endif()
+if(NOT dashboard_root_name)
+  set(dashboard_root_name "Builds/My Tests")
+endif()
+if(NOT dashboard_source_name)
+  set(dashboard_source_name "ADIOS2")
+endif()
+if(NOT dashboard_model)
+  set(dashboard_model Experimental)
+endif()
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
diff --git a/scripts/dashboard/common.cmake b/scripts/dashboard/common.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..ac88d93726957d9728a67b9e0b1d6f81f42a3d2a
--- /dev/null
+++ b/scripts/dashboard/common.cmake
@@ -0,0 +1,460 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+#
+# "Universal" Dashboard Script
+#
+# This script contains basic dashboard driver code common to all
+# clients and projects.  It is a combination of the universal.cmake script in
+# the Kitware DashboardScriptsNG repo and cmake_common.cmake used by CMake
+# dashboards.
+#
+# Create a project-specific common script with code of the following form,
+# where the final line includes this script.
+#
+#   set(CTEST_PROJECT_NAME "OpenChemistry")
+#   set(CTEST_DROP_SITE "cdash.openchemistry.org")
+#
+#   set(dashboard_git_url "git://source.openchemistry.org/openchemistry.git")
+#   set(dashboard_root_name "MyTests")
+#   set(dashboard_source_name "openchemistry")
+#
+#   get_filename_component(dir ${CMAKE_CURRENT_LIST_FILE} PATH)
+#   include(${dir}/universal.cmake)
+#
+# The following variables may be set before including this script
+# to configure it:
+#
+#   dashboard_model       = Nightly | Experimental
+#   dashboard_root_name   = Change name of "My Tests" directory
+#   dashboard_source_name = Name of source directory (CMake)
+#   dashboard_binary_name = Name of binary directory (CMake-build)
+#   dashboard_cache       = Initial CMakeCache.txt file content
+#   dashboard_track       = The name of the CDash "Track" to submit to
+
+#   dashboard_do_checkout  = True to enable source checkout via git
+#   dashboard_do_update    = True to enable the Update step
+#   dashboard_do_configure = True to enable the Configure step
+#   dashboard_do_build     = True to enable the Build step
+#   dashboard_do_test      = True to enable the Test step
+#   dashboard_do_coverage  = True to enable coverage (ex: gcov)
+#   dashboard_do_memcheck  = True to enable memcheck (ex: valgrind)
+
+#   CTEST_GIT_COMMAND     = path to git command-line client
+#   CTEST_BUILD_FLAGS     = build tool arguments (ex: -j2)
+#   CTEST_DASHBOARD_ROOT  = Where to put source and build trees
+#   CTEST_TEST_CTEST      = Whether to run long CTestTest* tests
+#   CTEST_TEST_TIMEOUT    = Per-test timeout length
+#   CTEST_TEST_ARGS       = ctest_test args (ex: PARALLEL_LEVEL 4)
+#   CMAKE_MAKE_PROGRAM    = Path to "make" tool to use
+#
+# Options to configure Git:
+#   dashboard_git_url      = Custom git clone url
+#   dashboard_git_branch   = Custom remote branch to track
+#   dashboard_git_crlf     = Value of core.autocrlf for repository
+#
+# For Makefile generators the script may be executed from an
+# environment already configured to use the desired compilers.
+# Alternatively the environment may be set at the top of the script:
+#
+#   set(ENV{CC}  /path/to/cc)   # C compiler
+#   set(ENV{CXX} /path/to/cxx)  # C++ compiler
+#   set(ENV{FC}  /path/to/fc)   # Fortran compiler (optional)
+#   set(ENV{LD_LIBRARY_PATH} /path/to/vendor/lib) # (if necessary)
+
+cmake_minimum_required(VERSION 2.8.2 FATAL_ERROR)
+
+if(NOT DEFINED dashboard_full)
+  set(dashboard_full TRUE)
+endif()
+
+# Initialize all build steps to "ON"
+if(NOT DEFINED dashboard_do_update)
+  set(dashboard_do_update ${dashboard_full})
+endif()
+
+if(NOT DEFINED dashboard_do_checkout)
+  set(dashboard_do_checkout ${dashboard_full})
+endif()
+
+if(NOT DEFINED dashboard_do_configure)
+  set(dashboard_do_configure ${dashboard_full})
+endif()
+
+if(NOT DEFINED dashboard_do_build)
+  set(dashboard_do_build ${dashboard_full})
+endif()
+
+if(NOT DEFINED dashboard_do_test)
+  set(dashboard_do_test ${dashboard_full})
+endif()
+
+# Default code coverage and memtesting to off
+if(NOT DEFINED dashboard_do_coverage)
+  set(dashboard_do_coverage FALSE)
+endif()
+
+if(NOT DEFINED dashboard_do_memcheck)
+  set(dashboard_do_memcheck FALSE)
+endif()
+
+if(NOT DEFINED dashboard_fresh)
+  if(dashboard_full OR dashboard_do_update)
+    set(dashboard_fresh TRUE)
+  else()
+    set(dashboard_fresh FALSE)
+  endif()
+endif()
+
+if(NOT DEFINED CTEST_PROJECT_NAME)
+  message(FATAL_ERROR "project-specific script including 'universal.cmake' should set CTEST_PROJECT_NAME")
+endif()
+
+if(NOT DEFINED dashboard_user_home)
+  set(dashboard_user_home "$ENV{HOME}")
+endif()
+
+# Select the top dashboard directory.
+if(NOT DEFINED dashboard_root_name)
+  set(dashboard_root_name "My Tests")
+endif()
+if(NOT DEFINED CTEST_DASHBOARD_ROOT)
+  get_filename_component(CTEST_DASHBOARD_ROOT "${CTEST_SCRIPT_DIRECTORY}/../${dashboard_root_name}" ABSOLUTE)
+endif()
+
+# Select the model (Nightly, Experimental, Continuous).
+if(NOT DEFINED dashboard_model)
+  set(dashboard_model Nightly)
+endif()
+if(NOT "${dashboard_model}" MATCHES "^(Nightly|Experimental)$")
+  message(FATAL_ERROR "dashboard_model must be Nightly or Experimental")
+endif()
+
+
+# Default to a Debug build.
+if(NOT DEFINED CTEST_BUILD_CONFIGURATION)
+  set(CTEST_BUILD_CONFIGURATION Debug)
+endif()
+
+# Choose CTest reporting mode.
+if(NOT "${CTEST_CMAKE_GENERATOR}" MATCHES "Make|Ninja")
+  # Launchers work only with Makefile and Ninja generators.
+  set(CTEST_USE_LAUNCHERS 0)
+elseif(NOT DEFINED CTEST_USE_LAUNCHERS)
+  # The setting is ignored by CTest < 2.8 so we need no version test.
+  set(CTEST_USE_LAUNCHERS 1)
+endif()
+
+# Configure testing.
+if(NOT DEFINED CTEST_TEST_CTEST)
+  set(CTEST_TEST_CTEST 1)
+endif()
+if(NOT CTEST_TEST_TIMEOUT)
+  set(CTEST_TEST_TIMEOUT 1500)
+endif()
+
+# Select Git source to use.
+if(dashboard_do_checkout)
+  if(NOT DEFINED dashboard_git_url)
+    message(FATAL_ERROR "project-specific script including 'universal.cmake' should set dashboard_git_url")
+  endif()
+  if(NOT DEFINED dashboard_git_branch)
+    set(dashboard_git_branch master)
+  endif()
+  if(NOT DEFINED dashboard_git_crlf)
+    if(UNIX)
+      set(dashboard_git_crlf false)
+    else()
+      set(dashboard_git_crlf true)
+    endif()
+  endif()
+
+  # Look for a GIT command-line client.
+  if(NOT DEFINED CTEST_GIT_COMMAND)
+    find_program(CTEST_GIT_COMMAND
+      NAMES git git.cmd
+      PATH_SUFFIXES Git/cmd Git/bin
+      )
+  endif()
+  if(NOT CTEST_GIT_COMMAND)
+    message(FATAL_ERROR "CTEST_GIT_COMMAND not available!")
+  endif()
+endif()
+
+# Select a source directory name.
+if(NOT DEFINED CTEST_SOURCE_DIRECTORY)
+  if(DEFINED dashboard_source_name)
+    set(CTEST_SOURCE_DIRECTORY ${CTEST_DASHBOARD_ROOT}/${dashboard_source_name})
+  else()
+    set(CTEST_SOURCE_DIRECTORY ${CTEST_DASHBOARD_ROOT}/${CTEST_PROJECT_NAME})
+  endif()
+endif()
+
+# Select a build directory name.
+if(NOT DEFINED CTEST_BINARY_DIRECTORY)
+  if(DEFINED dashboard_binary_name)
+    set(CTEST_BINARY_DIRECTORY ${CTEST_DASHBOARD_ROOT}/${dashboard_binary_name})
+  else()
+    set(CTEST_BINARY_DIRECTORY ${CTEST_SOURCE_DIRECTORY}-build)
+  endif()
+endif()
+
+macro(dashboard_git)
+  execute_process(
+    COMMAND ${CTEST_GIT_COMMAND} ${ARGN}
+    WORKING_DIRECTORY "${CTEST_SOURCE_DIRECTORY}"
+    OUTPUT_VARIABLE dashboard_git_output
+    ERROR_VARIABLE dashboard_git_output
+    RESULT_VARIABLE dashboard_git_failed
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE
+    )
+endmacro()
+
+if(dashboard_do_checkout)
+  # Delete source tree if it is incompatible with current VCS.
+  if(EXISTS ${CTEST_SOURCE_DIRECTORY})
+    if(NOT EXISTS "${CTEST_SOURCE_DIRECTORY}/.git")
+      set(vcs_refresh "because it is not managed by git.")
+    else()
+      execute_process(
+        COMMAND ${CTEST_GIT_COMMAND} reset --hard
+        WORKING_DIRECTORY "${CTEST_SOURCE_DIRECTORY}"
+        OUTPUT_VARIABLE output
+        ERROR_VARIABLE output
+        RESULT_VARIABLE failed
+        )
+      if(failed)
+        set(vcs_refresh "because its .git may be corrupted.")
+      endif()
+    endif()
+    if(vcs_refresh AND "${CTEST_SOURCE_DIRECTORY}" MATCHES "/CMake[^/]*")
+      message("Deleting source tree\n")
+      message("  ${CTEST_SOURCE_DIRECTORY}\n${vcs_refresh}")
+      file(REMOVE_RECURSE "${CTEST_SOURCE_DIRECTORY}")
+    endif()
+  endif()
+
+  # Support initial checkout if necessary.
+  if(NOT EXISTS "${CTEST_SOURCE_DIRECTORY}"
+      AND NOT DEFINED CTEST_CHECKOUT_COMMAND)
+  # Generate an initial checkout script.
+  get_filename_component(_name "${CTEST_SOURCE_DIRECTORY}" NAME)
+  set(ctest_checkout_script ${CTEST_DASHBOARD_ROOT}/${_name}-init.cmake)
+  file(WRITE ${ctest_checkout_script} "# git repo init script for ${_name}
+execute_process(
+  COMMAND \"${CTEST_GIT_COMMAND}\" clone -n -- \"${dashboard_git_url}\"
+          \"${CTEST_SOURCE_DIRECTORY}\"
+  )
+if(EXISTS \"${CTEST_SOURCE_DIRECTORY}/.git\")
+  execute_process(
+    COMMAND \"${CTEST_GIT_COMMAND}\" config core.autocrlf ${dashboard_git_crlf}
+    WORKING_DIRECTORY \"${CTEST_SOURCE_DIRECTORY}\"
+    )
+  execute_process(
+    COMMAND \"${CTEST_GIT_COMMAND}\" fetch
+    WORKING_DIRECTORY \"${CTEST_SOURCE_DIRECTORY}\"
+    )
+  execute_process(
+    COMMAND \"${CTEST_GIT_COMMAND}\" checkout ${dashboard_git_branch}
+    WORKING_DIRECTORY \"${CTEST_SOURCE_DIRECTORY}\"
+    )
+endif()"
+  )
+  set(CTEST_CHECKOUT_COMMAND "\"${CMAKE_COMMAND}\" -P \"${ctest_checkout_script}\"")
+  elseif(EXISTS "${CTEST_SOURCE_DIRECTORY}/.git")
+    # Upstream URL.
+    dashboard_git(config --get remote.origin.url)
+    if(NOT dashboard_git_output STREQUAL "${dashboard_git_url}")
+      dashboard_git(config remote.origin.url "${dashboard_git_url}")
+    endif()
+
+    # Local checkout.
+    dashboard_git(symbolic-ref HEAD)
+    if(NOT dashboard_git_output STREQUAL "${dashboard_git_branch}")
+      dashboard_git(checkout ${dashboard_git_branch})
+      if(dashboard_git_failed)
+        message(FATAL_ERROR "Failed to checkout branch ${dashboard_git_branch}:\n${dashboard_git_output}")
+      endif()
+    endif()
+  endif()
+endif()
+
+#-----------------------------------------------------------------------------
+
+# Check for required variables.
+foreach(req
+    CTEST_CMAKE_GENERATOR
+    CTEST_SITE
+    CTEST_BUILD_NAME
+    )
+  if(NOT DEFINED ${req})
+    message(FATAL_ERROR "The containing script must set ${req}")
+  endif()
+endforeach(req)
+
+# Print summary information.
+set(vars "")
+foreach(v
+    CTEST_SITE
+    CTEST_BUILD_NAME
+    CTEST_SOURCE_DIRECTORY
+    CTEST_BINARY_DIRECTORY
+    CTEST_CMAKE_GENERATOR
+    CTEST_BUILD_CONFIGURATION
+    CTEST_GIT_COMMAND
+    CTEST_CHECKOUT_COMMAND
+    CTEST_CONFIGURE_COMMAND
+    CTEST_SCRIPT_DIRECTORY
+    CTEST_USE_LAUNCHERS
+    )
+  set(vars "${vars}  ${v}=[${${v}}]\n")
+endforeach(v)
+message("Dashboard script configuration:\n${vars}\n")
+
+# Avoid non-ascii characters in tool output.
+set(ENV{LC_ALL} C)
+
+# Helper macro to write the initial cache.
+macro(write_cache)
+  set(cache_build_type "")
+  set(cache_make_program "")
+  if(CTEST_CMAKE_GENERATOR MATCHES "Make|Ninja")
+    set(cache_build_type CMAKE_BUILD_TYPE:STRING=${CTEST_BUILD_CONFIGURATION})
+    if(CMAKE_MAKE_PROGRAM)
+      set(cache_make_program CMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM})
+    endif()
+  endif()
+  file(WRITE ${CTEST_BINARY_DIRECTORY}/CMakeCache.txt "
+SITE:STRING=${CTEST_SITE}
+BUILDNAME:STRING=${CTEST_BUILD_NAME}
+CTEST_TEST_CTEST:BOOL=${CTEST_TEST_CTEST}
+CTEST_USE_LAUNCHERS:BOOL=${CTEST_USE_LAUNCHERS}
+DART_TESTING_TIMEOUT:STRING=${CTEST_TEST_TIMEOUT}
+GIT_EXECUTABLE:FILEPATH=${CTEST_GIT_COMMAND}
+${cache_build_type}
+${cache_make_program}
+${dashboard_cache}
+")
+endmacro(write_cache)
+
+if(COMMAND dashboard_hook_init)
+  dashboard_hook_init()
+endif()
+
+if(dashboard_fresh)
+  if(EXISTS CTEST_BINARY_DIRECTORY)
+    message("Clearing build tree...")
+    ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY})
+  else()
+    file(MAKE_DIRECTORY "${CTEST_BINARY_DIRECTORY}")
+  endif()
+  message("Starting fresh build...")
+  write_cache()
+endif()
+
+# Start a new submission.
+if(dashboard_track)
+  set(dashboard_track_arg TRACK "${dashboard_track}")
+endif()
+message("Calling ctest_start")
+if(dashboard_fresh)
+  if(COMMAND dashboard_hook_start)
+    dashboard_hook_start()
+  endif()
+  ctest_start(${dashboard_model} ${dashboard_track_arg})
+  ctest_submit(PARTS Start)
+  if(COMMAND dashboard_hook_started)
+    dashboard_hook_started()
+  endif()
+else()
+  ctest_start(${dashboard_model} ${dashboard_track_arg} APPEND)
+endif()
+
+# Look for updates.
+if(dashboard_do_update)
+  if(COMMAND dashboard_hook_update)
+    dashboard_hook_update()
+  endif()
+  message("Calling ctest_update...")
+  ctest_update(RETURN_VALUE count)
+  set(CTEST_CHECKOUT_COMMAND) # checkout on first iteration only
+  message("Found ${count} changed files")
+
+  if(ADIOS_CTEST_SUBMIT_NOTES)
+    message("Submitting dashboard scripts as Notes")
+    # Send the main script as a note while submitting the Update part
+    set(CTEST_NOTES_FILES
+      "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}"
+      "${CMAKE_CURRENT_LIST_FILE}"
+    )
+    ctest_submit(PARTS Update Notes)
+  else()
+    message("Skipping notes submission for Update step")
+    ctest_submit(PARTS Update)
+  endif()
+endif()
+
+if(dashboard_do_configure)
+  if(COMMAND dashboard_hook_configure)
+    dashboard_hook_configure()
+  endif()
+  message("Calling ctest_configure")
+  ctest_configure(${dashboard_configure_args})
+  if(ADIOS_CTEST_SUBMIT_NOTES)
+    message("Submitting CMakeCache.txt as Notes")
+    set(CTEST_NOTES_FILES "${CTEST_BINARY_DIRECTORY}/CMakeCache.txt")
+    ctest_submit(PARTS Configure Notes)
+  else()
+    message("Skipping notes submission for Configure step")
+    ctest_submit(PARTS Configure)
+  endif()
+endif()
+
+ctest_read_custom_files(${CTEST_BINARY_DIRECTORY})
+
+if(dashboard_do_build)
+  if(COMMAND dashboard_hook_build)
+    dashboard_hook_build()
+  endif()
+  message("Calling ctest_build")
+  ctest_build()
+  ctest_submit(PARTS Build)
+endif()
+
+if(dashboard_do_test)
+  if(COMMAND dashboard_hook_test)
+    dashboard_hook_test()
+  endif()
+  message("Calling ctest_test")
+  ctest_test(${CTEST_TEST_ARGS} RETURN_VALUE TEST_RESULTS)
+  if(${TEST_RESULTS} EQUAL 0)
+    message("ctest test results return value: ${TEST_RESULTS}")
+  else()
+    message(SEND_ERROR "Some tests failed")
+  endif()
+  ctest_submit(PARTS Test)
+endif()
+
+if(dashboard_do_coverage)
+  if(COMMAND dashboard_hook_coverage)
+    dashboard_hook_coverage()
+  endif()
+  message("Calling ctest_coverage")
+  ctest_coverage()
+  ctest_submit(PARTS Coverage)
+endif()
+
+if(dashboard_do_memcheck)
+  if(COMMAND dashboard_hook_memcheck)
+    dashboard_hook_memcheck()
+  endif()
+  message("Calling ctest_memcheck")
+  ctest_memcheck()
+  ctest_submit(PARTS MemCheck)
+endif()
+
+if(COMMAND dashboard_hook_end)
+  dashboard_hook_end()
+endif()
diff --git a/source/adios2/ADIOSConfig.h.in b/source/adios2/ADIOSConfig.h.in
index 44b9128c1f50b81c4d0458fe163833c1138aecdb..9a2570846194bfab15913bcf0d248fc3de9641ed 100644
--- a/source/adios2/ADIOSConfig.h.in
+++ b/source/adios2/ADIOSConfig.h.in
@@ -45,10 +45,15 @@
 /* CMake Option: ADIOS_USE_DataMan=ON */
 #cmakedefine ADIOS2_HAVE_DATAMAN
 
+/* CMake Option: ADIOS_USE_SysVShMem=ON */
+#cmakedefine ADIOS2_HAVE_SYSVSHMEM
+
+/* Optional Language Bindings: */
+
 /* CMake Option: ADIOS_USE_Python=ON */
 #cmakedefine ADIOS2_HAVE_PYTHON
 
-/* CMake Option: ADIOS_USE_SysVShMem=ON */
-#cmakedefine ADIOS2_HAVE_SYSVSHMEM
+/* CMake Option: ADIOS_USE_Fortran=ON */
+#cmakedefine ADIOS2_HAVE_FORTRAN
 
 #endif /* ADIOSCONFIG_H_ */
diff --git a/source/adios2/ADIOSMPI.h b/source/adios2/ADIOSMPI.h
index 95ae7619aebdb9b32af02a9f6adbd7b2488f06a3..7fc6ac05188e4d41f4c51fe0131643229a1138e5 100644
--- a/source/adios2/ADIOSMPI.h
+++ b/source/adios2/ADIOSMPI.h
@@ -12,6 +12,7 @@
 #include <mpi.h>
 #else
 #include "adios2/mpidummy.h"
+using namespace adios2::mpi;
 #endif
 
 #include <climits> //UXXX_MAX
diff --git a/source/adios2/ADIOSMPICommOnly.h b/source/adios2/ADIOSMPICommOnly.h
index 0a10379f5e32289d55ffcd984e59450d6efc26bc..2dba99e435ecfb0bb83a910ae31b01699a244456 100644
--- a/source/adios2/ADIOSMPICommOnly.h
+++ b/source/adios2/ADIOSMPICommOnly.h
@@ -11,10 +11,18 @@
 #ifdef ADIOS2_HAVE_MPI
 #include <mpi.h>
 #else
+#ifdef __cplusplus
 namespace adios2
 {
+namespace mpi
+{
 using MPI_Comm = int;
+} // end namespace mpi
 } // end namespace adios
+using adios2::mpi::MPI_Comm;
+#else
+typedef int MPI_Comm;
+#endif
 #endif
 
 #endif /* ADIOS2_ADIOSMPICOMMONLY_H_ */
diff --git a/source/adios2/ADIOSMacros.h b/source/adios2/ADIOSMacros.h
index f3043732b588cbde469d3b5f9e06adcc85afa03b..1f01f9ea600594a77b764181b90a504aa3fd4174 100644
--- a/source/adios2/ADIOSMacros.h
+++ b/source/adios2/ADIOSMacros.h
@@ -8,6 +8,8 @@
 #ifndef ADIOS2_ADIOSMACROS_H
 #define ADIOS2_ADIOSMACROS_H
 
+#include <string>
+
 #include "adios2/ADIOSTypes.h"
 // The ADIOS_FOREACH_TYPE_1ARG macro assumes the given argument is a macro which
 // takes a single argument that is a type and then inserts the given MACRO for
@@ -24,6 +26,7 @@
 //
 #define ADIOS2_FOREACH_TYPE_1ARG(MACRO)                                        \
     MACRO(char)                                                                \
+    MACRO(signed char)                                                         \
     MACRO(unsigned char)                                                       \
     MACRO(short)                                                               \
     MACRO(unsigned short)                                                      \
@@ -42,6 +45,7 @@
 
 #define ADIOS2_FOREACH_PRIMITIVE_TYPE_1ARG(MACRO)                              \
     MACRO(char)                                                                \
+    MACRO(signed char)                                                         \
     MACRO(unsigned char)                                                       \
     MACRO(short)                                                               \
     MACRO(unsigned short)                                                      \
@@ -49,6 +53,8 @@
     MACRO(unsigned int)                                                        \
     MACRO(long int)                                                            \
     MACRO(unsigned long int)                                                   \
+    MACRO(long long int)                                                       \
+    MACRO(unsigned long long int)                                              \
     MACRO(float)                                                               \
     MACRO(double)
 
@@ -62,4 +68,78 @@
     MACRO(float)                                                               \
     MACRO(double)
 
+#define ADIOS2_FOREACH_ATTRIBUTE_TYPE_1ARG(MACRO)                              \
+    MACRO(std::string)                                                         \
+    MACRO(char)                                                                \
+    MACRO(signed char)                                                         \
+    MACRO(unsigned char)                                                       \
+    MACRO(short)                                                               \
+    MACRO(unsigned short)                                                      \
+    MACRO(int)                                                                 \
+    MACRO(unsigned int)                                                        \
+    MACRO(long int)                                                            \
+    MACRO(unsigned long int)                                                   \
+    MACRO(long long int)                                                       \
+    MACRO(unsigned long long int)                                              \
+    MACRO(float)                                                               \
+    MACRO(double)                                                              \
+    MACRO(long double)
+
+// The ADIOS_FOREACH_TYPE_2ARGS macro assumes the given argument is a macro
+// which takes two arguments, the first being a type, and the second being a
+// label for that type, i.e. std::complex<float> and CFloat.
+//
+// An example of this might be to define a virtual method for each known
+// type, and since the method is virtual then it cannot be a template.
+// For example:
+//
+//   #define declare_foo(T,L) virtual const T& foo ## L (std::string bar);
+//   ADIOS_FOREACH_TYPE_2ARGS(declare_foo)
+//   #undef declare_foo
+//
+//   is equivalent to:
+//
+//   virtual           char& foo_Char(std::string bar);
+//   virtual unsigned  char& foo_UChar(std::string bar);
+//   virtual          short& foo_Short(std::string bar);
+//   virtual unsigned short& foo_UShort(std::string bar);
+//   ...
+//   virtual std::complex<long double>& foo_CLDouble(std::string bar);
+//
+#define ADIOS2_FOREACH_TYPE_2ARGS(MACRO)                                       \
+    MACRO(char, Char)                                                          \
+    MACRO(unsigned char, UChar)                                                \
+    MACRO(short, Short)                                                        \
+    MACRO(unsigned short, UShort)                                              \
+    MACRO(int, Int)                                                            \
+    MACRO(unsigned int, UInt)                                                  \
+    MACRO(long int, LInt)                                                      \
+    MACRO(long long int, LLInt)                                                \
+    MACRO(unsigned long int, ULInt)                                            \
+    MACRO(unsigned long long int, ULLInt)                                      \
+    MACRO(float, Float)                                                        \
+    MACRO(double, Double)                                                      \
+    MACRO(long double, LDouble)                                                \
+    MACRO(std::complex<float>, CFloat)                                         \
+    MACRO(std::complex<double>, CDouble)                                       \
+    MACRO(std::complex<long double>, CLDouble)
+
+#define ADIOS2_FOREACH_PRIMITIVE_TYPE_2ARGS(MACRO)                             \
+    MACRO(char, Char)                                                          \
+    MACRO(unsigned char, UChar)                                                \
+    MACRO(short, Short)                                                        \
+    MACRO(unsigned short, UShort)                                              \
+    MACRO(int, Int)                                                            \
+    MACRO(unsigned int, UInt)                                                  \
+    MACRO(long int, LInt)                                                      \
+    MACRO(long long int, LLInt)                                                \
+    MACRO(unsigned long int, ULInt)                                            \
+    MACRO(unsigned long long int, ULLInt)                                      \
+    MACRO(float, Float)                                                        \
+    MACRO(double, Double)
+
+#define ADIOS2_FOREACH_COMPLEX_TYPE_2ARGS(MACRO)                               \
+    MACRO(std::complex<float>, CFloat)                                         \
+    MACRO(std::complex<double>, CDouble)
+
 #endif /* ADIOS2_ADIOSMACROS_H */
diff --git a/source/adios2/ADIOSTypes.h b/source/adios2/ADIOSTypes.h
index bb806e56960f57681c2edd4ff500a94fc67103d5..0078b66b855ec5728cae3e43ad883c0d27fa1ddb 100644
--- a/source/adios2/ADIOSTypes.h
+++ b/source/adios2/ADIOSTypes.h
@@ -166,6 +166,13 @@ constexpr float DefaultBufferGrowthFactor(1.05f);
  *  2Gb - 100Kb (tolerance)*/
 constexpr size_t DefaultMaxFileBatchSize(2147381248);
 
+constexpr char PathSeparator =
+#ifdef _WIN32
+    '\\';
+#else
+    '/';
+#endif
+
 // adios alias values and types
 constexpr bool DebugON = true;
 constexpr bool DebugOFF = false;
diff --git a/source/adios2/CMakeLists.txt b/source/adios2/CMakeLists.txt
index f0891b33b70a60ff70f7a3d075c4ba6404c76f28..8115d1ead819073920e2d3fea4ec1a707449459b 100644
--- a/source/adios2/CMakeLists.txt
+++ b/source/adios2/CMakeLists.txt
@@ -4,6 +4,8 @@
 #------------------------------------------------------------------------------#
   
 add_library(adios2
+  core/Attribute.cpp core/Attribute.tcc
+  core/AttributeBase.cpp
   core/ADIOS.cpp
   core/Engine.cpp
   core/IO.cpp core/IO.tcc
@@ -16,6 +18,7 @@ add_library(adios2
   core/VariableCompound.cpp core/VariableCompound.tcc
   
 #helper
+  helper/adiosDynamicBinder.h  helper/adiosDynamicBinder.cpp
   helper/adiosMath.cpp
   helper/adiosMPIFunctions.cpp
   helper/adiosString.cpp
@@ -26,6 +29,10 @@ add_library(adios2
 #  engine/bp/BPFileReader.cpp
   engine/bp/BPFileWriter.cpp engine/bp/BPFileWriter.tcc
 
+  engine/plugin/PluginEngine.h engine/plugin/PluginEngine.inl
+  engine/plugin/PluginEngine.cpp
+  engine/plugin/PluginEngineInterface.h engine/plugin/PluginEngineInterface.cpp
+
   toolkit/capsule/Capsule.cpp
   toolkit/capsule/heap/STLVector.cpp 
  
@@ -49,9 +56,7 @@ target_include_directories(adios2
     $<BUILD_INTERFACE:${ADIOS2_BINARY_DIR}/source>
     $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
 )
-target_link_libraries(adios2 PRIVATE adios2sys pugixml)
-
-find_package(Threads REQUIRED)
+target_link_libraries(adios2 PRIVATE adios2sys_interface pugixml)
 target_link_libraries(adios2 PUBLIC ${CMAKE_THREAD_LIBS_INIT})
 
 if(UNIX)
@@ -79,19 +84,16 @@ endif()
 
 
 if(ADIOS2_HAVE_BZip2)
-  find_package(BZip2 REQUIRED)
   target_sources(adios2 PRIVATE transform/compress/CompressBZip2.cpp)
   target_link_libraries(adios2 PRIVATE BZip2::BZip2)
 endif()
 
 if(ADIOS2_HAVE_ZFP)
-  find_package(ZFP REQUIRED)
   target_sources(adios2 PRIVATE transform/compress/CompressZfp.cpp)
   target_link_libraries(adios2 PRIVATE zfp::zfp)
 endif()
 
 if(ADIOS2_HAVE_MPI)
-  find_package(MPI COMPONENTS C REQUIRED)
   target_include_directories(adios2 PUBLIC ${MPI_C_INCLUDE_PATH})
   target_link_libraries(adios2 PUBLIC ${MPI_C_LIBRARIES})
 else()
@@ -99,12 +101,6 @@ else()
 endif()
 
 if(ADIOS2_HAVE_ADIOS1)
-  if(ADIOS2_HAVE_MPI)
-    find_package(ADIOS1 1.12.0 REQUIRED)
-  else()
-    find_package(ADIOS1 1.12.0 COMPONENTS sequential REQUIRED)
-  endif()
-
   target_sources(adios2 PRIVATE
     engine/adios1/ADIOS1Reader.cpp
     engine/adios1/ADIOS1Writer.cpp
@@ -116,18 +112,6 @@ if(ADIOS2_HAVE_ADIOS1)
 endif()
 
 if(ADIOS2_HAVE_HDF5)
-  find_package(HDF5 REQUIRED)
-  if(ADIOS2_HAVE_MPI AND (NOT HDF5_IS_PARALLEL))
-    message(FATAL_ERROR
-      "A sequential version of HDF5 was detected but the parallel version "
-      "of ADIOS is being built, which requires a parallel HDF5."
-    )
-  elseif((NOT ADIOS2_HAVE_MPI) AND HDF5_IS_PARALLEL)
-    message(FATAL_ERROR
-      "A parallel version of HDF5 was detected but the sequential version "
-      "of ADIOS is being built, which requires a sequential HDF5."
-    )
-  endif()
   if(HDF5_C_INCLUDE_DIRS)
     target_include_directories(adios2 PRIVATE ${HDF5_C_INCLUDE_DIRS})
   else()
diff --git a/source/adios2/core/Attribute.cpp b/source/adios2/core/Attribute.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8e473101dcaec65e405c397f8e6122526d19a8f3
--- /dev/null
+++ b/source/adios2/core/Attribute.cpp
@@ -0,0 +1,17 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * Attribute.cpp : needed for template separation using Attribute.tcc
+ *
+ *  Created on: Aug 3, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include "Attribute.h"
+#include "Attribute.tcc"
+
+namespace adios2
+{
+
+} // end namespace adios2
diff --git a/source/adios2/core/Attribute.h b/source/adios2/core/Attribute.h
new file mode 100644
index 0000000000000000000000000000000000000000..ebac57baa185837ba88c419fa7a30d8c312457f9
--- /dev/null
+++ b/source/adios2/core/Attribute.h
@@ -0,0 +1,48 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * Attribute.h : template class that defines typed attributes
+ *
+ *  Created on: Aug 1, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#ifndef ADIOS2_CORE_ATTRIBUTE_H_
+#define ADIOS2_CORE_ATTRIBUTE_H_
+
+#include "adios2/core/AttributeBase.h"
+
+namespace adios2
+{
+/** @brief Attributes provide complementary information to IO Variables*/
+template <class T>
+class Attribute : public AttributeBase
+{
+
+public:
+    std::vector<T> m_DataArray;
+    T m_DataSingleValue;
+
+    /**
+     * Data array constructor
+     * @param name
+     * @param data
+     * @param elements
+     */
+    Attribute<T>(const std::string &name, const T *data, const size_t elements);
+
+    /**
+     * Single value constructor
+     * @param name
+     * @param data
+     * @param elements
+     */
+    Attribute<T>(const std::string &name, const T &data);
+
+    ~Attribute<T>() = default;
+};
+
+} // end namespace adios2
+
+#endif /* ADIOS2_CORE_ATTRIBUTE_H_ */
diff --git a/source/adios2/core/Attribute.tcc b/source/adios2/core/Attribute.tcc
new file mode 100644
index 0000000000000000000000000000000000000000..69846e400f8e7ba87ca5f66d3f06f76d6f7fdba5
--- /dev/null
+++ b/source/adios2/core/Attribute.tcc
@@ -0,0 +1,44 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * Attribute.tcc
+ *
+ *  Created on: Aug 1, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#ifndef ADIOS2_CORE_ATTRIBUTE_TCC_
+#define ADIOS2_CORE_ATTRIBUTE_TCC_
+
+#include "Attribute.h"
+
+#include "adios2/ADIOSMacros.h"
+#include "adios2/helper/adiosFunctions.h" //GetType<T>
+
+namespace adios2
+{
+
+#define declare_type(T)                                                        \
+                                                                               \
+    template <>                                                                \
+    Attribute<T>::Attribute(const std::string &name, const T *array,           \
+                            const size_t elements)                             \
+    : AttributeBase(name, GetType<T>(), elements, false),                      \
+      m_DataArray(std::vector<T>(array, array + elements)),                    \
+      m_DataSingleValue()                                                      \
+    {                                                                          \
+    }                                                                          \
+                                                                               \
+    template <>                                                                \
+    Attribute<T>::Attribute(const std::string &name, const T &value)           \
+    : AttributeBase(name, GetType<T>(), 1, true), m_DataSingleValue(value)     \
+    {                                                                          \
+    }
+
+ADIOS2_FOREACH_ATTRIBUTE_TYPE_1ARG(declare_type)
+#undef declare_type
+
+} // end namespace adios2
+
+#endif /* ADIOS2_CORE_ATTRIBUTE_TCC_ */
diff --git a/source/adios2/core/AttributeBase.cpp b/source/adios2/core/AttributeBase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f1c7eafedb400ceb4894cff84530c4f3f20676f2
--- /dev/null
+++ b/source/adios2/core/AttributeBase.cpp
@@ -0,0 +1,23 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * AttributeBase.cpp
+ *
+ *  Created on: Aug 1, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include "AttributeBase.h"
+
+namespace adios2
+{
+
+AttributeBase::AttributeBase(const std::string &name, const std::string type,
+                             const size_t elements, const bool isSingleValue)
+: m_Name(name), m_Type(type), m_Elements(elements),
+  m_IsSingleValue(isSingleValue)
+{
+}
+
+} // end namespace adios2
diff --git a/source/adios2/core/AttributeBase.h b/source/adios2/core/AttributeBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..9ed5039d5bd153eb029b45bd68aef1669b6ef833
--- /dev/null
+++ b/source/adios2/core/AttributeBase.h
@@ -0,0 +1,47 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * AttributeBase.h : base class for Attribute<T> class, allows RTTI at read time
+ *
+ *  Created on: Aug 1, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#ifndef ADIOS2_CORE_ATTRIBUTEBASE_H_
+#define ADIOS2_CORE_ATTRIBUTEBASE_H_
+
+/// \cond EXCLUDE_FROM_DOXYGEN
+#include <string>
+/// \endcond
+
+#include "adios2/ADIOSConfig.h"
+#include "adios2/ADIOSTypes.h"
+
+namespace adios2
+{
+
+class AttributeBase
+{
+
+public:
+    const std::string m_Name;
+    const std::string m_Type;
+    const size_t m_Elements;
+    const bool m_IsSingleValue;
+
+    /**
+     * Unique constructor used by Attribute<T> derived class
+     * @param name
+     * @param type
+     * @param elements
+     */
+    AttributeBase(const std::string &name, const std::string type,
+                  const size_t elements, const bool isSingleValue);
+
+    virtual ~AttributeBase() = default;
+};
+
+} // end namespace adios2
+
+#endif /* ADIOS2_CORE_ATTRIBUTEBASE_H_ */
diff --git a/source/adios2/core/Engine.cpp b/source/adios2/core/Engine.cpp
index 51f7a1a78daa51ec7923a6f03c481c5812a9af59..9d19e892b7d357e35e1bbe3ef2de05ff4377df5c 100644
--- a/source/adios2/core/Engine.cpp
+++ b/source/adios2/core/Engine.cpp
@@ -35,7 +35,15 @@ void Engine::SetCallBack(
 {
 }
 
-// should these functions throw an exception?
+void Engine::Write(VariableBase &variable, const void *values)
+{
+    DoWrite(variable.m_Name, values);
+}
+
+void Engine::Write(const std::string &variableName, const void *values)
+{
+    DoWrite(variableName, values);
+}
 
 void Engine::Advance(const float /*timeout_sec*/) {}
 void Engine::Advance(const AdvanceMode /*mode*/, const float /*timeout_sec*/) {}
@@ -89,13 +97,26 @@ void Engine::DoWrite(const std::string &variableName, const void *values)
 
     if (type == "compound")
     {
-        DoWrite(m_IO.GetVariableCompound(variableName), values);
+        VariableCompound &variable = m_IO.GetVariableCompound(variableName);
+
+        if (m_DebugMode)
+        {
+            variable.CheckDimsBeforeWrite("Write " + variable.m_Name);
+        }
+
+        DoWrite(variable, values);
     }
 #define declare_type(T)                                                        \
     else if (type == GetType<T>())                                             \
     {                                                                          \
-        DoWrite(m_IO.GetVariable<T>(variableName),                             \
-                reinterpret_cast<const T *>(values));                          \
+        Variable<T> &variable = m_IO.GetVariable<T>(variableName);             \
+                                                                               \
+        if (m_DebugMode)                                                       \
+        {                                                                      \
+            variable.CheckDimsBeforeWrite("Write " + variable.m_Name);         \
+        }                                                                      \
+                                                                               \
+        DoWrite(variable, reinterpret_cast<const T *>(values));                \
     }
     ADIOS2_FOREACH_TYPE_1ARG(declare_type)
 #undef declare_type
@@ -107,88 +128,15 @@ VariableBase *Engine::InquireVariableUnknown(const std::string &name,
 {
     return nullptr;
 }
-Variable<char> *Engine::InquireVariableChar(const std::string &name,
-                                            const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned char> *Engine::InquireVariableUChar(const std::string &name,
-                                                      const bool readIn)
-{
-    return nullptr;
-}
-Variable<short> *Engine::InquireVariableShort(const std::string &name,
-                                              const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned short> *Engine::InquireVariableUShort(const std::string &name,
-                                                        const bool readIn)
-{
-    return nullptr;
-}
-Variable<int> *Engine::InquireVariableInt(const std::string &name,
-                                          const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned int> *Engine::InquireVariableUInt(const std::string &name,
-                                                    const bool readIn)
-{
-    return nullptr;
-}
-Variable<long int> *Engine::InquireVariableLInt(const std::string &name,
-                                                const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned long int> *
-Engine::InquireVariableULInt(const std::string &name, const bool readIn)
-{
-    return nullptr;
-}
-Variable<long long int> *Engine::InquireVariableLLInt(const std::string &name,
-                                                      const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned long long int> *
-Engine::InquireVariableULLInt(const std::string &name, const bool readIn)
-{
-    return nullptr;
-}
-
-Variable<float> *Engine::InquireVariableFloat(const std::string &name,
-                                              const bool readIn)
-{
-    return nullptr;
-}
-Variable<double> *Engine::InquireVariableDouble(const std::string &name,
-                                                const bool readIn)
-{
-    return nullptr;
-}
-Variable<long double> *Engine::InquireVariableLDouble(const std::string &name,
-                                                      const bool readIn)
-{
-    return nullptr;
-}
-Variable<cfloat> *Engine::InquireVariableCFloat(const std::string &name,
-                                                const bool readIn)
-{
-    return nullptr;
-}
-Variable<cdouble> *Engine::InquireVariableCDouble(const std::string &name,
-                                                  const bool readIn)
-{
-    return nullptr;
-}
 
-Variable<cldouble> *Engine::InquireVariableCLDouble(const std::string &name,
-                                                    const bool readIn)
-{
-    return nullptr;
-}
+#define define(T, L)                                                           \
+    Variable<T> *Engine::InquireVariable##L(const std::string &name,           \
+                                            const bool readIn)                 \
+    {                                                                          \
+        return nullptr;                                                        \
+    }
+ADIOS2_FOREACH_TYPE_2ARGS(define)
+#undef define
 
 #define declare_type(T)                                                        \
     void Engine::DoScheduleRead(Variable<T> &variable, const T *values)        \
@@ -212,4 +160,14 @@ void Engine::ThrowUp(const std::string function) const
                                 "\n");
 }
 
-} // end namespace adios
+#define declare_template_instantiation(T)                                      \
+    template void Engine::Write<T>(Variable<T> &, const T *);                  \
+    template void Engine::Write<T>(Variable<T> &, const T);                    \
+                                                                               \
+    template void Engine::Write<T>(const std::string &, const T *);            \
+    template void Engine::Write<T>(const std::string &, const T);
+
+ADIOS2_FOREACH_TYPE_1ARG(declare_template_instantiation)
+#undef declare_template_instantiation
+
+} // end namespace adios2
diff --git a/source/adios2/core/Engine.h b/source/adios2/core/Engine.h
index 89076d81ed938e553f5e098dfc90cb58eb5d9b58..3a405452f07e5dd2c381d3e6884eb0974900f5ec 100644
--- a/source/adios2/core/Engine.h
+++ b/source/adios2/core/Engine.h
@@ -37,6 +37,9 @@ namespace adios2
  * Close */
 class Engine
 {
+public:
+    using AdvanceAsyncCallback =
+        std::function<void(std::shared_ptr<adios2::Engine>)>;
 
 public:
     /**
@@ -63,31 +66,44 @@ public:
     void Write(Variable<T> &variable, const T *values);
 
     /**
-     * String version
-     * @param variableName
+     * Single value version
+     * @param variable
      * @param values
      */
     template <class T>
-    void Write(const std::string &variableName, const T *values);
+    void Write(Variable<T> &variable, const T value);
 
     /**
-     * Single value version
-     * @param variable
+     * String version
+     * @param variableName
      * @param values
      */
     template <class T>
-    void Write(Variable<T> &variable, const T values);
+    void Write(const std::string &variableName, const T *values);
 
     /**
-     * Single value version using string as variable handlers, allows rvalues to
+     * Single value version using string as variable handlers, allows
+     * rvalues to
      * be passed
      * @param variableName
      * @param values
      */
     template <class T>
-    void Write(const std::string &variableName, const T values);
+    void Write(const std::string &variableName, const T value);
 
-    /// Read API
+    /**
+     * Runtime version for either Variable<T> or VariableCompound
+     * @param variable
+     * @param values
+     */
+    void Write(VariableBase &variable, const void *values);
+
+    /**
+     * Runtime version
+     * @param variableName
+     * @param values
+     */
+    void Write(const std::string &variableName, const void *values);
 
     /**
      *
@@ -234,9 +250,8 @@ public:
      * readers
      * @param callback Will be called when advance is completed.
      */
-    virtual void
-    AdvanceAsync(const AdvanceMode mode,
-                 std::function<void(std::shared_ptr<adios2::Engine>)> callback);
+    virtual void AdvanceAsync(const AdvanceMode mode,
+                              AdvanceAsyncCallback callback);
 
     AdvanceStatus GetAdvanceStatus();
 
@@ -343,40 +358,11 @@ protected:
     // READ
     virtual VariableBase *InquireVariableUnknown(const std::string &name,
                                                  const bool readIn);
-    virtual Variable<char> *InquireVariableChar(const std::string &name,
-                                                const bool readIn);
-    virtual Variable<unsigned char> *
-    InquireVariableUChar(const std::string &name, const bool readIn);
-    virtual Variable<short> *InquireVariableShort(const std::string &name,
-                                                  const bool readIn);
-    virtual Variable<unsigned short> *
-    InquireVariableUShort(const std::string &name, const bool readIn);
-    virtual Variable<int> *InquireVariableInt(const std::string &name,
-                                              const bool readIn);
-    virtual Variable<unsigned int> *InquireVariableUInt(const std::string &name,
-                                                        const bool readIn);
-    virtual Variable<long int> *InquireVariableLInt(const std::string &name,
-                                                    const bool readIn);
-    virtual Variable<unsigned long int> *
-    InquireVariableULInt(const std::string &name, const bool readIn);
-    virtual Variable<long long int> *
-    InquireVariableLLInt(const std::string &name, const bool readIn);
-    virtual Variable<unsigned long long int> *
-    InquireVariableULLInt(const std::string &name, const bool readIn);
-
-    virtual Variable<float> *InquireVariableFloat(const std::string &name,
-                                                  const bool readIn);
-    virtual Variable<double> *InquireVariableDouble(const std::string &name,
-                                                    const bool readIn);
-    virtual Variable<long double> *
-    InquireVariableLDouble(const std::string &name, const bool readIn);
-
-    virtual Variable<cfloat> *InquireVariableCFloat(const std::string &name,
-                                                    const bool readIn);
-    virtual Variable<cdouble> *InquireVariableCDouble(const std::string &name,
-                                                      const bool readIn);
-    virtual Variable<cldouble> *InquireVariableCLDouble(const std::string &name,
-                                                        const bool readIn);
+#define declare(T, L)                                                          \
+    virtual Variable<T> *InquireVariable##L(const std::string &name,           \
+                                            const bool readIn);
+    ADIOS2_FOREACH_TYPE_2ARGS(declare)
+#undef declare
 
 // Known-type
 #define declare_type(T)                                                        \
@@ -394,7 +380,17 @@ private:
     void ThrowUp(const std::string function) const;
 };
 
-} // end namespace adios
+#define declare_template_instantiation(T)                                      \
+    extern template void Engine::Write<T>(Variable<T> &, const T *);           \
+    extern template void Engine::Write<T>(Variable<T> &, const T);             \
+                                                                               \
+    extern template void Engine::Write<T>(const std::string &, const T *);     \
+    extern template void Engine::Write<T>(const std::string &, const T);
+
+ADIOS2_FOREACH_TYPE_1ARG(declare_template_instantiation)
+#undef declare_template_instantiation
+
+} // end namespace adios2
 
 #include "Engine.inl"
 
diff --git a/source/adios2/core/Engine.inl b/source/adios2/core/Engine.inl
index dccf770fd98215718fb83fdf6c0ba771827d1b8f..486df9c53c150d2a4d2813f5db81d85d9fcf5257 100644
--- a/source/adios2/core/Engine.inl
+++ b/source/adios2/core/Engine.inl
@@ -24,37 +24,6 @@ T *Engine::AllocateVariable(Variable<T> &variable, T fillValue)
                                 variable.m_Name + " in call to \n");
 }
 
-template <class T>
-void Engine::Write(Variable<T> &variable, const T *values)
-{
-    if (m_DebugMode)
-    {
-        variable.CheckDimsBeforeWrite("Write(" + variable.m_Name + ")");
-    }
-
-    DoWrite(variable, values);
-}
-
-template <class T>
-void Engine::Write(const std::string &variableName, const T *values)
-{
-    Write(m_IO.GetVariable<T>(variableName), values);
-}
-
-template <class T>
-void Engine::Write(Variable<T> &variable, const T values)
-{
-    const T val = values; // need an address for memory copy
-    Write(variable, &values);
-}
-
-template <class T>
-void Engine::Write(const std::string &variableName, const T values)
-{
-    const T val = values; // need an address for memory copy
-    Write(m_IO.GetVariable<T>(variableName), &values);
-}
-
 template <class T>
 void Engine::Read(Variable<T> &variable, T *values)
 {
@@ -122,6 +91,6 @@ void Engine::ScheduleRead(const std::string &variableName, T &values)
     DoScheduleRead(variableName, &values);
 }
 
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_CORE_ENGINE_INL_ */
diff --git a/source/adios2/core/Engine.tcc b/source/adios2/core/Engine.tcc
index 2aa935e722edce42f70cc55e8ca5383220be61fa..74666c7324f17e0fef9ee6ec6174763991883ece 100644
--- a/source/adios2/core/Engine.tcc
+++ b/source/adios2/core/Engine.tcc
@@ -16,6 +16,37 @@
 namespace adios2
 {
 
+template <class T>
+void Engine::Write(Variable<T> &variable, const T *values)
+{
+    if (m_DebugMode)
+    {
+        variable.CheckDimsBeforeWrite("Write " + variable.m_Name);
+    }
+
+    DoWrite(variable, values);
+}
+
+template <class T>
+void Engine::Write(Variable<T> &variable, const T values)
+{
+    const T val = values; // need an address for memory copy
+    Write(variable, &values);
+}
+
+template <class T>
+void Engine::Write(const std::string &variableName, const T *values)
+{
+    Write(m_IO.GetVariable<T>(variableName), values);
+}
+
+template <class T>
+void Engine::Write(const std::string &variableName, const T values)
+{
+    const T val = values; // need an address for memory copy
+    Write(m_IO.GetVariable<T>(variableName), &values);
+}
+
 template <>
 Variable<char> *Engine::InquireVariable<char>(const std::string &variableName,
                                               const bool readIn)
diff --git a/source/adios2/core/IO.cpp b/source/adios2/core/IO.cpp
index fe70d534ec6094c44a30440ddd2072daf12bcd2b..e635a100863235e804bc300dae48218382ededc5 100644
--- a/source/adios2/core/IO.cpp
+++ b/source/adios2/core/IO.cpp
@@ -13,6 +13,7 @@
 
 #include "adios2/ADIOSMPI.h"
 #include "adios2/engine/bp/BPFileWriter.h"
+#include "adios2/engine/plugin/PluginEngine.h"
 #include "adios2/helper/adiosFunctions.h" //BuildParametersMap
 
 #ifdef ADIOS2_HAVE_DATAMAN // external dependencies
@@ -45,6 +46,12 @@ void IO::SetIOMode(const IOMode ioMode) { m_IOMode = ioMode; };
 
 void IO::SetParameters(const Params &parameters) { m_Parameters = parameters; }
 
+void IO::SetSingleParameter(const std::string key,
+                            const std::string value) noexcept
+{
+    m_Parameters[key] = value;
+}
+
 const Params &IO::GetParameters() const { return m_Parameters; }
 
 unsigned int IO::AddTransport(const std::string type, const Params &parameters)
@@ -60,9 +67,77 @@ unsigned int IO::AddTransport(const std::string type, const Params &parameters)
     return static_cast<unsigned int>(m_TransportsParameters.size() - 1);
 }
 
+void IO::SetTransportSingleParameter(const unsigned int transportIndex,
+                                     const std::string key,
+                                     const std::string value)
+{
+    if (m_DebugMode)
+    {
+        if (transportIndex >=
+            static_cast<unsigned int>(m_TransportsParameters.size()))
+        {
+            throw std::invalid_argument("ERROR: transportIndex is larger than "
+                                        "transports created with AddTransport "
+                                        "function calls\n");
+        }
+    }
+
+    m_TransportsParameters[transportIndex][key] = value;
+}
+
+VariableCompound &
+IO::DefineVariableCompound(const std::string &name, const size_t sizeOfVariable,
+                           const Dims &shape, const Dims &start,
+                           const Dims &count, const bool constantDims)
+{
+    if (m_DebugMode)
+    {
+        auto itVariable = m_Variables.find(name);
+        if (!IsEnd(itVariable, m_Variables))
+        {
+            throw std::invalid_argument("ERROR: variable " + name +
+                                        " exists in IO object " + m_Name +
+                                        ", in call to DefineVariable\n");
+        }
+    }
+    const unsigned int size = m_Compound.size();
+    auto itVariableCompound = m_Compound.emplace(
+        size, VariableCompound(name, sizeOfVariable, shape, start, count,
+                               constantDims, m_DebugMode));
+    m_Variables.emplace(name, std::make_pair("compound", size));
+    return itVariableCompound.first->second;
+}
+
 VariableCompound &IO::GetVariableCompound(const std::string &name)
 {
-    return m_Compound.at(GetVariableIndex(name));
+    return m_Compound.at(GetMapIndex(name, m_Variables, "VariableCompound"));
+}
+
+const DataMap &IO::GetAttributesDataMap() const noexcept
+{
+    return m_Attributes;
+}
+
+VariableBase *IO::GetVariableBase(const std::string &name) noexcept
+{
+    VariableBase *variableBase = nullptr;
+    auto itVariable = m_Variables.find(name);
+    if (itVariable == m_Variables.end())
+    {
+        return variableBase;
+    }
+
+    const std::string type(itVariable->second.first);
+    if (type == "compound")
+    {
+        variableBase = &GetVariableCompound(name);
+    }
+#define declare_type(T)                                                        \
+    else if (type == GetType<T>()) { variableBase = &GetVariable<T>(name); }
+    ADIOS2_FOREACH_TYPE_1ARG(declare_type)
+#undef declare_type
+
+    return variableBase;
 }
 
 std::string IO::GetVariableType(const std::string &name) const
@@ -214,6 +289,10 @@ std::shared_ptr<Engine> IO::Open(const std::string &name,
                                     "HDF5 library, can't use HDF5\n");
 #endif
     }
+    else if (m_EngineType == "PluginEngine")
+    {
+        engine = std::make_shared<PluginEngine>(*this, name, openMode, mpiComm);
+    }
     else
     {
         if (m_DebugMode)
@@ -235,30 +314,42 @@ std::shared_ptr<Engine> IO::Open(const std::string &name,
 }
 
 // PRIVATE Functions
-unsigned int IO::GetVariableIndex(const std::string &name) const
+unsigned int IO::GetMapIndex(const std::string &name, const DataMap &dataMap,
+                             const std::string hint) const
 {
+    auto itDataMap = dataMap.find(name);
+
     if (m_DebugMode)
     {
-        if (!VariableExists(name))
+        if (IsEnd(itDataMap, dataMap))
         {
-            throw std::invalid_argument(
-                "ERROR: variable " + m_Name +
-                " wasn't created with DefineVariable, in call to IO object " +
-                m_Name + " GetVariable\n");
+            throw std::invalid_argument("ERROR: " + hint + " " + m_Name +
+                                        " wasn't created with Define " + hint +
+                                        ", in call to IO object " + m_Name +
+                                        " Get" + hint + "\n");
         }
     }
-    auto itVariable = m_Variables.find(name);
-    return itVariable->second.second;
+    return itDataMap->second.second;
 }
 
-bool IO::VariableExists(const std::string &name) const
+void IO::CheckAttributeCommon(const std::string &name) const
 {
-    bool exists = false;
-    if (m_Variables.count(name) == 1)
+    auto itAttribute = m_Attributes.find(name);
+    if (!IsEnd(itAttribute, m_Attributes))
     {
-        exists = true;
+        throw std::invalid_argument("ERROR: attribute " + name +
+                                    " exists in IO object " + m_Name +
+                                    ", in call to DefineAttribute\n");
     }
-    return exists;
+}
+
+bool IO::IsEnd(DataMap::const_iterator itDataMap, const DataMap &dataMap) const
+{
+    if (itDataMap == dataMap.end())
+    {
+        return true;
+    }
+    return false;
 }
 
 void IO::CheckTransportType(const std::string type) const
@@ -275,11 +366,22 @@ void IO::CheckTransportType(const std::string type) const
 
 // Explicitly instantiate the necessary public template implementations
 #define define_template_instantiation(T)                                       \
-    template Variable<T> &IO::DefineVariable<T>(                               \
-        const std::string &, const Dims, const Dims, const Dims, const bool);  \
+    template Variable<T> &IO::DefineVariable<T>(const std::string &,           \
+                                                const Dims &, const Dims &,    \
+                                                const Dims &, const bool);     \
     template Variable<T> &IO::GetVariable<T>(const std::string &);
 
 ADIOS2_FOREACH_TYPE_1ARG(define_template_instantiation)
 #undef define_template_instatiation
 
+#define declare_template_instantiation(T)                                      \
+    template Attribute<T> &IO::DefineAttribute<T>(const std::string &,         \
+                                                  const T *, const size_t);    \
+    template Attribute<T> &IO::DefineAttribute<T>(const std::string &,         \
+                                                  const T &);                  \
+    template Attribute<T> &IO::GetAttribute(const std::string &);
+
+ADIOS2_FOREACH_ATTRIBUTE_TYPE_1ARG(declare_template_instantiation)
+#undef declare_template_instantiation
+
 } // end namespace adios
diff --git a/source/adios2/core/IO.h b/source/adios2/core/IO.h
index 59fc9f6c7fb2589feaf1f470d230b7b6b56d0e3d..f29191dac387e7d9b97b44d9ee8b8b5d3cca1ce1 100644
--- a/source/adios2/core/IO.h
+++ b/source/adios2/core/IO.h
@@ -24,12 +24,16 @@
 #include "adios2/ADIOSMPICommOnly.h"
 #include "adios2/ADIOSMacros.h"
 #include "adios2/ADIOSTypes.h"
+#include "adios2/core/Attribute.h"
 #include "adios2/core/Variable.h"
 #include "adios2/core/VariableCompound.h"
 
 namespace adios2
 {
 
+/** used for Variables and Attributes */
+using DataMap = std::map<std::string, std::pair<std::string, unsigned int>>;
+
 // forward declaration needed as IO is passed to Engine derived
 // classes
 class Engine;
@@ -87,6 +91,14 @@ public:
      */
     void SetParameters(const Params &parameters = Params());
 
+    /**
+     * Sets a single parameter overwriting value if key exists;
+     * @param key parameter key
+     * @param value parameter value
+     */
+    void SetSingleParameter(const std::string key,
+                            const std::string value) noexcept;
+
     /**
      * Retrieve existing parameter set
      */
@@ -96,11 +108,23 @@ public:
      * Adds a transport and its parameters for the IO Engine
      * @param type must be a supported transport type
      * @param params acceptable parameters for a particular transport
-     * @return
+     * @return transportIndex handler
      */
     unsigned int AddTransport(const std::string type,
                               const Params &params = Params());
 
+    /**
+     * Set a single parameter to an existing transport identified with a
+     * transportIndex handler from AddTransport. This function overwrites
+     * existing parameter.
+     * @param transportIndex index handler from AddTransport
+     * @param key parameter key
+     * @param value parameter value
+     */
+    void SetTransportSingleParameter(const unsigned int transportIndex,
+                                     const std::string key,
+                                     const std::string value);
+
     /**
      * Define a Variable of primitive data type for I/O.
      * Default (name only) is a local single value,
@@ -114,9 +138,10 @@ public:
      * @return reference to Variable object
      */
     template <class T>
-    Variable<T> &DefineVariable(const std::string &name, const Dims shape = {},
-                                const Dims start = {}, const Dims count = {},
-                                const bool constantShape = false);
+    Variable<T> &
+    DefineVariable(const std::string &name, const Dims &shape = Dims{},
+                   const Dims &start = Dims{}, const Dims &count = Dims{},
+                   const bool constantDims = false);
 
     /**
      * Define a Variable of primitive data type for I/O.
@@ -130,12 +155,39 @@ public:
      * change over time
      * @return reference to Variable object
      */
+    template <class T>
+    VariableCompound &DefineVariableCompound(const std::string &name,
+                                             const Dims &shape = Dims{},
+                                             const Dims &start = Dims{},
+                                             const Dims &count = Dims{},
+                                             const bool constantDims = false);
+
+    VariableCompound &DefineVariableCompound(const std::string &name,
+                                             const size_t sizeOfVariable,
+                                             const Dims &shape = Dims{},
+                                             const Dims &start = Dims{},
+                                             const Dims &count = Dims{},
+                                             const bool constantDims = false);
 
+    /**
+     * Define attribute from contiguous data array owned by an application
+     * @param name must be unique for the IO object
+     * @param array pointer to user data
+     * @param elements number of data elements
+     * @return reference to internal Attribute
+     */
     template <class T>
-    VariableCompound &
-    DefineVariableCompound(const std::string &name, const Dims shape = Dims{},
-                           const Dims start = Dims{}, const Dims count = Dims{},
-                           const bool constantShape = false);
+    Attribute<T> &DefineAttribute(const std::string &name, const T *array,
+                                  const size_t elements);
+
+    /**
+     * Define attribute from a single variable making a copy
+     * @param name must be unique for the IO object
+     * @param value single data value
+     * @return reference to internal Attribute
+     */
+    template <class T>
+    Attribute<T> &DefineAttribute(const std::string &name, const T &value);
 
     /**
      * Removes an existing Variable previously created with DefineVariable or
@@ -155,6 +207,14 @@ public:
     template <class T>
     Variable<T> &GetVariable(const std::string &name);
 
+    /**
+     * Runtime function: return a pointer to VariableBase
+     * @param name unique variable identifier
+     * @return nullptr if not found, pointer to VariableBase if variable is
+     * found
+     */
+    VariableBase *GetVariableBase(const std::string &name) noexcept;
+
     /**
      * Gets an existing variable of compound type by name
      * @param name of variable to be retrieved
@@ -163,6 +223,21 @@ public:
      */
     VariableCompound &GetVariableCompound(const std::string &name);
 
+    /**
+     * Return  map with attributes name and type info
+     * @return m_Attributes
+     */
+    const DataMap &GetAttributesDataMap() const noexcept;
+
+    /**
+     * Gets an existing attribute of primitive type by name
+     * @param name of attribute to be retrieved
+     * @return reference to an existing attribute created with DefineAttribute
+     * throws an exception if Attribute is not found
+     */
+    template <class T>
+    Attribute<T> &GetAttribute(const std::string &name);
+
     /**
      * Get the type if variable (by name id) exists
      * @param name input id
@@ -222,10 +297,11 @@ private:
      *        pair.second = index in fixed size map (e.g. m_Int8, m_Double)
      * </pre>
      */
-    std::map<std::string, std::pair<std::string, unsigned int>> m_Variables;
+    DataMap m_Variables;
 
     /** Variable containers based on fixed-size type */
     std::map<unsigned int, Variable<char>> m_Char;
+    std::map<unsigned int, Variable<signed char>> m_SChar;
     std::map<unsigned int, Variable<unsigned char>> m_UChar;
     std::map<unsigned int, Variable<short>> m_Short;
     std::map<unsigned int, Variable<unsigned short>> m_UShort;
@@ -243,25 +319,64 @@ private:
     std::map<unsigned int, Variable<cldouble>> m_CLDouble;
     std::map<unsigned int, VariableCompound> m_Compound;
 
-    std::map<std::string, std::string> m_AttributesString;
-    std::map<std::string, double> m_AttributesNumeric;
-
-    std::set<std::string> m_EngineNames;
-
     /** Gets the internal reference to a variable map for type T
      *  This function is specialized in IO.tcc */
     template <class T>
     std::map<unsigned int, Variable<T>> &GetVariableMap();
 
-    /** Gets the internal index in variable map for an existing variable */
-    unsigned int GetVariableIndex(const std::string &name) const;
+    /**
+     * Map holding attribute identifiers
+     * <pre>
+     * key: unique attribute name,
+     * value: pair.first = type as string GetType<T> from
+     *                     helper/adiosTemplates.h
+     *        pair.second = index in fixed size map (e.g. m_Int8, m_Double)
+     * </pre>
+     */
+    DataMap m_Attributes;
+
+    std::map<unsigned int, Attribute<std::string>> m_StringA;
+    std::map<unsigned int, Attribute<char>> m_CharA;
+    std::map<unsigned int, Attribute<signed char>> m_SCharA;
+    std::map<unsigned int, Attribute<unsigned char>> m_UCharA;
+    std::map<unsigned int, Attribute<short>> m_ShortA;
+    std::map<unsigned int, Attribute<unsigned short>> m_UShortA;
+    std::map<unsigned int, Attribute<int>> m_IntA;
+    std::map<unsigned int, Attribute<unsigned int>> m_UIntA;
+    std::map<unsigned int, Attribute<long int>> m_LIntA;
+    std::map<unsigned int, Attribute<unsigned long int>> m_ULIntA;
+    std::map<unsigned int, Attribute<long long int>> m_LLIntA;
+    std::map<unsigned int, Attribute<unsigned long long int>> m_ULLIntA;
+    std::map<unsigned int, Attribute<float>> m_FloatA;
+    std::map<unsigned int, Attribute<double>> m_DoubleA;
+    std::map<unsigned int, Attribute<long double>> m_LDoubleA;
+
+    template <class T>
+    std::map<unsigned int, Attribute<T>> &GetAttributeMap();
+
+    /**
+     * Gets map index for Variables or Attributes
+     * @param name
+     * @param dataMap m_Variables or m_Attributes
+     * @param hint "Variable", "Attribute", or "VariableCompound"
+     * @return index in type map
+     */
+    unsigned int GetMapIndex(const std::string &name, const DataMap &dataMap,
+                             const std::string hint) const;
+
+    /** Checks if attribute exists, called from DefineAttribute different
+     * signatures */
+    void CheckAttributeCommon(const std::string &name) const;
+
+    std::set<std::string> m_EngineNames;
 
     /**
-     * Checks if variable exists by checking its name
-     * @param name unique variable name to be checked against existing variables
-     * @return true: variable name exists, false: variable name doesn't exist
+     * Checks if iterator points to end. Used for Variables and Attributes.
+     * @param itDataMap iterator to be tested
+     * @param dataMap map
+     * @return true: itDataMap == dataMap.end(), false otherwise
      */
-    bool VariableExists(const std::string &name) const;
+    bool IsEnd(DataMap::const_iterator itDataMap, const DataMap &dataMap) const;
 
     void CheckTransportType(const std::string type) const;
 };
@@ -269,14 +384,24 @@ private:
 // Explicit declaration of the public template methods
 #define declare_template_instantiation(T)                                      \
     extern template Variable<T> &IO::DefineVariable<T>(                        \
-        const std::string &name, const Dims, const Dims, const Dims,           \
-        const bool constantShape);                                             \
+        const std::string &, const Dims &, const Dims &, const Dims &,         \
+        const bool);                                                           \
     extern template Variable<T> &IO::GetVariable<T>(const std::string &name);
 
 ADIOS2_FOREACH_TYPE_1ARG(declare_template_instantiation)
 #undef declare_template_instantiation
 
-} // end namespace adios
+#define declare_template_instantiation(T)                                      \
+    extern template Attribute<T> &IO::DefineAttribute<T>(                      \
+        const std::string &, const T *, const size_t);                         \
+    extern template Attribute<T> &IO::DefineAttribute<T>(const std::string &,  \
+                                                         const T &);           \
+    extern template Attribute<T> &IO::GetAttribute(const std::string &);
+
+ADIOS2_FOREACH_ATTRIBUTE_TYPE_1ARG(declare_template_instantiation)
+#undef declare_template_instantiation
+
+} // end namespace adios2
 
 #include "adios2/core/IO.inl"
 
diff --git a/source/adios2/core/IO.inl b/source/adios2/core/IO.inl
index 6ef9299c028b5033a4030448308ee695cca69db4..4a89c759c71952c545eb8f49184d37bc49880202 100644
--- a/source/adios2/core/IO.inl
+++ b/source/adios2/core/IO.inl
@@ -2,7 +2,8 @@
  * Distributed under the OSI-approved Apache License, Version 2.0.  See
  * accompanying file Copyright.txt for details.
  *
- * IO.inl inline template functions implementation of IO class
+ * IO.inl inline template functions implementation of IO class. VariableCompound
+ * can take any type so must be inlined as type is not known a priori.
  *
  *  Created on: May 15, 2017
  *      Author: William F Godoy godoywf@ornl.gov
@@ -21,27 +22,14 @@ namespace adios2
 
 template <class T>
 VariableCompound &IO::DefineVariableCompound(const std::string &name,
-                                             const Dims shape, const Dims start,
-                                             const Dims count,
-                                             const bool constantShape)
+                                             const Dims &shape, const Dims &start,
+                                             const Dims &count,
+                                             const bool constantDims)
 {
-    if (m_DebugMode)
-    {
-        if (VariableExists(name))
-        {
-            throw std::invalid_argument("ERROR: variable " + name +
-                                        " exists in IO object " + m_Name +
-                                        ", in call to DefineVariable\n");
-        }
-    }
-    const unsigned int size = m_Compound.size();
-    auto itVariableCompound = m_Compound.emplace(
-        size, VariableCompound(name, sizeof(T), shape, start, count,
-                               constantShape, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair("compound", size));
-    return itVariableCompound.first->second;
+    return DefineVariableCompound(name, sizeof(T), shape, start, count,
+                                  constantDims);
 }
 
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_CORE_IO_INL_ */
diff --git a/source/adios2/core/IO.tcc b/source/adios2/core/IO.tcc
index dddb3167848b6849ab92c1d0cfd8a16b7af1ddeb..d4c19ab8cd92b0063fc619ef7556517fd2b0aa36 100644
--- a/source/adios2/core/IO.tcc
+++ b/source/adios2/core/IO.tcc
@@ -26,13 +26,14 @@ namespace adios2
 {
 
 template <class T>
-Variable<T> &IO::DefineVariable(const std::string &name, const Dims shape,
-                                const Dims start, const Dims count,
-                                const bool constantShape)
+Variable<T> &IO::DefineVariable(const std::string &name, const Dims &shape,
+                                const Dims &start, const Dims &count,
+                                const bool constantDims)
 {
     if (m_DebugMode)
     {
-        if (VariableExists(name))
+        auto itVariable = m_Variables.find(name);
+        if (!IsEnd(itVariable, m_Variables))
         {
             throw std::invalid_argument("ERROR: variable " + name +
                                         " exists in IO object " + m_Name +
@@ -41,10 +42,11 @@ Variable<T> &IO::DefineVariable(const std::string &name, const Dims shape,
     }
 
     auto &variableMap = GetVariableMap<T>();
-    const unsigned int size = variableMap.size();
+    const unsigned int size =
+        static_cast<const unsigned int>(variableMap.size());
     auto itVariablePair =
         variableMap.emplace(size, Variable<T>(name, shape, start, count,
-                                              constantShape, m_DebugMode));
+                                              constantDims, m_DebugMode));
 
     m_Variables.emplace(name, std::make_pair(GetType<T>(), size));
     return itVariablePair.first->second;
@@ -53,7 +55,53 @@ Variable<T> &IO::DefineVariable(const std::string &name, const Dims shape,
 template <class T>
 Variable<T> &IO::GetVariable(const std::string &name)
 {
-    return GetVariableMap<T>().at(GetVariableIndex(name));
+    return GetVariableMap<T>().at(GetMapIndex(name, m_Variables, "Variable"));
+}
+
+template <class T>
+Attribute<T> &IO::DefineAttribute(const std::string &name, const T &value)
+{
+    if (m_DebugMode)
+    {
+        CheckAttributeCommon(name);
+    }
+
+    auto &attributeMap = GetAttributeMap<T>();
+    const unsigned int size =
+        static_cast<const unsigned int>(attributeMap.size());
+
+    auto itAttributePair =
+        attributeMap.emplace(size, Attribute<T>(name, value));
+    m_Attributes.emplace(name, std::make_pair(GetType<T>(), size));
+
+    return itAttributePair.first->second;
+}
+
+template <class T>
+Attribute<T> &IO::DefineAttribute(const std::string &name, const T *array,
+                                  const size_t elements)
+{
+    if (m_DebugMode)
+    {
+        CheckAttributeCommon(name);
+    }
+
+    auto &attributeMap = GetAttributeMap<T>();
+    const unsigned int size =
+        static_cast<const unsigned int>(attributeMap.size());
+
+    auto itAttributePair =
+        attributeMap.emplace(size, Attribute<T>(name, array, elements));
+    m_Attributes.emplace(name, std::make_pair(GetType<T>(), size));
+
+    return itAttributePair.first->second;
+}
+
+template <class T>
+Attribute<T> &IO::GetAttribute(const std::string &name)
+{
+    return GetAttributeMap<T>().at(
+        GetMapIndex(name, m_Attributes, "Attribute"));
 }
 
 // PRIVATE
@@ -63,6 +111,12 @@ std::map<unsigned int, Variable<char>> &IO::GetVariableMap()
     return m_Char;
 }
 
+template <>
+std::map<unsigned int, Variable<signed char>> &IO::GetVariableMap()
+{
+    return m_SChar;
+}
+
 template <>
 std::map<unsigned int, Variable<unsigned char>> &IO::GetVariableMap()
 {
@@ -153,6 +207,97 @@ std::map<unsigned int, Variable<cldouble>> &IO::GetVariableMap()
     return m_CLDouble;
 }
 
-} // end namespace adios
+// attributes
+template <>
+std::map<unsigned int, Attribute<std::string>> &IO::GetAttributeMap()
+{
+    return m_StringA;
+}
+
+template <>
+std::map<unsigned int, Attribute<char>> &IO::GetAttributeMap()
+{
+    return m_CharA;
+}
+
+template <>
+std::map<unsigned int, Attribute<signed char>> &IO::GetAttributeMap()
+{
+    return m_SCharA;
+}
+
+template <>
+std::map<unsigned int, Attribute<unsigned char>> &IO::GetAttributeMap()
+{
+    return m_UCharA;
+}
+
+template <>
+std::map<unsigned int, Attribute<short>> &IO::GetAttributeMap()
+{
+    return m_ShortA;
+}
+
+template <>
+std::map<unsigned int, Attribute<unsigned short>> &IO::GetAttributeMap()
+{
+    return m_UShortA;
+}
+
+template <>
+std::map<unsigned int, Attribute<int>> &IO::GetAttributeMap()
+{
+    return m_IntA;
+}
+
+template <>
+std::map<unsigned int, Attribute<unsigned int>> &IO::GetAttributeMap()
+{
+    return m_UIntA;
+}
+
+template <>
+std::map<unsigned int, Attribute<long int>> &IO::GetAttributeMap()
+{
+    return m_LIntA;
+}
+
+template <>
+std::map<unsigned int, Attribute<unsigned long int>> &IO::GetAttributeMap()
+{
+    return m_ULIntA;
+}
+
+template <>
+std::map<unsigned int, Attribute<long long int>> &IO::GetAttributeMap()
+{
+    return m_LLIntA;
+}
+
+template <>
+std::map<unsigned int, Attribute<unsigned long long int>> &IO::GetAttributeMap()
+{
+    return m_ULLIntA;
+}
+
+template <>
+std::map<unsigned int, Attribute<float>> &IO::GetAttributeMap()
+{
+    return m_FloatA;
+}
+
+template <>
+std::map<unsigned int, Attribute<double>> &IO::GetAttributeMap()
+{
+    return m_DoubleA;
+}
+
+template <>
+std::map<unsigned int, Attribute<long double>> &IO::GetAttributeMap()
+{
+    return m_LDoubleA;
+}
+
+} // end namespace adios2
 
 #endif /* ADIOS2_CORE_IO_TCC_ */
diff --git a/source/adios2/core/Variable.cpp b/source/adios2/core/Variable.cpp
index 55777209f8e5382653e6d185106d1eff596c8d94..86b5e3d165d79ef6da78cd90db00ca49e64ea6fd 100644
--- a/source/adios2/core/Variable.cpp
+++ b/source/adios2/core/Variable.cpp
@@ -2,7 +2,7 @@
  * Distributed under the OSI-approved Apache License, Version 2.0.  See
  * accompanying file Copyright.txt for details.
  *
- * Variable.cpp  needed for template separation using Variable.tcc
+ * Variable.cpp : needed for template separation using Variable.tcc
  *
  *  Created on: Jun 8, 2017
  *      Author: William F Godoy godoywf@ornl.gov
@@ -14,4 +14,4 @@
 namespace adios2
 {
 
-} // end namespace adios
+} // end namespace adios2
diff --git a/source/adios2/core/Variable.h b/source/adios2/core/Variable.h
index 9ed428735bb8f924d61d7314cd94fbd49084a781..dbf5ec2dc00f5754d098f8ef79c8cee44f971b3f 100644
--- a/source/adios2/core/Variable.h
+++ b/source/adios2/core/Variable.h
@@ -18,8 +18,6 @@
 #include <vector>
 /// \endcond
 
-#include "adios2/ADIOSMacros.h"
-#include "adios2/core/Transform.h"
 #include "adios2/core/VariableBase.h"
 
 namespace adios2
@@ -50,15 +48,15 @@ public:
      * @param constantShape
      * @param debugMode
      */
-    Variable<T>(const std::string &name, const Dims shape, const Dims start,
-                const Dims count, const bool constantShape,
+    Variable<T>(const std::string &name, const Dims &shape, const Dims &start,
+                const Dims &count, const bool constantShape,
                 const bool debugMode);
 
-    virtual ~Variable<T>() = default;
+    ~Variable<T>() = default;
 
     void ApplyTransforms() final;
 };
 
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_CORE_VARIABLE_H_ */
diff --git a/source/adios2/core/Variable.tcc b/source/adios2/core/Variable.tcc
index e5a241109009f95fd99e07aa5750f5bfca1a93a7..0de077a2e58fc3dd66314eca48143213f82c335d 100644
--- a/source/adios2/core/Variable.tcc
+++ b/source/adios2/core/Variable.tcc
@@ -14,7 +14,7 @@
 #include "Variable.h"
 
 #include "adios2/ADIOSMacros.h"
-#include "adios2/helper/adiosFunctions.h" //GetType
+#include "adios2/helper/adiosFunctions.h" //GetType<T>
 
 namespace adios2
 {
@@ -22,11 +22,11 @@ namespace adios2
 #define declare_type(T)                                                        \
                                                                                \
     template <>                                                                \
-    Variable<T>::Variable(const std::string &name, const Dims shape,           \
-                          const Dims start, const Dims count,                  \
-                          const bool constantShape, const bool debugMode)      \
+    Variable<T>::Variable(const std::string &name, const Dims &shape,          \
+                          const Dims &start, const Dims &count,                \
+                          const bool constantDims, const bool debugMode)       \
     : VariableBase(name, GetType<T>(), sizeof(T), shape, start, count,         \
-                   constantShape, debugMode)                                   \
+                   constantDims, debugMode)                                    \
     {                                                                          \
     }                                                                          \
                                                                                \
@@ -38,6 +38,6 @@ namespace adios2
 ADIOS2_FOREACH_TYPE_1ARG(declare_type)
 #undef declare_type
 
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_CORE_VARIABLE_TCC_ */
diff --git a/source/adios2/core/VariableBase.cpp b/source/adios2/core/VariableBase.cpp
index d98f572d15db6b399d008e6d5c4364cbbd29f91c..775944e0a775fb2f0ff635c36b50ec790cdf54f0 100644
--- a/source/adios2/core/VariableBase.cpp
+++ b/source/adios2/core/VariableBase.cpp
@@ -21,8 +21,8 @@ namespace adios2
 {
 
 VariableBase::VariableBase(const std::string &name, const std::string type,
-                           const size_t elementSize, const Dims shape,
-                           const Dims start, const Dims count,
+                           const size_t elementSize, const Dims &shape,
+                           const Dims &start, const Dims &count,
                            const bool constantDims, const bool debugMode)
 : m_Name(name), m_Type(type), m_ElementSize(elementSize), m_Shape(shape),
   m_Start(start), m_Count(count), m_ConstantDims(constantDims),
@@ -41,7 +41,7 @@ size_t VariableBase::TotalSize() const noexcept
     return GetTotalSize(m_Count);
 }
 
-void VariableBase::SetSelection(const Dims start, const Dims count)
+void VariableBase::SetSelection(const Dims &start, const Dims &count)
 {
     if (m_DebugMode)
     {
diff --git a/source/adios2/core/VariableBase.h b/source/adios2/core/VariableBase.h
index 61fc093dfd72f5860565edc4eb5da69d5ec72611..f169a3299d8251eda7f6d599e6ec2b042673c15d 100644
--- a/source/adios2/core/VariableBase.h
+++ b/source/adios2/core/VariableBase.h
@@ -63,8 +63,8 @@ public:
     unsigned int m_AvailableSteps = 1;
 
     VariableBase(const std::string &name, const std::string type,
-                 const size_t elementSize, const Dims shape, const Dims start,
-                 const Dims count, const bool constantShape,
+                 const size_t elementSize, const Dims &shape, const Dims &start,
+                 const Dims &count, const bool constantShape,
                  const bool debugMode);
 
     virtual ~VariableBase() = default;
@@ -82,7 +82,7 @@ public:
     size_t TotalSize() const noexcept;
 
     /** Set the local dimension and global offset of the variable */
-    void SetSelection(const Dims start, const Dims count);
+    void SetSelection(const Dims &start, const Dims &count);
 
     /** Overloaded version of SetSelection using a SelectionBoundingBox */
     void SetSelection(const SelectionBoundingBox &selection);
diff --git a/source/adios2/core/VariableCompound.cpp b/source/adios2/core/VariableCompound.cpp
index ab946581dfcc080b8ca509e1ae3ee37981e906b0..8fee9b54b27b93878ec858c3fab41717249bb6bc 100644
--- a/source/adios2/core/VariableCompound.cpp
+++ b/source/adios2/core/VariableCompound.cpp
@@ -19,10 +19,10 @@ namespace adios2
 VariableCompound::VariableCompound(const std::string name,
                                    const size_t sizeOfStruct, const Dims shape,
                                    const Dims start, const Dims count,
-                                   const bool constantShape,
+                                   const bool constantDims,
                                    const bool debugMode)
 : VariableBase(name, "compound", sizeOfStruct, shape, start, count,
-               constantShape, debugMode)
+               constantDims, debugMode)
 {
 }
 
diff --git a/source/adios2/core/VariableCompound.h b/source/adios2/core/VariableCompound.h
index 369058c8553c456451a558827b21fe1b22bd9506..70494e0e6968f6ee96dbfea8a41569510e31fc43 100644
--- a/source/adios2/core/VariableCompound.h
+++ b/source/adios2/core/VariableCompound.h
@@ -41,7 +41,7 @@ public:
 
     VariableCompound(const std::string name, const std::size_t sizeOfStruct,
                      const Dims shape, const Dims start, const Dims count,
-                     const bool constantShape, const bool debugMode);
+                     const bool constantDims, const bool debugMode);
 
     ~VariableCompound() = default;
 
diff --git a/source/adios2/engine/bp/BPFileWriter.cpp b/source/adios2/engine/bp/BPFileWriter.cpp
index a37b69c68de70986c81a5ae00d2557b8cdc548d4..175d9975645db538748c467d26635aaeeced01d5 100644
--- a/source/adios2/engine/bp/BPFileWriter.cpp
+++ b/source/adios2/engine/bp/BPFileWriter.cpp
@@ -12,6 +12,7 @@
 #include "BPFileWriter.tcc"
 
 #include "adios2/ADIOSMPI.h"
+#include "adios2/ADIOSMacros.h"
 #include "adios2/core/IO.h"
 #include "adios2/helper/adiosFunctions.h" //CheckIndexRange
 #include "adios2/toolkit/transport/file/FileStream.h"
@@ -47,7 +48,7 @@ ADIOS2_FOREACH_TYPE_1ARG(declare_type)
 
 void BPFileWriter::Advance(const float /*timeout_sec*/)
 {
-    m_BP1Writer.Advance();
+    m_BP1Writer.Advance(m_IO);
 }
 
 void BPFileWriter::Close(const int transportIndex)
@@ -65,7 +66,7 @@ void BPFileWriter::Close(const int transportIndex)
     }
 
     // close bp buffer by flattening data and metadata
-    m_BP1Writer.Close();
+    m_BP1Writer.Close(m_IO);
     // send data to corresponding transports
     m_TransportsManager.WriteFiles(m_BP1Writer.m_HeapBuffer.GetData(),
                                    m_BP1Writer.m_HeapBuffer.m_DataPosition,
diff --git a/source/adios2/engine/bp/BPFileWriter.tcc b/source/adios2/engine/bp/BPFileWriter.tcc
index 649201ff80c59527eafed3ea291d0c4fe66861d4..d6ccde49cd61661e3bf1b5ec758e3306660c9292 100644
--- a/source/adios2/engine/bp/BPFileWriter.tcc
+++ b/source/adios2/engine/bp/BPFileWriter.tcc
@@ -34,15 +34,9 @@ void BPFileWriter::DoWriteCommon(Variable<T> &variable, const T *values)
 
     const size_t newSize = m_BP1Writer.m_HeapBuffer.GetDataSize();
 
-    //    if (resizeResult == format::BP1Base::ResizeResult::Success)
-    //    {
-    //        std::cout << "Old buffer size: " << oldSize << "\n";
-    //        std::cout << "New buffer size: " << newSize << "\n";
-    //    }
-
     if (resizeResult == format::BP1Base::ResizeResult::Flush)
     {
-        m_BP1Writer.Flush();
+        m_BP1Writer.Flush(m_IO);
         auto &heapBuffer = m_BP1Writer.m_HeapBuffer;
 
         m_TransportsManager.WriteFiles(heapBuffer.GetData(),
diff --git a/source/adios2/engine/hdf5/HDF5ReaderP.cpp b/source/adios2/engine/hdf5/HDF5ReaderP.cpp
index a1d3b8c3c664171189042ad1133ee87fc33ad901..985514e9bdc0a640dc631b974c06230fd87c790f 100644
--- a/source/adios2/engine/hdf5/HDF5ReaderP.cpp
+++ b/source/adios2/engine/hdf5/HDF5ReaderP.cpp
@@ -77,13 +77,17 @@ void HDF5ReaderP::UseHDFRead(const std::string &variableName, T *values,
     hsize_t dims[ndims];
     herr_t status_n = H5Sget_simple_extent_dims(fileSpace, dims, NULL);
 
-    hsize_t start[ndims] = {0}, count[ndims] = {0}, stride[ndims] = {1};
+    // hsize_t start[ndims] = {0}, count[ndims] = {0}, stride[ndims] = {1};
+    hsize_t start[ndims], count[ndims], stride[ndims];
 
     int totalElements = 1;
     for (int i = 0; i < ndims; i++)
     {
         count[i] = dims[i];
         totalElements *= dims[i];
+        start[i] = 0;
+        count[i] = 0;
+        stride[i] = 1;
     }
 
     start[0] = rank * dims[0] / size;
diff --git a/source/adios2/engine/plugin/PluginEngine.cpp b/source/adios2/engine/plugin/PluginEngine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c81aa060fa61b6e8b135d801f9e1682f0b57eb6d
--- /dev/null
+++ b/source/adios2/engine/plugin/PluginEngine.cpp
@@ -0,0 +1,191 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * PluginEngine.cpp
+ *
+ *  Created on: July 17, 2017
+ *      Author: Chuck Atkins <chuck.atkins@kitware.com>
+ */
+
+#include "PluginEngine.h"
+#include "PluginEngineInterface.h"
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <stdexcept>
+#include <utility>
+
+#include "adios2/helper/adiosDynamicBinder.h"
+
+namespace adios2
+{
+
+/******************************************************************************/
+
+struct PluginEngine::Impl
+{
+    using Registry =
+        std::map<std::string, std::pair<EngineCreateFun, EngineDestroyFun>>;
+    static Registry m_Registry;
+
+    std::string m_PluginName;
+    std::unique_ptr<adios2::DynamicBinder> m_Binder;
+    EngineCreateFun m_HandleCreate;
+    EngineDestroyFun m_HandleDestroy;
+    PluginEngineInterface *m_Plugin = nullptr;
+};
+PluginEngine::Impl::Registry PluginEngine::Impl::m_Registry;
+
+/******************************************************************************/
+
+void PluginEngine::RegisterPlugin(const std::string pluginName,
+                                  EngineCreateFun create,
+                                  EngineDestroyFun destroy)
+{
+    PluginEngine::Impl::m_Registry.emplace(pluginName,
+                                           std::make_pair(create, destroy));
+}
+
+/******************************************************************************/
+
+PluginEngine::PluginEngine(IO &io, const std::string &name,
+                           const OpenMode openMode, MPI_Comm mpiComm)
+: Engine("Plugin", io, name, openMode, mpiComm), m_Impl(new Impl)
+{
+    Init();
+    m_Impl->m_Plugin =
+        m_Impl->m_HandleCreate(io, m_Impl->m_PluginName, openMode, mpiComm);
+}
+
+PluginEngine::~PluginEngine() { m_Impl->m_HandleDestroy(m_Impl->m_Plugin); }
+
+void PluginEngine::PerformReads(ReadMode mode)
+{
+    m_Impl->m_Plugin->PerformReads(mode);
+}
+
+void PluginEngine::Release() { m_Impl->m_Plugin->Release(); }
+
+void PluginEngine::Advance(const float timeoutSeconds)
+{
+    m_Impl->m_Plugin->Advance(timeoutSeconds);
+}
+
+void PluginEngine::Advance(const AdvanceMode mode, const float timeoutSeconds)
+{
+    m_Impl->m_Plugin->Advance(mode, timeoutSeconds);
+}
+
+void PluginEngine::AdvanceAsync(const AdvanceMode mode,
+                                AdvanceAsyncCallback callback)
+{
+    m_Impl->m_Plugin->AdvanceAsync(mode, callback);
+}
+
+void PluginEngine::SetCallBack(
+    std::function<void(const void *, std::string, std::string, std::string,
+                       std::vector<size_t>)>
+        callback)
+{
+    m_Impl->m_Plugin->SetCallBack(callback);
+}
+
+void PluginEngine::Close(const int transportIndex)
+{
+    m_Impl->m_Plugin->Close(transportIndex);
+}
+
+void PluginEngine::Init()
+{
+    auto paramPluginNameIt = m_IO.m_Parameters.find("PluginName");
+    if (paramPluginNameIt == m_IO.m_Parameters.end())
+    {
+        throw std::invalid_argument("PluginEngine: PluginName must be "
+                                    "specified in engine parameters");
+    }
+    m_Impl->m_PluginName = paramPluginNameIt->second;
+
+    // First we check to see if we can find the plugin currently registerd
+    auto registryEntryIt =
+        PluginEngine::Impl::m_Registry.find(m_Impl->m_PluginName);
+
+    if (registryEntryIt != PluginEngine::Impl::m_Registry.end())
+    {
+        m_Impl->m_HandleCreate = registryEntryIt->second.first;
+        m_Impl->m_HandleDestroy = registryEntryIt->second.second;
+    }
+    else
+    {
+        // It's not currently registered so try to load it from a shared
+        // library
+        //
+        auto paramPluginLibraryIt = m_IO.m_Parameters.find("PluginLibrary");
+        if (paramPluginLibraryIt == m_IO.m_Parameters.end())
+        {
+            throw std::invalid_argument(
+                "PluginEngine: PluginLibrary must be specified in "
+                "engine parameters if no PluginName "
+                "is specified");
+        }
+        std::string &pluginLibrary = paramPluginLibraryIt->second;
+
+        m_Impl->m_Binder.reset(new adios2::DynamicBinder(pluginLibrary));
+
+        m_Impl->m_HandleCreate = reinterpret_cast<EngineCreatePtr>(
+            m_Impl->m_Binder->GetSymbol("EngineCreate"));
+        if (!m_Impl->m_HandleCreate)
+        {
+            throw std::runtime_error("PluginEngine: Unable to locate "
+                                     "EngineCreate symbol in specified plugin "
+                                     "library");
+        }
+
+        m_Impl->m_HandleDestroy = reinterpret_cast<EngineDestroyPtr>(
+            m_Impl->m_Binder->GetSymbol("EngineDestroy"));
+        if (!m_Impl->m_HandleDestroy)
+        {
+            throw std::runtime_error("PluginEngine: Unable to locate "
+                                     "EngineDestroy symbol in specified plugin "
+                                     "library");
+        }
+    }
+}
+
+#define define(T)                                                              \
+    void PluginEngine::DoWrite(Variable<T> &variable, const T *values)         \
+    {                                                                          \
+        m_Impl->m_Plugin->DoWrite(variable, values);                           \
+    }                                                                          \
+    void PluginEngine::DoScheduleRead(Variable<T> &variable, const T *values)  \
+    {                                                                          \
+        m_Impl->m_Plugin->DoScheduleRead(variable, values);                    \
+    }                                                                          \
+    void PluginEngine::DoScheduleRead(const std::string &variableName,         \
+                                      const T *values)                         \
+    {                                                                          \
+        m_Impl->m_Plugin->DoScheduleRead(variableName, values);                \
+    }
+ADIOS2_FOREACH_TYPE_1ARG(define)
+#undef define
+void PluginEngine::DoWrite(VariableCompound &variable, const void *values)
+{
+    m_Impl->m_Plugin->DoWrite(variable, values);
+}
+
+#define define(T, L)                                                           \
+    Variable<T> *PluginEngine::InquireVariable##L(const std::string &name,     \
+                                                  const bool readIn)           \
+    {                                                                          \
+        return m_Impl->m_Plugin->InquireVariable##L(name, readIn);             \
+    }
+ADIOS2_FOREACH_TYPE_2ARGS(define)
+#undef define
+VariableBase *PluginEngine::InquireVariableUnknown(const std::string &name,
+                                                   const bool readIn)
+{
+    return m_Impl->m_Plugin->InquireVariableUnknown(name, readIn);
+}
+
+} // end namespace adios
diff --git a/source/adios2/engine/plugin/PluginEngine.h b/source/adios2/engine/plugin/PluginEngine.h
new file mode 100644
index 0000000000000000000000000000000000000000..290f04efdab7b4d213ffb12dea2451e8b149f2b2
--- /dev/null
+++ b/source/adios2/engine/plugin/PluginEngine.h
@@ -0,0 +1,112 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * PluginEngine.h Support for an engine implemented outside libadios2
+ *
+ *  Created on: July 17, 2017
+ *      Author: Chuck Atkins <chuck.atkins@kitware.com>
+ */
+
+#ifndef ADIOS2_ENGINE_PLUGIN_PLUGINENGINE_H_
+#define ADIOS2_ENGINE_PLUGIN_PLUGINENGINE_H_
+
+#include "PluginEngineInterface.h"
+
+#include <functional>  // for function
+#include <memory>      // for unique_ptr
+#include <string>      // for string
+#include <type_traits> // for add_pointer
+#include <vector>      // for vector
+
+#include "adios2/ADIOSMPICommOnly.h"
+#include "adios2/ADIOSMacros.h"
+#include "adios2/ADIOSTypes.h"
+#include "adios2/core/Engine.h"
+#include "adios2/core/IO.h"
+#include "adios2/core/Variable.h"
+#include "adios2/core/VariableCompound.h"
+
+namespace adios2
+{
+
+/** A front-end wrapper for an engine implemented outside of libadios2 */
+class PluginEngine : public Engine
+{
+public:
+    // Function pointers used for the plugin factory methods
+
+    using EngineCreatePtr = std::add_pointer<PluginEngineInterface *(
+        IO &, const std::string &, const OpenMode, MPI_Comm)>::type;
+    using EngineDestroyPtr =
+        std::add_pointer<void(PluginEngineInterface *)>::type;
+    using EngineCreateFun =
+        std::function<std::remove_pointer<EngineCreatePtr>::type>;
+    using EngineDestroyFun =
+        std::function<std::remove_pointer<EngineDestroyPtr>::type>;
+
+    static void RegisterPlugin(const std::string pluginName,
+                               EngineCreateFun create,
+                               EngineDestroyFun destroy);
+    static void RegisterPlugin(const std::string pluginName,
+                               EngineCreatePtr create, EngineDestroyPtr destroy)
+    {
+        RegisterPlugin(pluginName, EngineCreateFun(create),
+                       EngineDestroyFun(destroy));
+    }
+
+    // This is just a shortcut method to handle the case where the class type is
+    // directly available to the caller so a sim[ple new and delete call is
+    // sufficient to create and destroy the engine object
+    template <typename T>
+    static void RegisterPlugin(const std::string name);
+
+public:
+    PluginEngine(IO &io, const std::string &name, const OpenMode openMode,
+                 MPI_Comm mpiComm);
+    virtual ~PluginEngine();
+
+    void PerformReads(ReadMode mode) override;
+    void Release() override;
+    void Advance(const float timeoutSeconds = 0.0) override;
+    void Advance(const AdvanceMode mode,
+                 const float timeoutSeconds = 0.0) override;
+    void AdvanceAsync(const AdvanceMode mode,
+                      AdvanceAsyncCallback callback) override;
+
+    void SetCallBack(std::function<void(const void *, std::string, std::string,
+                                        std::string, std::vector<size_t>)>
+                         callback) override;
+
+    void Close(const int transportIndex = -1) override;
+
+protected:
+    void Init() override;
+
+#define declare(T)                                                             \
+    void DoWrite(Variable<T> &variable, const T *values) override;             \
+    void DoScheduleRead(Variable<T> &variable, const T *values) override;      \
+    void DoScheduleRead(const std::string &variableName, const T *values)      \
+        override;
+    ADIOS2_FOREACH_TYPE_1ARG(declare)
+#undef declare
+    void DoWrite(VariableCompound &variable, const void *values) override;
+
+#define declare(T, L)                                                          \
+    Variable<T> *InquireVariable##L(const std::string &name,                   \
+                                    const bool readIn) override;
+    ADIOS2_FOREACH_TYPE_2ARGS(declare)
+#undef declare
+    VariableBase *InquireVariableUnknown(const std::string &name,
+                                         const bool readIn) override;
+
+private:
+    struct Impl;
+    std::unique_ptr<Impl> m_Impl;
+};
+
+} // end namespace adios
+
+#include "PluginEngine.inl"
+
+#endif /* ADIOS2_ENGINE_PLUGIN_PLUGINENGINE_H_ */
diff --git a/source/adios2/engine/plugin/PluginEngine.inl b/source/adios2/engine/plugin/PluginEngine.inl
new file mode 100644
index 0000000000000000000000000000000000000000..e28cbd8b327c306a64cb57bc4b83bc830ee74653
--- /dev/null
+++ b/source/adios2/engine/plugin/PluginEngine.inl
@@ -0,0 +1,36 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * PluginEngine.h Support for an engine implemented outside libadios2
+ *
+ *  Created on: July 17, 2017
+ *      Author: Chuck Atkins <chuck.atkins@kitware.com>
+ */
+
+#ifndef ADIOS2_ENGINE_PLUGIN_ENGINE_INL_
+#define ADIOS2_ENGINE_PLUGIN_ENGINE_INL_
+#ifndef ADIOS2_ENGINE_PLUGIN_PLUGINENGINE_H_
+#error "Inline file should only be included from it's header, never on it's own"
+#endif
+
+#include "PluginEngine.h"
+
+namespace adios2
+{
+
+template <typename T>
+void PluginEngine::RegisterPlugin(const std::string name)
+{
+    EngineCreateFun createFun =
+        [](IO &io, const std::string &name, const OpenMode openMode,
+           MPI_Comm mpiComm) -> PluginEngineInterface * {
+        return new T(io, name, openMode, mpiComm);
+    };
+    EngineDestroyFun destroyFun = [](Engine *obj) -> void { delete obj; };
+
+    RegisterPlugin(name, createFun, destroyFun);
+}
+}
+
+#endif // ADIOS2_ENGINE_PLUGIN_ENGINE_INL_
diff --git a/source/adios2/engine/plugin/PluginEngineInterface.cpp b/source/adios2/engine/plugin/PluginEngineInterface.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a6e20d69668f2e31f41c4deabe0319c130abeed4
--- /dev/null
+++ b/source/adios2/engine/plugin/PluginEngineInterface.cpp
@@ -0,0 +1,23 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * PluginEngineInterface.cxx
+ *
+ *  Created on: Aug 17, 2017
+ *      Author: Chuck Atkins <chuck.atkins@kitware.com>
+ */
+
+#include "PluginEngineInterface.h"
+
+namespace adios2
+{
+
+PluginEngineInterface::PluginEngineInterface(IO &io, const std::string &name,
+                                             const OpenMode openMode,
+                                             MPI_Comm mpiComm)
+: Engine("PluginInterface", io, name, openMode, mpiComm)
+{
+}
+
+} // end namespace adios
diff --git a/source/adios2/engine/plugin/PluginEngineInterface.h b/source/adios2/engine/plugin/PluginEngineInterface.h
new file mode 100644
index 0000000000000000000000000000000000000000..20d9e9c144f6b348528cf142856826b66ce24623
--- /dev/null
+++ b/source/adios2/engine/plugin/PluginEngineInterface.h
@@ -0,0 +1,41 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * PluginEngineInterface.h Engines using the plugin interface should derive from
+ * this class.
+ *
+ *  Created on: July 17, 2017
+ *      Author: Chuck Atkins <chuck.atkins@kitware.com>
+ */
+
+#ifndef ADIOS2_ENGINE_PLUGIN_PLUGINENGINEINTERFACE_H_
+#define ADIOS2_ENGINE_PLUGIN_PLUGINENGINEINTERFACE_H_
+
+/// \cond EXCLUDE_FROM_DOXYGEN
+/// \endcond
+
+#include "adios2/ADIOSConfig.h"
+#include "adios2/ADIOSMPICommOnly.h"
+#include "adios2/ADIOSTypes.h"
+#include "adios2/core/Engine.h"
+#include "adios2/core/IO.h"
+
+namespace adios2
+{
+
+/** An engine interface to be used aby the plugin infrastructure */
+class PluginEngineInterface : public Engine
+{
+    // Give the plugin engine access to everything
+    friend class PluginEngine;
+
+public:
+    PluginEngineInterface(IO &io, const std::string &name,
+                          const OpenMode openMode, MPI_Comm mpiComm);
+    virtual ~PluginEngineInterface() = default;
+};
+
+} // end namespace adios
+
+#endif /* ADIOS2_ENGINE_PLUGIN_PLUGINENGINEINTERFACE_H_ */
diff --git a/source/adios2/helper/adiosDynamicBinder.cpp b/source/adios2/helper/adiosDynamicBinder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..00ecd6342178804ac83816e31f16f46f71d798a5
--- /dev/null
+++ b/source/adios2/helper/adiosDynamicBinder.cpp
@@ -0,0 +1,99 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * DynamicBinder.cpp
+ *
+ * Created on: Jul 21, 2017
+ *     Author: Chuck Atkins
+ */
+
+#include "adiosDynamicBinder.h"
+
+#include <algorithm> // for copy
+#include <iostream>  // for operator<<, stringstream, bas...
+#include <iterator>  // for ostream_iterator
+#include <sstream>   // for stringstream
+#include <stdexcept> // for runtime_error
+#include <vector>    // for vector
+
+#include <adios2sys/DynamicLoader.hxx>
+
+namespace adios2
+{
+
+struct DynamicBinder::Impl
+{
+    adios2sys::DynamicLoader::LibraryHandle m_LibraryHandle;
+};
+
+DynamicBinder::DynamicBinder(std::string libName)
+{
+    std::vector<std::string> libPrefixes;
+    libPrefixes.emplace_back("");
+    libPrefixes.emplace_back("lib");
+#ifdef __CYGWIN__
+    libPrefixes.emplace_back("cyg");
+#endif
+
+    std::vector<std::string> libSuffixes;
+#ifdef __APPLE__
+    libSuffixes.emplace_back(".dylib");
+    libSuffixes.emplace_back(".so");
+#endif
+#ifdef __hpux
+    libSuffixes.emplace_back(".sl");
+#endif
+#ifdef __unix__
+    libSuffixes.emplace_back(".so");
+#endif
+#ifdef _WIN32
+    libSuffixes.emplace_back(".dll");
+#endif
+
+    std::vector<std::string> searchedLibs;
+    std::string fileName;
+
+    // Test the various combinations of library names
+    for (const std::string &prefix : libPrefixes)
+    {
+        for (const std::string &suffix : libSuffixes)
+        {
+            fileName = prefix + libName + suffix;
+            m_Impl->m_LibraryHandle =
+                adios2sys::DynamicLoader::OpenLibrary(fileName);
+            searchedLibs.push_back(fileName);
+            if (m_Impl->m_LibraryHandle)
+            {
+                break;
+            }
+        }
+        if (m_Impl->m_LibraryHandle)
+        {
+            break;
+        }
+    }
+    if (!m_Impl->m_LibraryHandle)
+    {
+        std::stringstream errString;
+        errString << "Unable to locate the " << libName
+                  << " library; searched for ";
+        std::copy(searchedLibs.begin(), searchedLibs.end(),
+                  std::ostream_iterator<std::string>(errString, " "));
+
+        throw std::runtime_error(errString.str());
+    }
+}
+
+DynamicBinder::~DynamicBinder()
+{
+    adios2sys::DynamicLoader::CloseLibrary(m_Impl->m_LibraryHandle);
+}
+
+DynamicBinder::VoidSymbolPointer
+DynamicBinder::GetSymbol(std::string symbolName)
+{
+    return adios2sys::DynamicLoader::GetSymbolAddress(m_Impl->m_LibraryHandle,
+                                                      symbolName);
+}
+};
diff --git a/source/adios2/helper/adiosDynamicBinder.h b/source/adios2/helper/adiosDynamicBinder.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e68cde4595bf4d7f3f55e693356bb4fc08b6db0
--- /dev/null
+++ b/source/adios2/helper/adiosDynamicBinder.h
@@ -0,0 +1,38 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * DynamicBinder.h
+ *
+ * Created on: Jul 21, 2017
+ *     Author: Chuck Atkins
+ */
+
+#ifndef ADIOS2_HELPER_DYNAMICBINDER_H_
+#define ADIOS2_HELPER_DYNAMICBINDER_H_
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+namespace adios2
+{
+
+class DynamicBinder
+{
+public:
+    using VoidSymbolPointer = std::add_pointer<void()>::type;
+
+public:
+    DynamicBinder(std::string libName);
+    ~DynamicBinder();
+
+    VoidSymbolPointer GetSymbol(std::string symbolName);
+
+private:
+    struct Impl;
+    std::unique_ptr<Impl> m_Impl;
+};
+}
+
+#endif // ADIOS2_HELPER_DYNAMICBINDER_H_
diff --git a/source/adios2/helper/adiosType.inl b/source/adios2/helper/adiosType.inl
index 5c4243b965b168a164bfd1e5e4239ab93ac20ae3..b81bb598183f5f11080ef293a61f3ed396366718 100644
--- a/source/adios2/helper/adiosType.inl
+++ b/source/adios2/helper/adiosType.inl
@@ -27,12 +27,24 @@ inline std::string GetType<void>() noexcept
 {
     return "unknown";
 }
+
+template <>
+inline std::string GetType<std::string>() noexcept
+{
+    return "string";
+}
+
 template <>
 inline std::string GetType<char>() noexcept
 {
     return "char";
 }
 template <>
+inline std::string GetType<signed char>() noexcept
+{
+    return "signed char";
+}
+template <>
 inline std::string GetType<unsigned char>() noexcept
 {
     return "unsigned char";
diff --git a/source/adios2/mpidummy.cpp b/source/adios2/mpidummy.cpp
index c97938e95d78063c73195ac0cacd2275335f516c..51a5dcf881e3c516d90adaee0388da69b9ccee91 100644
--- a/source/adios2/mpidummy.cpp
+++ b/source/adios2/mpidummy.cpp
@@ -41,6 +41,8 @@
 
 namespace adios2
 {
+namespace mpi
+{
 
 static char mpierrmsg[MPI_MAX_ERROR_STRING];
 
@@ -380,4 +382,5 @@ int MPI_Get_processor_name(char *name, int *resultlen)
     return 0;
 }
 
+} // end namespace mpi
 } // end namespace adios
diff --git a/source/adios2/mpidummy.h b/source/adios2/mpidummy.h
index 5457f0dd7788c55653787571271a4294878d9079..1194d2a1f9d2714d10cf6d4cd5a8496809c9431e 100644
--- a/source/adios2/mpidummy.h
+++ b/source/adios2/mpidummy.h
@@ -16,6 +16,8 @@
 
 namespace adios2
 {
+namespace mpi
+{
 
 typedef int MPI_Comm;
 typedef std::uint64_t MPI_Status;
@@ -123,6 +125,7 @@ int MPI_Get_processor_name(char *name, int *resultlen);
 
 double MPI_Wtime();
 
+} // end namespace mpi
 } // end namespace adios
 
 #endif /* ADIOS2_MPIDUMMY_H_ */
diff --git a/source/adios2/toolkit/format/bp1/BP1Base.cpp b/source/adios2/toolkit/format/bp1/BP1Base.cpp
index ecc990f38bef44f499eb417f0e9e609845ae5a6c..8d4e6a314d3e78031870d1f652c5da7cbc936f40 100644
--- a/source/adios2/toolkit/format/bp1/BP1Base.cpp
+++ b/source/adios2/toolkit/format/bp1/BP1Base.cpp
@@ -11,6 +11,7 @@
 #include "BP1Base.h"
 #include "BP1Base.tcc"
 
+#include "adios2/ADIOSTypes.h"            //PathSeparator
 #include "adios2/helper/adiosFunctions.h" //CreateDirectory, StringToTimeUnit
 
 namespace adios2
@@ -111,8 +112,16 @@ BP1Base::GetBPNames(const std::vector<std::string> &baseNames) const noexcept
                            const int rank) -> std::string {
 
         const std::string bpBaseName = AddExtension(baseName, ".bp");
-        // name.bp.dir/name.bp.rank
-        const std::string bpName(bpBaseName + ".dir/" + bpBaseName + "." +
+
+        // path/root.bp.dir/root.bp.rank
+        std::string bpRootName = bpBaseName;
+        const auto lastPathSeparator(bpBaseName.find_last_of(PathSeparator));
+
+        if (lastPathSeparator != std::string::npos)
+        {
+            bpRootName = bpBaseName.substr(lastPathSeparator);
+        }
+        const std::string bpName(bpBaseName + ".dir/" + bpRootName + "." +
                                  std::to_string(rank));
         return bpName;
     };
@@ -424,4 +433,4 @@ ADIOS2_FOREACH_TYPE_1ARG(declare_template_instantiation)
 #undef declare_template_instantiation
 
 } // end namespace format
-} // end namespace adios
+} // end namespace adios2
diff --git a/source/adios2/toolkit/format/bp1/BP1Base.h b/source/adios2/toolkit/format/bp1/BP1Base.h
index 084a26298cbb7bbd879ddfe4f153ee85b57ee5ea..083c3861ab20268ccfd283e07e389e3c96228bf6 100644
--- a/source/adios2/toolkit/format/bp1/BP1Base.h
+++ b/source/adios2/toolkit/format/bp1/BP1Base.h
@@ -303,6 +303,6 @@ ADIOS2_FOREACH_TYPE_1ARG(declare_template_instantiation)
 #undef declare_template_instantiation
 
 } // end namespace format
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_TOOLKIT_FORMAT_BP1_BP1BASE_H_ */
diff --git a/source/adios2/toolkit/format/bp1/BP1Base.tcc b/source/adios2/toolkit/format/bp1/BP1Base.tcc
index 4d5aab4e14dc8dfe209175d819f849517c8f56a9..58b361b465d985fbfa27db2e416824c45ea7ca2b 100644
--- a/source/adios2/toolkit/format/bp1/BP1Base.tcc
+++ b/source/adios2/toolkit/format/bp1/BP1Base.tcc
@@ -22,12 +22,24 @@ namespace adios2
 namespace format
 {
 
+template <>
+int8_t BP1Base::GetDataType<std::string>() const noexcept
+{
+    return type_string;
+}
+
 template <>
 int8_t BP1Base::GetDataType<char>() const noexcept
 {
     return type_byte;
 }
 
+template <>
+int8_t BP1Base::GetDataType<signed char>() const noexcept
+{
+    return type_byte;
+}
+
 template <>
 int8_t BP1Base::GetDataType<short>() const noexcept
 {
diff --git a/source/adios2/toolkit/format/bp1/BP1Structs.h b/source/adios2/toolkit/format/bp1/BP1Structs.h
index b62b79f582d855fa5646a53bbfb364a6f29313f7..9d1724c9f2210858fb063500ba5a1c3e03a26d57 100644
--- a/source/adios2/toolkit/format/bp1/BP1Structs.h
+++ b/source/adios2/toolkit/format/bp1/BP1Structs.h
@@ -58,7 +58,8 @@ struct BP1MetadataSet
      */
     uint32_t TimeStep = 1;
 
-    BP1Index PGIndex = BP1Index(0); ///< single buffer for PGIndex
+    /** single buffer for PGIndex */
+    BP1Index PGIndex = BP1Index(0);
 
     // no priority for now
     /** @brief key: variable name, value: bp metadata variable index */
@@ -67,7 +68,10 @@ struct BP1MetadataSet
     /** @brief key: attribute name, value: bp metadata attribute index */
     std::unordered_map<std::string, BP1Index> AttributesIndices;
 
-    const unsigned int MiniFooterSize = 28; ///< from bpls reader
+    bool AreAttributesWritten = false;
+
+    /** Fixed size for mini footer */
+    const unsigned int MiniFooterSize = 28;
 
     // PG (relative) positions in Data buffer, to be updated every advance step
     // or init
@@ -85,6 +89,6 @@ struct BP1MetadataSet
 };
 
 } // end namespace format
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_TOOLKIT_FORMAT_BP1_BP1STRUCTS_H_ */
diff --git a/source/adios2/toolkit/format/bp1/BP1Writer.cpp b/source/adios2/toolkit/format/bp1/BP1Writer.cpp
index 7bc9f34b18e379806764b3c36808272b03d67e76..103d56e03a0b944ccd2c0a9d1c3d83eec097055d 100644
--- a/source/adios2/toolkit/format/bp1/BP1Writer.cpp
+++ b/source/adios2/toolkit/format/bp1/BP1Writer.cpp
@@ -14,6 +14,8 @@
 #include <string>
 #include <vector>
 
+#include "adios2/helper/adiosFunctions.h" //GetType<T>
+
 namespace adios2
 {
 namespace format
@@ -117,10 +119,14 @@ void BP1Writer::WriteProcessGroupIndex(
     }
 }
 
-void BP1Writer::Advance()
+void BP1Writer::Advance(IO &io)
 {
     // enforce memory policy here to restrict buffer size for each timestep
     // this is flushing
+    if (m_Profiler.IsActive)
+    {
+        m_Profiler.Timers.at("buffering").Resume();
+    }
 
     if (m_MaxBufferSize == DefaultMaxBufferSize)
     {
@@ -128,12 +134,7 @@ void BP1Writer::Advance()
         m_MaxBufferSize = m_HeapBuffer.m_DataPosition + 64;
     }
 
-    if (m_Profiler.IsActive)
-    {
-        m_Profiler.Timers.at("buffering").Resume();
-    }
-
-    FlattenData();
+    FlattenData(io);
     ++m_MetadataSet.TimeStep;
 
     if (m_Profiler.IsActive)
@@ -142,14 +143,14 @@ void BP1Writer::Advance()
     }
 }
 
-void BP1Writer::Flush()
+void BP1Writer::Flush(IO &io)
 {
     if (m_Profiler.IsActive)
     {
         m_Profiler.Timers.at("buffering").Resume();
     }
 
-    FlattenData();
+    FlattenData(io);
 
     if (m_Profiler.IsActive)
     {
@@ -157,7 +158,7 @@ void BP1Writer::Flush()
     }
 }
 
-void BP1Writer::Close() noexcept
+void BP1Writer::Close(IO &io) noexcept
 {
     if (m_Profiler.IsActive)
     {
@@ -168,7 +169,7 @@ void BP1Writer::Close() noexcept
     {
         if (m_MetadataSet.DataPGIsOpen)
         {
-            FlattenData();
+            FlattenData(io);
         }
 
         FlattenMetadata();
@@ -248,9 +249,64 @@ BP1Writer::AggregateProfilingJSON(const std::string &rankProfilingLog) noexcept
 }
 
 // PRIVATE FUNCTIONS
-void BP1Writer::WriteDimensionsRecord(const Dims localDimensions,
-                                      const Dims globalDimensions,
-                                      const Dims offsets,
+void BP1Writer::WriteAttributes(IO &io)
+{
+    const auto attributesDataMap = io.GetAttributesDataMap();
+
+    auto &buffer = m_HeapBuffer.m_Data;
+    auto &position = m_HeapBuffer.m_DataPosition;
+
+    // used only to update m_HeapBuffer.m_DataAbsolutePosition;
+    const size_t attributesCountPosition = position;
+
+    // count is known ahead of time, write
+    const uint32_t attributesCount =
+        static_cast<const uint32_t>(attributesDataMap.size());
+    CopyToBuffer(buffer, position, &attributesCount);
+
+    // will go back
+    const size_t attributesLengthPosition = position;
+    position += 8; // skip attributes length
+
+    m_HeapBuffer.m_DataAbsolutePosition += position - attributesCountPosition;
+
+    uint32_t memberID = 0;
+
+    for (const auto &attributePair : attributesDataMap)
+    {
+        const std::string name(attributePair.first);
+        const std::string type(attributePair.second.first);
+
+        if (type == "unknown")
+        {
+        }
+#define declare_type(T)                                                        \
+    else if (type == GetType<T>())                                             \
+    {                                                                          \
+        Stats<T> stats;                                                        \
+        stats.Offset = m_HeapBuffer.m_DataAbsolutePosition;                    \
+        stats.MemberID = memberID;                                             \
+        Attribute<T> &attribute = io.GetAttribute<T>(name);                    \
+        WriteAttributeInData(attribute, stats);                                \
+        WriteAttributeInIndex(attribute, stats);                               \
+    }
+        ADIOS2_FOREACH_ATTRIBUTE_TYPE_1ARG(declare_type)
+#undef declare_type
+
+        ++memberID;
+    }
+
+    // complete attributes length
+    const uint64_t attributesLength =
+        static_cast<const uint64_t>(position - attributesLengthPosition);
+
+    size_t backPosition = attributesLengthPosition;
+    CopyToBuffer(buffer, backPosition, &attributesLength);
+}
+
+void BP1Writer::WriteDimensionsRecord(const Dims &localDimensions,
+                                      const Dims &globalDimensions,
+                                      const Dims &offsets,
                                       std::vector<char> &buffer) noexcept
 {
     if (offsets.empty())
@@ -272,9 +328,9 @@ void BP1Writer::WriteDimensionsRecord(const Dims localDimensions,
     }
 }
 
-void BP1Writer::WriteDimensionsRecord(const Dims localDimensions,
-                                      const Dims globalDimensions,
-                                      const Dims offsets,
+void BP1Writer::WriteDimensionsRecord(const Dims &localDimensions,
+                                      const Dims &globalDimensions,
+                                      const Dims &offsets,
                                       std::vector<char> &buffer,
                                       size_t &position,
                                       const bool isCharacteristic) noexcept
@@ -356,7 +412,7 @@ BP1Writer::GetBP1Index(const std::string name,
     return itName->second;
 }
 
-void BP1Writer::FlattenData() noexcept
+void BP1Writer::FlattenData(IO &io) noexcept
 {
     auto &buffer = m_HeapBuffer.m_Data;
     auto &position = m_HeapBuffer.m_DataPosition;
@@ -369,13 +425,19 @@ void BP1Writer::FlattenData() noexcept
         position - m_MetadataSet.DataPGVarsCountPosition - 8 - 4;
     CopyToBuffer(buffer, m_MetadataSet.DataPGVarsCountPosition, &varsLength);
 
-    // attributes (empty for now) count (4) and length (8) are zero by moving
-    // positions in time step zero
-    position += 12;
-    m_HeapBuffer.m_DataAbsolutePosition += 12;
+    // attributes are only written once
+    if (!m_MetadataSet.AreAttributesWritten)
+    {
+        WriteAttributes(io);
+        m_MetadataSet.AreAttributesWritten = true;
+    }
+    else
+    {
+        position += 12;
+        m_HeapBuffer.m_DataAbsolutePosition += 12;
+    }
 
-    // Finish writing pg group length
-    // without record itself, 12 due to empty attributes
+    // Finish writing pg group length without record itself
     const uint64_t dataPGLength =
         position - m_MetadataSet.DataPGLengthPosition - 8;
     CopyToBuffer(buffer, m_MetadataSet.DataPGLengthPosition, &dataPGLength);
@@ -385,7 +447,7 @@ void BP1Writer::FlattenData() noexcept
 
 void BP1Writer::FlattenMetadata() noexcept
 {
-    auto lf_IndexCountLength =
+    auto lf_SetIndexCountLength =
         [](std::unordered_map<std::string, BP1Index> &indices, uint32_t &count,
            uint64_t &length) {
 
@@ -426,12 +488,13 @@ void BP1Writer::FlattenMetadata() noexcept
     // var index count and length (total), and each index length
     uint32_t varsCount;
     uint64_t varsLength;
-    lf_IndexCountLength(m_MetadataSet.VarsIndices, varsCount, varsLength);
+    lf_SetIndexCountLength(m_MetadataSet.VarsIndices, varsCount, varsLength);
+
     // attribute index count and length, and each index length
     uint32_t attributesCount;
     uint64_t attributesLength;
-    lf_IndexCountLength(m_MetadataSet.AttributesIndices, attributesCount,
-                        attributesLength);
+    lf_SetIndexCountLength(m_MetadataSet.AttributesIndices, attributesCount,
+                           attributesLength);
 
     const size_t footerSize = static_cast<const size_t>(
         (pgLength + 16) + (varsLength + 12) + (attributesLength + 12) +
diff --git a/source/adios2/toolkit/format/bp1/BP1Writer.h b/source/adios2/toolkit/format/bp1/BP1Writer.h
index 7f65b749e2730dece386db1e273a4f77cf7208cf..5ba3fed6f527dbba902cfb4c9bfed640370319e7 100644
--- a/source/adios2/toolkit/format/bp1/BP1Writer.h
+++ b/source/adios2/toolkit/format/bp1/BP1Writer.h
@@ -20,6 +20,8 @@
 #include "adios2/ADIOSConfig.h"
 #include "adios2/ADIOSMacros.h"
 #include "adios2/ADIOSTypes.h"
+#include "adios2/core/Attribute.h"
+#include "adios2/core/IO.h"
 #include "adios2/core/Variable.h"
 #include "adios2/toolkit/capsule/heap/STLVector.h"
 #include "adios2/toolkit/format/bp1/BP1Base.h"
@@ -41,7 +43,7 @@ public:
      */
     BP1Writer(MPI_Comm mpiComm, const bool debugMode = false);
 
-    virtual ~BP1Writer() = default;
+    ~BP1Writer() = default;
 
     /**
      * Writes a process group index PGIndex and list of methods (from
@@ -69,16 +71,16 @@ public:
     void WriteVariablePayload(const Variable<T> &variable) noexcept;
 
     /** Flattens data buffer and closes current process group */
-    void Advance();
+    void Advance(IO &io);
 
     /** Flattens data buffer and close current process group, doesn't
      *  advance time index */
-    void Flush();
+    void Flush(IO &io);
 
     /**
      * @param isFirstClose true: first time close, false: already closed buffer
      */
-    void Close() noexcept;
+    void Close(IO &io) noexcept;
 
     /**
      * Get a string with profiling information for this rank
@@ -102,6 +104,65 @@ private:
     /** BP format version */
     const uint8_t m_Version = 3;
 
+    /**
+     * Writes in BP buffer all attributes defined in an IO object.
+     * Called by FlattenData function
+     * @param io input containing attributes
+     */
+    void WriteAttributes(IO &io);
+
+    /**
+     * Called from WriteAttributeInData specialized functions
+     * @param attribute input
+     * @param stats
+     * @return attribute length position
+     */
+    template <class T>
+    size_t WriteAttributeHeaderInData(const Attribute<T> &attribute,
+                                      Stats<T> &stats) noexcept;
+
+    /**
+     * Called from WriteAttributeInData specialized functions
+     * @param attribute input
+     * @param stats
+     * @param attributeLengthPosition
+     */
+    template <class T>
+    void
+    WriteAttributeLengthInData(const Attribute<T> &attribute, Stats<T> &stats,
+                               const size_t attributeLengthPosition) noexcept;
+
+    /**
+     * Write a single attribute in data buffer, called from WriteAttributes
+     * @param attribute input
+     * @param stats
+     */
+    template <class T>
+    void WriteAttributeInData(const Attribute<T> &attribute,
+                              Stats<T> &stats) noexcept;
+
+    /**
+     * Writes attribute value in index characteristic value.
+     * @param characteristicID
+     * @param characteristicsCounter
+     * @param attribute
+     * @param buffer
+     */
+    template <class T>
+    void WriteAttributeCharacteristicValueInIndex(
+        std::uint8_t &characteristicsCounter, const Attribute<T> &attribute,
+        std::vector<char> &buffer) noexcept;
+
+    /**
+     * Write a single attribute in m_Metadata AttributesIndex, called from
+     * WriteAttributes
+     * @param attribute
+     * @param stats
+     */
+    template <class T>
+    void WriteAttributeInIndex(const Attribute<T> &attribute,
+                               const Stats<T> &stats) noexcept;
+
     /**
      * Get variable statistics
      * @param variable
@@ -160,14 +221,16 @@ private:
      * data
      * characteristic
      */
-    void WriteDimensionsRecord(const Dims localDimensions,
-                               const Dims globalDimensions, const Dims offsets,
+    void WriteDimensionsRecord(const Dims &localDimensions,
+                               const Dims &globalDimensions,
+                               const Dims &offsets,
                                std::vector<char> &buffer) noexcept;
 
     /** Overloaded version for data buffer */
-    void WriteDimensionsRecord(const Dims localDimensions,
-                               const Dims globalDimensions, const Dims offsets,
-                               std::vector<char> &buffer, size_t &position,
+    void WriteDimensionsRecord(const Dims &localDimensions,
+                               const Dims &globalDimensions,
+                               const Dims &offsets, std::vector<char> &buffer,
+                               size_t &position,
                                const bool isCharacteristic = false) noexcept;
 
     /** Writes min max */
@@ -224,7 +287,7 @@ private:
      * @param metadataSet
      * @param buffer
      */
-    void FlattenData() noexcept;
+    void FlattenData(IO &io) noexcept;
 
     /**
      * Flattens the metadata indices into a single metadata buffer in capsule
@@ -245,6 +308,6 @@ ADIOS2_FOREACH_TYPE_1ARG(declare_template_instantiation)
 #undef declare_template_instantiation
 
 } // end namespace format
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_UTILITIES_FORMAT_BP1_BP1WRITER_H_ */
diff --git a/source/adios2/toolkit/format/bp1/BP1Writer.tcc b/source/adios2/toolkit/format/bp1/BP1Writer.tcc
index c1aa88ca6643d8e98fe48c3c4145b137b7ec7ff4..321edf743638cd0e293f5f64dd90e6bd6a1d36b6 100644
--- a/source/adios2/toolkit/format/bp1/BP1Writer.tcc
+++ b/source/adios2/toolkit/format/bp1/BP1Writer.tcc
@@ -62,6 +62,7 @@ void BP1Writer::WriteVariablePayload(const Variable<T> &variable) noexcept
 
     CopyToBufferThreads(m_HeapBuffer.m_Data, m_HeapBuffer.m_DataPosition,
                         variable.m_AppValues, variable.TotalSize(), m_Threads);
+
     m_HeapBuffer.m_DataAbsolutePosition += variable.PayLoadSize();
 
     if (m_Profiler.IsActive)
@@ -71,6 +72,262 @@ void BP1Writer::WriteVariablePayload(const Variable<T> &variable) noexcept
 }
 
 // PRIVATE
+template <class T>
+size_t BP1Writer::WriteAttributeHeaderInData(const Attribute<T> &attribute,
+                                             Stats<T> &stats) noexcept
+{
+    auto &buffer = m_HeapBuffer.m_Data;
+    auto &position = m_HeapBuffer.m_DataPosition;
+
+    // will go back to write length
+    const size_t attributeLengthPosition = position;
+    position += 4; // skip length
+
+    CopyToBuffer(buffer, position, &stats.MemberID);
+    WriteNameRecord(attribute.m_Name, buffer, position);
+    position += 2; // skip path
+
+    // TODO: attribute from Variable??
+    constexpr int8_t no = 'n';
+    CopyToBuffer(buffer, position, &no); // not associated with a Variable
+
+    return attributeLengthPosition;
+}
+
+template <class T>
+void BP1Writer::WriteAttributeLengthInData(
+    const Attribute<T> &attribute, Stats<T> &stats,
+    const size_t attributeLengthPosition) noexcept
+{
+    auto &buffer = m_HeapBuffer.m_Data;
+    auto &position = m_HeapBuffer.m_DataPosition;
+
+    // back to attribute length
+    const uint32_t attributeLength =
+        static_cast<const uint32_t>(position - attributeLengthPosition);
+    size_t backPosition = attributeLengthPosition;
+    CopyToBuffer(buffer, backPosition, &attributeLengthPosition);
+
+    m_HeapBuffer.m_DataAbsolutePosition += position - attributeLengthPosition;
+}
+
+template <>
+inline void
+BP1Writer::WriteAttributeInData(const Attribute<std::string> &attribute,
+                                Stats<std::string> &stats) noexcept
+{
+    const size_t attributeLengthPosition =
+        WriteAttributeHeaderInData(attribute, stats);
+
+    auto &buffer = m_HeapBuffer.m_Data;
+    auto &position = m_HeapBuffer.m_DataPosition;
+
+    uint8_t dataType = GetDataType<std::string>();
+    if (!attribute.m_IsSingleValue)
+    {
+        dataType = type_string_array;
+    }
+    CopyToBuffer(buffer, position, &dataType);
+
+    // here record payload offset
+    stats.PayloadOffset = m_HeapBuffer.m_DataAbsolutePosition + position -
+                          attributeLengthPosition;
+
+    if (dataType == type_string)
+    {
+        const uint32_t dataSize =
+            static_cast<const uint32_t>(attribute.m_DataSingleValue.size());
+        CopyToBuffer(buffer, position, &dataSize);
+        CopyToBuffer(buffer, position, attribute.m_DataSingleValue.data(),
+                     attribute.m_DataSingleValue.size());
+    }
+    else if (dataType == type_string_array)
+    {
+        const uint32_t elements =
+            static_cast<const uint32_t>(attribute.m_Elements);
+        CopyToBuffer(buffer, position, &elements);
+
+        for (size_t s = 0; s < attribute.m_Elements; ++s)
+        {
+            // include zero terminated
+            const std::string element(attribute.m_DataArray[s] + '\0');
+
+            const uint32_t elementSize =
+                static_cast<const uint32_t>(element.size());
+
+            CopyToBuffer(buffer, position, &elementSize);
+            CopyToBuffer(buffer, position, element.data(), element.size());
+        }
+    }
+
+    WriteAttributeLengthInData(attribute, stats, attributeLengthPosition);
+}
+
+template <class T>
+void BP1Writer::WriteAttributeInData(const Attribute<T> &attribute,
+                                     Stats<T> &stats) noexcept
+{
+    const size_t attributeLengthPosition =
+        WriteAttributeHeaderInData(attribute, stats);
+
+    auto &buffer = m_HeapBuffer.m_Data;
+    auto &position = m_HeapBuffer.m_DataPosition;
+
+    uint8_t dataType = GetDataType<T>();
+    CopyToBuffer(buffer, position, &dataType);
+
+    // here record payload offset
+    stats.PayloadOffset = m_HeapBuffer.m_DataAbsolutePosition + position -
+                          attributeLengthPosition;
+
+    const uint32_t dataSize = attribute.m_Elements * sizeof(T);
+    CopyToBuffer(buffer, position, &dataSize);
+
+    if (attribute.m_IsSingleValue) // single value
+    {
+        CopyToBuffer(buffer, position, &attribute.m_DataSingleValue);
+    }
+    else // array
+    {
+        CopyToBuffer(buffer, position, attribute.m_DataArray.data(),
+                     attribute.m_Elements);
+    }
+
+    WriteAttributeLengthInData(attribute, stats, attributeLengthPosition);
+}
+
+template <>
+inline void BP1Writer::WriteAttributeCharacteristicValueInIndex(
+    uint8_t &characteristicsCounter, const Attribute<std::string> &attribute,
+    std::vector<char> &buffer) noexcept
+{
+    uint8_t characteristicID = characteristic_value;
+
+    InsertToBuffer(buffer, &characteristicID);
+
+    if (attribute.m_IsSingleValue) // Single string
+    {
+        const uint16_t dataSize =
+            static_cast<const uint16_t>(attribute.m_DataSingleValue.size());
+        InsertToBuffer(buffer, &dataSize);
+        InsertToBuffer(buffer, attribute.m_DataSingleValue.data(),
+                       attribute.m_DataSingleValue.size());
+    }
+    else // string array
+    {
+        for (size_t s = 0; s < attribute.m_Elements; ++s)
+        {
+            // without zero terminated character
+            const std::string element(attribute.m_DataArray[s]);
+
+            const uint16_t elementSize =
+                static_cast<const uint16_t>(element.size());
+
+            InsertToBuffer(buffer, &elementSize);
+            InsertToBuffer(buffer, element.data(), element.size());
+        }
+    }
+    ++characteristicsCounter;
+}
+
+template <class T>
+void BP1Writer::WriteAttributeCharacteristicValueInIndex(
+    uint8_t &characteristicsCounter, const Attribute<T> &attribute,
+    std::vector<char> &buffer) noexcept
+{
+    uint8_t characteristicID = characteristic_value;
+
+    InsertToBuffer(buffer, &characteristicID);
+
+    if (attribute.m_IsSingleValue) // single value
+    {
+        InsertToBuffer(buffer, &attribute.m_DataSingleValue);
+    }
+    else // array
+    {
+        InsertToBuffer(buffer, attribute.m_DataArray.data(),
+                       attribute.m_Elements);
+    }
+    ++characteristicsCounter;
+}
+
+template <class T>
+void BP1Writer::WriteAttributeInIndex(const Attribute<T> &attribute,
+                                      const Stats<T> &stats) noexcept
+{
+    BP1Index index(stats.MemberID);
+    auto &buffer = index.Buffer;
+
+    buffer.insert(buffer.end(), 4, '\0'); // skip attribute length (4)
+    InsertToBuffer(buffer, &stats.MemberID);
+    buffer.insert(buffer.end(), 2, '\0'); // skip group name
+    WriteNameRecord(attribute.m_Name, buffer);
+    buffer.insert(buffer.end(), 2, '\0'); // skip path
+
+    uint8_t dataType = GetDataType<T>(); // dataType
+
+    if (dataType == type_string && !attribute.m_IsSingleValue)
+    {
+        dataType = type_string_array;
+    }
+
+    InsertToBuffer(buffer, &dataType);
+
+    // Characteristics Sets Count in Metadata
+    index.Count = 1;
+    InsertToBuffer(buffer, &index.Count);
+
+    // START OF CHARACTERISTICS
+    const size_t characteristicsCountPosition = buffer.size();
+    // skip characteristics count(1) + length (4)
+    buffer.insert(buffer.end(), 5, '\0');
+    uint8_t characteristicsCounter = 0;
+
+    // DIMENSIONS
+    uint8_t characteristicID = characteristic_dimensions;
+    InsertToBuffer(buffer, &characteristicID);
+    constexpr uint8_t dimensions = 1;
+    InsertToBuffer(buffer, &dimensions); // count
+    constexpr uint16_t dimensionsLength = 24;
+    InsertToBuffer(buffer, &dimensionsLength); // length
+    WriteDimensionsRecord({attribute.m_Elements}, {}, {}, buffer);
+    ++characteristicsCounter;
+
+    // VALUE
+    WriteAttributeCharacteristicValueInIndex(characteristicsCounter, attribute,
+                                             buffer);
+
+    // TIME Index
+    WriteCharacteristicRecord(characteristic_time_index, characteristicsCounter,
+                              stats.TimeIndex, buffer);
+
+    const uint32_t rankU32 =
+        static_cast<const uint32_t>(m_BP1Aggregator.m_RankMPI);
+    WriteCharacteristicRecord(characteristic_file_index, characteristicsCounter,
+                              rankU32, buffer);
+
+    WriteCharacteristicRecord(characteristic_offset, characteristicsCounter,
+                              stats.Offset, buffer);
+
+    WriteCharacteristicRecord(characteristic_payload_offset,
+                              characteristicsCounter, stats.PayloadOffset,
+                              buffer);
+    // END OF CHARACTERISTICS
+
+    // Back to characteristics count and length
+    size_t backPosition = characteristicsCountPosition;
+    CopyToBuffer(buffer, backPosition, &characteristicsCounter); // count (1)
+
+    // remove its own length (4) + characteristic counter (1)
+    const uint32_t characteristicsLength = static_cast<const uint32_t>(
+        buffer.size() - characteristicsCountPosition - 4 - 1);
+
+    CopyToBuffer(buffer, backPosition, &characteristicsLength); // length
+
+    // Finish characteristic count length
+    m_MetadataSet.AttributesIndices.emplace(attribute.m_Name, index);
+}
+
 template <class T>
 BP1Writer::Stats<typename TypeInfo<T>::ValueType>
 BP1Writer::GetStats(const Variable<T> &variable) const noexcept
@@ -103,7 +360,7 @@ void BP1Writer::WriteVariableMetadataInData(
     WriteNameRecord(variable.m_Name, buffer, position);
     position += 2; // skip path
 
-    const uint8_t dataType = GetDataType<T>(); // dataType
+    const uint8_t dataType = GetDataType<T>();
     CopyToBuffer(buffer, position, &dataType);
 
     constexpr char no = 'n'; // isDimension
@@ -126,8 +383,8 @@ void BP1Writer::WriteVariableMetadataInData(
 
     // Back to varLength including payload size
     // not need to remove its own size (8) from length from bpdump
-    const uint64_t varLength =
-        position - varLengthPosition + variable.PayLoadSize();
+    const uint64_t varLength = static_cast<const uint64_t>(
+        position - varLengthPosition + variable.PayLoadSize());
 
     size_t backPosition = varLengthPosition;
     CopyToBuffer(buffer, backPosition, &varLength);
@@ -143,8 +400,7 @@ void BP1Writer::WriteVariableMetadataInIndex(
 {
     auto &buffer = index.Buffer;
 
-    if (isNew) // write variable header (might be shared with
-               // attributes index)
+    if (isNew) // write variable header
     {
         buffer.insert(buffer.end(), 4, '\0'); // skip var length (4)
         InsertToBuffer(buffer, &stats.MemberID);
@@ -345,6 +601,6 @@ void BP1Writer::WriteVariableCharacteristics(
 }
 
 } // end namespace format
-} // end namespace adios
+} // end namespace adios2
 
-#endif // ADIOS2_UTILITIES_FORMAT_BP1_BP1WRITER_TCC_
+#endif // ADIOS2_TOOLKIT_FORMAT_BP1_BP1WRITER_TCC_
diff --git a/source/adios2/toolkit/interop/adios1/ADIOS1Common.h b/source/adios2/toolkit/interop/adios1/ADIOS1Common.h
index 75f5c99fb568e7e5d3091c22e1709a2ba1c553cc..0473c896b10954f2d7d0bb4b2878184cba25d9a0 100644
--- a/source/adios2/toolkit/interop/adios1/ADIOS1Common.h
+++ b/source/adios2/toolkit/interop/adios1/ADIOS1Common.h
@@ -97,6 +97,6 @@ ADIOS2_FOREACH_TYPE_1ARG(declare_template_instantiation)
 #undef declare_template_instantiation
 
 } // end namespace interop
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_TOOLKIT_INTEROP_ADIOS1_ADIOS1COMMON_H_ */
diff --git a/source/adios2/toolkit/interop/adios1/ADIOS1Common.tcc b/source/adios2/toolkit/interop/adios1/ADIOS1Common.tcc
index b0fde3f8edfdf06348f4b4e937812c8615c19770..2233621a8907f6b44147e83177668c2df726a5ce 100644
--- a/source/adios2/toolkit/interop/adios1/ADIOS1Common.tcc
+++ b/source/adios2/toolkit/interop/adios1/ADIOS1Common.tcc
@@ -48,6 +48,11 @@ enum ADIOS_DATATYPES ADIOS1Common::GetADIOS1Type<char>() const {
     return adios_byte;
 }
 
+template <>
+enum ADIOS_DATATYPES ADIOS1Common::GetADIOS1Type<signed char>() const {
+    return adios_byte;
+}
+
 template <>
 enum ADIOS_DATATYPES ADIOS1Common::GetADIOS1Type<unsigned char>() const {
     return adios_unsigned_byte;
@@ -126,6 +131,6 @@ enum ADIOS_DATATYPES ADIOS1Common::GetADIOS1Type<cldouble>() const {
 }
 
 } // end namespace interop
-} // end namespace adios
+} // end namespace adios2
 
 #endif /* ADIOS2_TOOLKIT_INTEROP_ADIOS1_ADIOS1COMMON_TCC_ */
diff --git a/source/adios2/toolkit/interop/hdf5/HDF5Common.tcc b/source/adios2/toolkit/interop/hdf5/HDF5Common.tcc
index 54ca1bc764ed33d52f2662842115ed899ceb04e2..e5555692b833e76b334ce3fcb9e42b5d9c849570 100644
--- a/source/adios2/toolkit/interop/hdf5/HDF5Common.tcc
+++ b/source/adios2/toolkit/interop/hdf5/HDF5Common.tcc
@@ -103,6 +103,11 @@ hid_t HDF5Common::GetHDF5Type<char>()
     return H5T_NATIVE_CHAR;
 }
 template <>
+hid_t HDF5Common::GetHDF5Type<signed char>()
+{
+    return H5T_NATIVE_SCHAR;
+}
+template <>
 hid_t HDF5Common::GetHDF5Type<unsigned char>()
 {
     return H5T_NATIVE_UCHAR;
diff --git a/testing/adios2/bindings/C/CMakeLists.txt b/testing/adios2/bindings/C/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9897e1b0316505c8972a5a72fea5f24584c18a76
--- /dev/null
+++ b/testing/adios2/bindings/C/CMakeLists.txt
@@ -0,0 +1,16 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+
+if(ADIOS2_HAVE_MPI)
+  add_executable(TestBPWriteTypes_c TestBPWriteTypes.c)
+  target_include_directories(TestBPWriteTypes_c PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(TestBPWriteTypes_c ${MPI_C_LIBRARIES})
+else()
+  add_executable(TestBPWriteTypes_c TestBPWriteTypes_nompi.c)
+endif()
+
+target_link_libraries(TestBPWriteTypes_c adios2)
+
+add_test(NAME BPWrite_c COMMAND TestBPWriteTypes_c)
diff --git a/testing/adios2/bindings/C/SmallTestData_c.h b/testing/adios2/bindings/C/SmallTestData_c.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3aea0ce684d5038e5d536720a37e6b3320ceeae
--- /dev/null
+++ b/testing/adios2/bindings/C/SmallTestData_c.h
@@ -0,0 +1,35 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ */
+
+#ifndef TESTING_ADIOS2_BINDINGS_C_SMALLTESTDATA_C_H_
+#define TESTING_ADIOS2_BINDINGS_C_SMALLTESTDATA_C_H_
+
+size_t data_Nx = 10;
+char data_I8[10] = {0, 1, -2, 3, -4, 5, -6, 7, -8, 9};
+short data_I16[10] = {512, 513, -510, 515, -508, 517, -506, 519, -504, 521};
+int data_I32[10] = {131072, 131073,  -131070, 131075,  -131068,
+                    131077, -131066, 131079,  -131064, 131081};
+long int data_I64[10] = {8589934592,  8589934593, -8589934590, 8589934595,
+                         -8589934588, 8589934597, -8589934586, 8589934599,
+                         -8589934584, 8589934601};
+
+unsigned char data_U8[10] = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137};
+
+unsigned short data_U16[10] = {32768, 32769, 32770, 32771, 32772,
+                               32773, 32774, 32775, 32776, 32777};
+
+unsigned int data_U32[10] = {2147483648, 2147483649, 2147483650, 2147483651,
+                             2147483652, 2147483653, 2147483654, 2147483655,
+                             2147483656, 2147483657};
+
+unsigned long int data_U64[10] = {9223372036854775808UL, 9223372036854775809UL,
+                                  9223372036854775810UL, 9223372036854775811UL,
+                                  9223372036854775812UL, 9223372036854775813UL,
+                                  9223372036854775814UL, 9223372036854775815UL,
+                                  9223372036854775816UL, 9223372036854775817UL};
+float data_R32[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+double data_R64[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+#endif /* TESTING_ADIOS2_BINDINGS_C_SMALLTESTDATA_C_H_ */
diff --git a/testing/adios2/bindings/C/TestBPWriteTypes.c b/testing/adios2/bindings/C/TestBPWriteTypes.c
new file mode 100644
index 0000000000000000000000000000000000000000..55917aad8d47ab807ca24810fffeb230a2024466
--- /dev/null
+++ b/testing/adios2/bindings/C/TestBPWriteTypes.c
@@ -0,0 +1,102 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * TestBPWriteTypes.c
+ *
+ *  Created on: Aug 9, 2017
+ *      Author: wgodoy
+ */
+
+#include <mpi.h>
+
+#include <adios2_c.h>
+
+#include "SmallTestData_c.h"
+
+int main(int argc, char *argv[])
+{
+    MPI_Init(&argc, &argv);
+    int rank, size;
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+    MPI_Comm_size(MPI_COMM_WORLD, &size);
+
+    adios2_ADIOS *adiosH = adios2_init(MPI_COMM_WORLD, adios2_debug_mode_on);
+
+    // IO
+    adios2_IO *ioH = adios2_declare_io(adiosH, "CArrayTypes");
+    // Set engine parameters
+    adios2_set_engine(ioH, "BPFileWriter");
+    adios2_set_param(ioH, "ProfileUnits", "Microseconds");
+    adios2_set_param(ioH, "Threads", "1");
+
+    // Set transport and parameters
+    const unsigned int transportID = adios2_add_transport(ioH, "File");
+    adios2_set_transport_param(ioH, transportID, "library", "fstream");
+
+    // count dims are allocated in stack
+    size_t count[1];
+    count[0] = data_Nx;
+
+    // Define variables in ioH
+    {
+
+        adios2_define_variable(ioH, "varI8", adios2_type_char, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varI16", adios2_type_short, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varI32", adios2_type_int, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varI64", adios2_type_long_int, 1, NULL,
+                               NULL, count, adios2_constant_dims_true);
+
+        adios2_define_variable(ioH, "varU8", adios2_type_unsigned_char, 1, NULL,
+                               NULL, count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varU16", adios2_type_unsigned_short, 1,
+                               NULL, NULL, count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varU32", adios2_type_unsigned_int, 1, NULL,
+                               NULL, count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varU64", adios2_type_unsigned_long_int, 1,
+                               NULL, NULL, count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varR32", adios2_type_float, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varR64", adios2_type_double, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+    }
+    // get variables
+    adios2_Variable *varI8 = adios2_get_variable(ioH, "varI8");
+    adios2_Variable *varI16 = adios2_get_variable(ioH, "varI16");
+    adios2_Variable *varI32 = adios2_get_variable(ioH, "varI32");
+    adios2_Variable *varI64 = adios2_get_variable(ioH, "varI64");
+    adios2_Variable *varU8 = adios2_get_variable(ioH, "varU8");
+    adios2_Variable *varU16 = adios2_get_variable(ioH, "varU16");
+    adios2_Variable *varU32 = adios2_get_variable(ioH, "varU32");
+    adios2_Variable *varU64 = adios2_get_variable(ioH, "varU64");
+    adios2_Variable *varR32 = adios2_get_variable(ioH, "varR32");
+    adios2_Variable *varR64 = adios2_get_variable(ioH, "varR64");
+
+    // Open Engine handler, Write and Close
+    adios2_Engine *engineH =
+        adios2_open(ioH, "ctypes.bp", adios2_open_mode_write);
+
+    adios2_write(engineH, varI8, data_I8);
+    adios2_write(engineH, varI16, data_I16);
+    adios2_write(engineH, varI32, data_I32);
+    adios2_write(engineH, varI64, data_I64);
+
+    adios2_write(engineH, varU8, data_U8);
+    adios2_write(engineH, varU16, data_U16);
+    adios2_write(engineH, varU32, data_U32);
+    adios2_write(engineH, varU64, data_U64);
+
+    adios2_write(engineH, varR32, data_R32);
+    adios2_write(engineH, varR64, data_R64);
+
+    adios2_close(engineH);
+
+    // deallocate adiosH
+    adios2_finalize(adiosH);
+
+    MPI_Finalize();
+    return 0;
+}
diff --git a/testing/adios2/bindings/C/TestBPWriteTypes_nompi.c b/testing/adios2/bindings/C/TestBPWriteTypes_nompi.c
new file mode 100644
index 0000000000000000000000000000000000000000..247e5b4bdbd135bc56b1b99dd8d82db11b67b9a9
--- /dev/null
+++ b/testing/adios2/bindings/C/TestBPWriteTypes_nompi.c
@@ -0,0 +1,92 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * TestBPWriteTypes.c
+ *
+ *  Created on: Aug 9, 2017
+ *      Author: wgodoy
+ */
+
+#include <adios2_c.h>
+
+#include "SmallTestData_c.h"
+
+int main(int argc, char *argv[])
+{
+    adios2_ADIOS *adiosH = adios2_init_nompi(adios2_debug_mode_on);
+
+    // IO
+    adios2_IO *ioH = adios2_declare_io(adiosH, "CArrayTypes");
+    // Set engine parameters
+    adios2_set_engine(ioH, "BPFileWriter");
+    adios2_set_param(ioH, "ProfileUnits", "Microseconds");
+    adios2_set_param(ioH, "Threads", "1");
+
+    // Set transport and parameters
+    const unsigned int transportID = adios2_add_transport(ioH, "File");
+    adios2_set_transport_param(ioH, transportID, "library", "fstream");
+
+    // count dims are allocated in stack
+    size_t count[1];
+    count[0] = data_Nx;
+
+    // Define variables in ioH
+    {
+        adios2_define_variable(ioH, "varI8", adios2_type_char, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varI16", adios2_type_short, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varI32", adios2_type_int, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varI64", adios2_type_long_int, 1, NULL,
+                               NULL, count, adios2_constant_dims_true);
+
+        adios2_define_variable(ioH, "varU8", adios2_type_unsigned_char, 1, NULL,
+                               NULL, count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varU16", adios2_type_unsigned_short, 1,
+                               NULL, NULL, count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varU32", adios2_type_unsigned_int, 1, NULL,
+                               NULL, count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varU64", adios2_type_unsigned_long_int, 1,
+                               NULL, NULL, count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varR32", adios2_type_float, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+        adios2_define_variable(ioH, "varR64", adios2_type_double, 1, NULL, NULL,
+                               count, adios2_constant_dims_true);
+    }
+    // get variables
+    adios2_Variable *varI8 = adios2_get_variable(ioH, "varI8");
+    adios2_Variable *varI16 = adios2_get_variable(ioH, "varI16");
+    adios2_Variable *varI32 = adios2_get_variable(ioH, "varI32");
+    adios2_Variable *varI64 = adios2_get_variable(ioH, "varI64");
+    adios2_Variable *varU8 = adios2_get_variable(ioH, "varU8");
+    adios2_Variable *varU16 = adios2_get_variable(ioH, "varU16");
+    adios2_Variable *varU32 = adios2_get_variable(ioH, "varU32");
+    adios2_Variable *varU64 = adios2_get_variable(ioH, "varU64");
+    adios2_Variable *varR32 = adios2_get_variable(ioH, "varR32");
+    adios2_Variable *varR64 = adios2_get_variable(ioH, "varR64");
+
+    // Open Engine handler, Write and Close
+    adios2_Engine *engineH =
+        adios2_open(ioH, "ctypes.bp", adios2_open_mode_write);
+
+    adios2_write(engineH, varI8, data_I8);
+    adios2_write(engineH, varI16, data_I16);
+    adios2_write(engineH, varI32, data_I32);
+    adios2_write(engineH, varI64, data_I64);
+
+    adios2_write(engineH, varU8, data_U8);
+    adios2_write(engineH, varU16, data_U16);
+    adios2_write(engineH, varU32, data_U32);
+    adios2_write(engineH, varU64, data_U64);
+
+    adios2_write(engineH, varR32, data_R32);
+    adios2_write(engineH, varR64, data_R64);
+
+    adios2_close(engineH);
+
+    // deallocate adiosH
+    adios2_finalize(adiosH);
+    return 0;
+}
diff --git a/testing/adios2/bindings/CMakeLists.txt b/testing/adios2/bindings/CMakeLists.txt
index 8aa1ecd22b23c1ddd515657bde6f8ac8c6ec7af1..e82e710498992578abd98a4c98f42d17dafea7df 100644
--- a/testing/adios2/bindings/CMakeLists.txt
+++ b/testing/adios2/bindings/CMakeLists.txt
@@ -6,3 +6,9 @@
 if(ADIOS2_HAVE_Python)
   add_subdirectory(python)
 endif()
+
+if(ADIOS2_HAVE_Fortran)
+  add_subdirectory(fortran)
+endif()
+
+add_subdirectory(C)
\ No newline at end of file
diff --git a/testing/adios2/bindings/fortran/CMakeLists.txt b/testing/adios2/bindings/fortran/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..913dab1a72d93ee04e465d10092e40599f6e130c
--- /dev/null
+++ b/testing/adios2/bindings/fortran/CMakeLists.txt
@@ -0,0 +1,21 @@
+#------------------------------------------------------------------------------#
+# Distributed under the OSI-approved Apache License, Version 2.0.  See
+# accompanying file Copyright.txt for details.
+#------------------------------------------------------------------------------#
+
+add_executable(TestBPWriteTypes_f SmallTestData_mod.f90)
+
+if(ADIOS2_HAVE_MPI)
+  target_sources(TestBPWriteTypes_f PRIVATE TestBPWriteTypes.f90)
+  target_include_directories(TestBPWriteTypes_f 
+                                        PRIVATE ${MPI_Fortran_INCLUDE_PATH}
+                                                ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(TestBPWriteTypes_f PRIVATE ${MPI_Fortran_LIBRARIES}
+                                                   ${MPI_C_LIBRARIES})  
+else()
+  target_sources(TestBPWriteTypes_f PRIVATE TestBPWriteTypes_nompi.f90)
+endif()
+
+target_link_libraries(TestBPWriteTypes_f PRIVATE adios2_f)
+
+add_test(NAME BPWrite_f COMMAND TestBPWriteTypes_f)
diff --git a/testing/adios2/bindings/fortran/SmallTestData_mod.f90 b/testing/adios2/bindings/fortran/SmallTestData_mod.f90
new file mode 100644
index 0000000000000000000000000000000000000000..e562714f57daa70c98584a47287312efe0efe528
--- /dev/null
+++ b/testing/adios2/bindings/fortran/SmallTestData_mod.f90
@@ -0,0 +1,35 @@
+! Distributed under the OSI-approved Apache License, Version 2.0.  See
+! accompanying file Copyright.txt for details.
+!
+! SmallTestData.f90 : Fortran 90 arrays data for tests
+!
+!  Created on: Aug 9, 2017
+!      Author: William F Godoy godoywf@ornl.gov
+!
+
+
+module small_test_data
+    implicit none
+
+    integer(kind=1), parameter, dimension(10) :: data_I8 = &
+    & (/ 0, 1, -2, 3, -4, 5, -6, 7, -8, 9 /)
+
+    integer(kind=2), parameter, dimension(10) :: data_I16 = &
+    & (/ 512, 513, -510, 515, -508, 517, -506, 519, -504, 521 /)
+
+    integer(kind=4), parameter, dimension(10) :: data_I32 = &
+    & (/ 131072, 131073,  -131070, 131075,  -131068, 131077, -131066, 131079, &
+    &   -131064, 131081 /)
+
+!  Having trouble with width
+!    integer(kind=8), parameter, dimension(10) :: data_I64 = &
+!    & (/ 8589934592,  8589934593, -8589934590, 8589934595, -8589934588, &
+!    &    8589934597, -8589934586, 8589934599, -8589934584, 8589934601 /)
+
+    real, parameter, dimension(10) :: data_R32 = &
+    & (/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 /)
+
+    real(kind=8), parameter, dimension(10) :: data_R64 = &
+    & (/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 /)
+
+end module
diff --git a/testing/adios2/bindings/fortran/TestBPWriteTypes.f90 b/testing/adios2/bindings/fortran/TestBPWriteTypes.f90
new file mode 100644
index 0000000000000000000000000000000000000000..5340c1a59d28a5e86e3f7ffc8068f78eabc0c4ab
--- /dev/null
+++ b/testing/adios2/bindings/fortran/TestBPWriteTypes.f90
@@ -0,0 +1,50 @@
+program TestBPWriteTypes
+    use small_test_data
+    use mpi
+    use adios2
+    implicit none
+
+    integer, dimension(1) :: shape_dims, start_dims, count_dims
+    integer :: inx, irank, isize, ierr, i
+
+    integer(kind=8) :: adios, io, engine
+    integer(kind=8), dimension(6) :: variables
+
+    ! Launch MPI
+    call MPI_Init(ierr)
+    call MPI_Comm_rank(MPI_COMM_WORLD, irank, ierr)
+    call MPI_Comm_size(MPI_COMM_WORLD, isize, ierr)
+
+    ! Application variables
+    inx = 10
+
+    ! Variable dimensions
+    shape_dims(1) = isize * inx
+    start_dims(1) = irank * inx
+    count_dims(1) = inx
+
+    ! Create adios handler passing the communicator, debug mode and error flag
+    call adios2_init(adios, MPI_COMM_WORLD, adios2_debug_mode_on, ierr)
+
+    ! Declare an IO process configuration inside adios
+    call adios2_declare_io(io, adios, "bpIO", ierr)
+
+    ! Defines a variable to be written in bp format
+    call adios2_define_variable(variables(1), io, "var_I8", adios2_type_integer1, 1, &
+        & shape_dims, start_dims, count_dims, adios2_constant_dims_true, ierr)
+
+    ! Open myVector_f.bp in write mode, this launches an engine
+    call adios2_open(engine, io, "ftypes.bp", adios2_open_mode_write, ierr)
+
+    ! Write myArray contents to bp buffer, based on var1 metadata
+    call adios2_write(engine, variables(1), data_I8, ierr)
+
+    ! Closes engine1 and deallocates it, becomes unreachable
+    call adios2_close(engine, ierr)
+
+    ! Deallocates adios and calls its destructor
+    call adios2_finalize(adios, ierr)
+
+    call MPI_Finalize(ierr)
+
+end program TestBPWriteTypes
diff --git a/testing/adios2/bindings/fortran/TestBPWriteTypes_nompi.f90 b/testing/adios2/bindings/fortran/TestBPWriteTypes_nompi.f90
new file mode 100644
index 0000000000000000000000000000000000000000..3736a7ce9bd589107dc5b3aadbeb2c91c633e3c9
--- /dev/null
+++ b/testing/adios2/bindings/fortran/TestBPWriteTypes_nompi.f90
@@ -0,0 +1,43 @@
+program TestBPWriteTypes
+    use small_test_data
+    use adios2
+    implicit none
+
+    integer, dimension(1) :: shape_dims, start_dims, count_dims
+    integer :: inx, ierr, i
+
+    integer(kind=8) :: adios, io, engine
+    integer(kind=8), dimension(6) :: variables
+
+    ! Application variables
+    inx = 10
+
+    ! Variable dimensions
+    shape_dims(1) = inx
+    start_dims(1) = 0
+    count_dims(1) = inx
+
+    ! Create adios handler passing the communicator, debug mode and error flag
+    call adios2_init(adios, adios2_debug_mode_on, ierr)
+
+    ! Declare an IO process configuration inside adios
+    call adios2_declare_io(io, adios, "bpIO", ierr)
+
+    ! Defines a variable to be written in bp format
+    call adios2_define_variable(variables(1), io, "var_I8", &
+        & adios2_type_integer1, 1, shape_dims, start_dims, count_dims, &
+        & adios2_constant_dims_true, ierr)
+
+    ! Open myVector_f.bp in write mode, this launches an engine
+    call adios2_open(engine, io, "ftypes.bp", adios2_open_mode_write, ierr)
+
+    ! Write myArray contents to bp buffer, based on var1 metadata
+    call adios2_write(engine, variables(1), data_I8, ierr)
+
+    ! Closes engine1 and deallocates it, becomes unreachable
+    call adios2_close(engine, ierr)
+
+    ! Deallocates adios and calls its destructor
+    call adios2_finalize(adios, ierr)
+
+end program TestBPWriteTypes
diff --git a/testing/adios2/engine/SmallTestData.h b/testing/adios2/engine/SmallTestData.h
index 05de0729e8e7fdf1ba957942892a24a945722082..829758a3ff87edb4fedd5879d3b208599caa7ed3 100644
--- a/testing/adios2/engine/SmallTestData.h
+++ b/testing/adios2/engine/SmallTestData.h
@@ -5,26 +5,39 @@
 #ifndef TESTING_ADIOS2_ENGINE_SMALLTESTDATA_H_
 #define TESTING_ADIOS2_ENGINE_SMALLTESTDATA_H_
 
+#include <cstdint>
+
+#include <array>
+#include <limits>
+#include <string>
+
+#ifdef WIN32
+#define NOMINMAX
+#endif
+
 // Test data for each type.  Make sure our values exceed the range of the
 // previous size to make sure we all bytes for each element
 struct SmallTestData
 {
-    std::array<char, 10> I8 = {{0, 1, -2, 3, -4, 5, -6, 7, -8, 9}};
-    std::array<short, 10> I16 = {
+    std::string S1 = "Testing ADIOS2 String Attributes";
+    std::array<std::string, 3> S3 = {{"one", "two", "three"}};
+
+    std::array<int8_t, 10> I8 = {{0, 1, -2, 3, -4, 5, -6, 7, -8, 9}};
+    std::array<int16_t, 10> I16 = {
         {512, 513, -510, 515, -508, 517, -506, 519, -504, 521}};
-    std::array<int, 10> I32 = {{131072, 131073, -131070, 131075, -131068,
-                                131077, -131066, 131079, -131064, 131081}};
-    std::array<long int, 10> I64 = {
+    std::array<int32_t, 10> I32 = {{131072, 131073, -131070, 131075, -131068,
+                                    131077, -131066, 131079, -131064, 131081}};
+    std::array<int64_t, 10> I64 = {
         {8589934592, 8589934593, -8589934590, 8589934595, -8589934588,
          8589934597, -8589934586, 8589934599, -8589934584, 8589934601}};
-    std::array<unsigned char, 10> U8 = {
+    std::array<uint8_t, 10> U8 = {
         {128, 129, 130, 131, 132, 133, 134, 135, 136, 137}};
-    std::array<unsigned short, 10> U16 = {
+    std::array<uint16_t, 10> U16 = {
         {32768, 32769, 32770, 32771, 32772, 32773, 32774, 32775, 32776, 32777}};
-    std::array<unsigned int, 10> U32 = {
+    std::array<uint32_t, 10> U32 = {
         {2147483648, 2147483649, 2147483650, 2147483651, 2147483652, 2147483653,
          2147483654, 2147483655, 2147483656, 2147483657}};
-    std::array<unsigned long int, 10> U64 = {
+    std::array<uint64_t, 10> U64 = {
         {9223372036854775808UL, 9223372036854775809UL, 9223372036854775810UL,
          9223372036854775811UL, 9223372036854775812UL, 9223372036854775813UL,
          9223372036854775814UL, 9223372036854775815UL, 9223372036854775816UL,
@@ -33,4 +46,22 @@ struct SmallTestData
     std::array<double, 10> R64 = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};
 };
 
+SmallTestData generateNewSmallTestData(SmallTestData in, int step, int rank,
+                                       int size)
+{
+    int j = rank + 1 + step * size;
+    std::for_each(in.I8.begin(), in.I8.end(), [&](int8_t &v) { v += j; });
+    std::for_each(in.I16.begin(), in.I16.end(), [&](int16_t &v) { v += j; });
+    std::for_each(in.I32.begin(), in.I32.end(), [&](int32_t &v) { v += j; });
+    std::for_each(in.I64.begin(), in.I64.end(), [&](int64_t &v) { v += j; });
+    std::for_each(in.U8.begin(), in.U8.end(), [&](uint8_t &v) { v += j; });
+    std::for_each(in.U16.begin(), in.U16.end(), [&](uint16_t &v) { v += j; });
+    std::for_each(in.U32.begin(), in.U32.end(), [&](uint32_t &v) { v += j; });
+    std::for_each(in.U64.begin(), in.U64.end(), [&](uint64_t &v) { v += j; });
+    std::for_each(in.R32.begin(), in.R32.end(), [&](float &v) { v += j; });
+    std::for_each(in.R64.begin(), in.R64.end(), [&](double &v) { v += j; });
+
+    return in;
+}
+
 #endif // TESTING_ADIOS2_ENGINE_SMALLTESTDATA_H_
diff --git a/testing/adios2/engine/adios1/CMakeLists.txt b/testing/adios2/engine/adios1/CMakeLists.txt
index 259a2aed065852814bf0e4d82f88abb14cedd28c..ace06e557d1705e87449bc73972562005a035a1c 100644
--- a/testing/adios2/engine/adios1/CMakeLists.txt
+++ b/testing/adios2/engine/adios1/CMakeLists.txt
@@ -3,12 +3,15 @@
 # accompanying file Copyright.txt for details.
 #------------------------------------------------------------------------------#
 
-# MPI versions of the test are not properly implemented at the moment
-if(NOT ADIOS2_HAVE_MPI)
-  find_package(ADIOS1 COMPONENTS sequential REQUIRED)
-
-  add_executable(TestADIOS1WriteRead TestADIOS1WriteRead.cpp)
-  target_link_libraries(TestADIOS1WriteRead adios2 gtest adios1::adios)
-
-  gtest_add_tests(TARGET TestADIOS1WriteRead)
+add_executable(TestADIOS1WriteRead TestADIOS1WriteRead.cpp)
+target_link_libraries(TestADIOS1WriteRead adios2 gtest adios1::adios)
+if(ADIOS2_HAVE_MPI)
+  target_include_directories(TestADIOS1WriteRead PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(TestADIOS1WriteRead ${MPI_C_LIBRARIES})
+  set(extra_test_args
+    EXEC_WRAPPER
+      ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS}
+  )
 endif()
+
+gtest_add_tests(TARGET TestADIOS1WriteRead ${extra_test_args})
diff --git a/testing/adios2/engine/adios1/TestADIOS1WriteRead.cpp b/testing/adios2/engine/adios1/TestADIOS1WriteRead.cpp
index 19f338e4bd7138527f4a565d78721b9071a02408..99fc439bccb5667a190bba1d7553518f34418b71 100644
--- a/testing/adios2/engine/adios1/TestADIOS1WriteRead.cpp
+++ b/testing/adios2/engine/adios1/TestADIOS1WriteRead.cpp
@@ -16,6 +16,10 @@
 
 #include "../SmallTestData.h"
 
+#ifdef ADIOS2_HAVE_MPI
+#include "mpi.h"
+#endif
+
 class ADIOS1WriteReadTest : public ::testing::Test
 {
 public:
@@ -31,71 +35,116 @@ public:
 // ADIOS2 write, native ADIOS1 read
 TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read1D8)
 {
+    // Each process would write a 1x8 array and all processes would
+    // form a mpiSize * Nx 1D array
     std::string fname = "ADIOS2ADIOS1WriteADIOS1Read1D8.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 8;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
-        adios_init_noxml(MPI_COMM_WORLD);
-
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 1D variables (NumOfProcesses * Nx)
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{8});
+            adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank)};
+            adios2::Dims count{static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
             auto &var_u8 =
-                io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{8});
-            auto &var_u16 = io.DefineVariable<unsigned short>("u16", {}, {},
-                                                              adios2::Dims{8});
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
             auto &var_u32 =
-                io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{8});
-            auto &var_u64 = io.DefineVariable<unsigned long>("u64", {}, {},
-                                                             adios2::Dims{8});
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the ADIOS 1 Engine
         io.SetEngine("ADIOS1Writer");
-        io.AddTransport("File");
+
+#ifdef ADIOS2_HAVE_MPI
+        io.AddTransport("file", {{"library", "MPI"}});
+#else
+        io.AddTransport("file");
+#endif
 
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 1D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel({mpiRank * Nx}, {Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -107,13 +156,6 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read1D8)
         adios_finalize(0);
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
         adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
                                "verbose=3");
@@ -127,62 +169,85 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read1D8)
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 1);
-        ASSERT_EQ(var_i8->dims[0], 8);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 1);
-        ASSERT_EQ(var_i16->dims[0], 8);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 1);
-        ASSERT_EQ(var_i32->dims[0], 8);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 1);
-        ASSERT_EQ(var_i64->dims[0], 8);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 1);
-        ASSERT_EQ(var_u8->dims[0], 8);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 1);
-        ASSERT_EQ(var_u16->dims[0], 8);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 1);
-        ASSERT_EQ(var_u32->dims[0], 8);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 1);
-        ASSERT_EQ(var_u64->dims[0], 8);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 1);
-        ASSERT_EQ(var_r32->dims[0], 8);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 1);
-        ASSERT_EQ(var_r64->dims[0], 8);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[1] = {0};
-        uint64_t count[1] = {8};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], mpiSize * Nx);
+
+        std::array<int8_t, Nx> I8;
+        std::array<int16_t, Nx> I16;
+        std::array<int32_t, Nx> I32;
+        std::array<int64_t, Nx> I64;
+        std::array<uint8_t, Nx> U8;
+        std::array<uint16_t, Nx> U16;
+        std::array<uint32_t, Nx> U32;
+        std::array<uint64_t, Nx> U64;
+        std::array<float, Nx> R32;
+        std::array<double, Nx> R64;
+
+        uint64_t start[1] = {mpiRank * Nx};
+        uint64_t count[1] = {Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(1, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -197,22 +262,22 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read1D8)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -260,71 +325,123 @@ TEST_F(ADIOS1WriteReadTest, DISABLED_ADIOS1WriteADIOS2ADIOS1Read1D8)
 // ADIOS2 write, native ADIOS1 read
 TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read2D2x4)
 {
+    // Each process would write a 2x4 array and all processes would
+    // form a 2D 2 * (numberOfProcess*Nx) matrix where Nx is 4 here
     std::string fname = "ADIOS2ADIOS1WriteADIOS1Read2D2x4Test.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 4;
+
+    // Number of rows
+    const std::size_t Ny = 2;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
-        adios_init_noxml(MPI_COMM_WORLD);
-
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (Ny * (NumOfProcesses * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{2, 4});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{2, 4});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{2, 4});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the ADIOS 1 Engine
         io.SetEngine("ADIOS1Writer");
+
+#ifdef ADIOS2_HAVE_MPI
+        io.AddTransport("file", {{"library", "MPI"}});
+#else
         io.AddTransport("file");
+#endif
 
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -336,13 +453,6 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read2D2x4)
         adios_finalize(0);
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
         adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
                                "verbose=3");
@@ -356,72 +466,98 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read2D2x4)
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 2);
-        ASSERT_EQ(var_i8->dims[0], 2);
-        ASSERT_EQ(var_i8->dims[1], 4);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], Ny);
+        ASSERT_EQ(var_i8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 2);
-        ASSERT_EQ(var_i16->dims[0], 2);
-        ASSERT_EQ(var_i16->dims[1], 4);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], Ny);
+        ASSERT_EQ(var_i16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 2);
-        ASSERT_EQ(var_i32->dims[0], 2);
-        ASSERT_EQ(var_i32->dims[1], 4);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], Ny);
+        ASSERT_EQ(var_i32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 2);
-        ASSERT_EQ(var_i64->dims[0], 2);
-        ASSERT_EQ(var_i64->dims[1], 4);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], Ny);
+        ASSERT_EQ(var_i64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 2);
-        ASSERT_EQ(var_u8->dims[0], 2);
-        ASSERT_EQ(var_u8->dims[1], 4);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], Ny);
+        ASSERT_EQ(var_u8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 2);
-        ASSERT_EQ(var_u16->dims[0], 2);
-        ASSERT_EQ(var_u16->dims[1], 4);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], Ny);
+        ASSERT_EQ(var_u16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 2);
-        ASSERT_EQ(var_u32->dims[0], 2);
-        ASSERT_EQ(var_u32->dims[1], 4);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], Ny);
+        ASSERT_EQ(var_u32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 2);
-        ASSERT_EQ(var_u64->dims[0], 2);
-        ASSERT_EQ(var_u64->dims[1], 4);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], Ny);
+        ASSERT_EQ(var_u64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 2);
-        ASSERT_EQ(var_r32->dims[0], 2);
-        ASSERT_EQ(var_r32->dims[1], 4);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], Ny);
+        ASSERT_EQ(var_r32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 2);
-        ASSERT_EQ(var_r64->dims[0], 2);
-        ASSERT_EQ(var_r64->dims[1], 4);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[2] = {0, 0};
-        uint64_t count[2] = {2, 4};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], Ny);
+        ASSERT_EQ(var_r64->dims[1], mpiSize * Nx);
+
+        // If the size of the array is smaller than the data
+        // the result is weird... double and uint64_t would get completely
+        // garbage data
+        std::array<int8_t, Nx * Ny> I8;
+        std::array<int16_t, Nx * Ny> I16;
+        std::array<int32_t, Nx * Ny> I32;
+        std::array<int64_t, Nx * Ny> I64;
+        std::array<uint8_t, Nx * Ny> U8;
+        std::array<uint16_t, Nx * Ny> U16;
+        std::array<uint32_t, Nx * Ny> U32;
+        std::array<uint64_t, Nx * Ny> U64;
+        std::array<float, Nx * Ny> R32;
+        std::array<double, Nx * Ny> R64;
+
+        uint64_t start[2] = {0, static_cast<uint64_t>(mpiRank * Nx)};
+        uint64_t count[2] = {Ny, Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(2, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -436,22 +572,22 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read2D2x4)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -496,74 +632,125 @@ TEST_F(ADIOS1WriteReadTest, DISABLED_ADIOS1WriteADIOS2ADIOS1Read2D2x4)
 // 2D 4x2 test data
 //******************************************************************************
 
-// ADIOS2 write, native ADIOS1 read
-TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read2D4x2)
+// ADIOS2 write using ADIOS1 Writer, native ADIOS1 read
+TEST_F(ADIOS1WriteReadTest, _ADIOS2ADIOS1WriteADIOS1Read2D4x2)
 {
+    // Each process would write a 4x2 array and all processes would
+    // form a 2D 4 * (NumberOfProcess * Nx) matrix where Nx is 2 here
     std::string fname = "ADIOS2ADIOS1WriteADIOS1Read2D4x2Test.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 2;
+    // Number of cols
+    const std::size_t Ny = 4;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
-        adios_init_noxml(MPI_COMM_WORLD);
-
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (4 * (NumberOfProcess * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{4, 2});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(mpiSize * Nx)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{4, 2});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{4, 2});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the ADIOS 1 Engine
         io.SetEngine("ADIOS1Writer");
+
+#ifdef ADIOS2_HAVE_MPI
+        io.AddTransport("file", {{"library", "MPI"}});
+#else
         io.AddTransport("file");
+#endif
 
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -575,13 +762,6 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read2D4x2)
         adios_finalize(0);
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
         adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
                                "verbose=3");
@@ -595,72 +775,98 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read2D4x2)
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 2);
-        ASSERT_EQ(var_i8->dims[0], 4);
-        ASSERT_EQ(var_i8->dims[1], 2);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], Ny);
+        ASSERT_EQ(var_i8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 2);
-        ASSERT_EQ(var_i16->dims[0], 4);
-        ASSERT_EQ(var_i16->dims[1], 2);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], Ny);
+        ASSERT_EQ(var_i16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 2);
-        ASSERT_EQ(var_i32->dims[0], 4);
-        ASSERT_EQ(var_i32->dims[1], 2);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], Ny);
+        ASSERT_EQ(var_i32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 2);
-        ASSERT_EQ(var_i64->dims[0], 4);
-        ASSERT_EQ(var_i64->dims[1], 2);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], Ny);
+        ASSERT_EQ(var_i64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 2);
-        ASSERT_EQ(var_u8->dims[0], 4);
-        ASSERT_EQ(var_u8->dims[1], 2);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], Ny);
+        ASSERT_EQ(var_u8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 2);
-        ASSERT_EQ(var_u16->dims[0], 4);
-        ASSERT_EQ(var_u16->dims[1], 2);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], Ny);
+        ASSERT_EQ(var_u16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 2);
-        ASSERT_EQ(var_u32->dims[0], 4);
-        ASSERT_EQ(var_u32->dims[1], 2);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], Ny);
+        ASSERT_EQ(var_u32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 2);
-        ASSERT_EQ(var_u64->dims[0], 4);
-        ASSERT_EQ(var_u64->dims[1], 2);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], Ny);
+        ASSERT_EQ(var_u64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 2);
-        ASSERT_EQ(var_r32->dims[0], 4);
-        ASSERT_EQ(var_r32->dims[1], 2);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], Ny);
+        ASSERT_EQ(var_r32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 2);
-        ASSERT_EQ(var_r64->dims[0], 4);
-        ASSERT_EQ(var_r64->dims[1], 2);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[2] = {0, 0};
-        uint64_t count[2] = {4, 2};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], Ny);
+        ASSERT_EQ(var_r64->dims[1], mpiSize * Nx);
+
+        // If the size of the array is smaller than the data
+        // the result is weird... double and uint64_t would get completely
+        // garbage data
+        std::array<int8_t, Nx * Ny> I8;
+        std::array<int16_t, Nx * Ny> I16;
+        std::array<int32_t, Nx * Ny> I32;
+        std::array<int64_t, Nx * Ny> I64;
+        std::array<uint8_t, Nx * Ny> U8;
+        std::array<uint16_t, Nx * Ny> U16;
+        std::array<uint32_t, Nx * Ny> U32;
+        std::array<uint64_t, Nx * Ny> U64;
+        std::array<float, Nx * Ny> R32;
+        std::array<double, Nx * Ny> R64;
+
+        uint64_t start[2] = {0, static_cast<uint64_t>(mpiRank * Nx)};
+        uint64_t count[2] = {Ny, Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(2, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -675,22 +881,22 @@ TEST_F(ADIOS1WriteReadTest, ADIOS2ADIOS1WriteADIOS1Read2D4x2)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -739,6 +945,7 @@ int main(int argc, char **argv)
 {
 #ifdef ADIOS2_HAVE_MPI
     MPI_Init(nullptr, nullptr);
+    adios_init_noxml(MPI_COMM_WORLD);
 #endif
 
     ::testing::InitGoogleTest(&argc, argv);
diff --git a/testing/adios2/engine/bp/CMakeLists.txt b/testing/adios2/engine/bp/CMakeLists.txt
index 3153fd2ab8e638c45a2c10bf63ae5aaafcb21dc9..fb032ba79213616b4dd555c61952bfdbe812eb84 100644
--- a/testing/adios2/engine/bp/CMakeLists.txt
+++ b/testing/adios2/engine/bp/CMakeLists.txt
@@ -4,28 +4,50 @@
 #------------------------------------------------------------------------------#
 
 # MPI versions of the test are not properly implemented at the moment
-if(NOT ADIOS2_HAVE_MPI)
-  
-  if(ADIOS2_HAVE_ADIOS1)
-    find_package(ADIOS1 COMPONENTS sequential REQUIRED)
-
-    add_executable(TestBPWriteRead TestBPWriteRead.cpp)
-    target_link_libraries(TestBPWriteRead adios2 gtest adios1::adios)
-  
-    add_executable(TestBPWriteReadstdio TestBPWriteReadstdio.cpp)
-    target_link_libraries(TestBPWriteReadstdio adios2 gtest adios1::adios)
-
-    add_executable(TestBPWriteReadfstream TestBPWriteReadfstream.cpp)
-    target_link_libraries(TestBPWriteReadfstream adios2 gtest adios1::adios)
-    
-    gtest_add_tests(TARGET TestBPWriteRead)
-    gtest_add_tests(TARGET TestBPWriteReadstdio)
-    gtest_add_tests(TARGET TestBPWriteReadfstream)
-    
-  endif()
-    
+
+if (ADIOS2_HAVE_ADIOS1)
+  add_executable(TestBPWriteRead TestBPWriteRead.cpp)
+  target_link_libraries(TestBPWriteRead adios2 gtest adios1::adios)
+
+  add_executable(TestBPWriteReadAttributes TestBPWriteReadAttributes.cpp)
+  target_link_libraries(TestBPWriteReadAttributes adios2 gtest adios1::adios)
+
+  add_executable(TestBPWriteReadstdio TestBPWriteReadstdio.cpp)
+  target_link_libraries(TestBPWriteReadstdio adios2 gtest adios1::adios)
+
+  add_executable(TestBPWriteReadfstream TestBPWriteReadfstream.cpp)
+  target_link_libraries(TestBPWriteReadfstream adios2 gtest adios1::adios)
+
   add_executable(TestBPWriteProfilingJSON TestBPWriteProfilingJSON.cpp)
-  target_link_libraries(TestBPWriteProfilingJSON adios2 gtest gtest_main NLohmannJson)
-  
-  gtest_add_tests(TARGET TestBPWriteProfilingJSON)
+  target_link_libraries(TestBPWriteProfilingJSON
+    adios2 gtest NLohmannJson
+  )
+
+  if(ADIOS2_HAVE_MPI)
+    target_include_directories(TestBPWriteRead PRIVATE ${MPI_C_INCLUDE_PATH})
+    target_link_libraries(TestBPWriteRead ${MPI_C_LIBRARIES})
+
+    target_include_directories(TestBPWriteReadAttributes PRIVATE ${MPI_C_INCLUDE_PATH})
+    target_link_libraries(TestBPWriteReadAttributes ${MPI_C_LIBRARIES})
+
+    target_include_directories(TestBPWriteReadstdio PRIVATE ${MPI_C_INCLUDE_PATH})
+    target_link_libraries(TestBPWriteReadstdio ${MPI_C_LIBRARIES})
+
+    target_include_directories(TestBPWriteReadfstream PRIVATE ${MPI_C_INCLUDE_PATH})
+    target_link_libraries(TestBPWriteReadfstream ${MPI_C_LIBRARIES})
+
+    target_include_directories(TestBPWriteProfilingJSON PRIVATE ${MPI_C_INCLUDE_PATH})
+    target_link_libraries(TestBPWriteProfilingJSON ${MPI_C_LIBRARIES})
+
+    set(extra_test_args
+      EXEC_WRAPPER
+        ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS}
+    )
+  endif()
+
+  gtest_add_tests(TARGET TestBPWriteRead ${extra_test_args})
+  gtest_add_tests(TARGET TestBPWriteReadAttributes ${extra_test_args})
+  gtest_add_tests(TARGET TestBPWriteReadstdio ${extra_test_args})
+  gtest_add_tests(TARGET TestBPWriteReadfstream ${extra_test_args})
+  gtest_add_tests(TARGET TestBPWriteProfilingJSON ${extra_test_args})
 endif()
diff --git a/testing/adios2/engine/bp/TestBPWriteProfilingJSON.cpp b/testing/adios2/engine/bp/TestBPWriteProfilingJSON.cpp
index 501062dbc2f558e97e7ab1d94dec696289b57cac..5fbf1723c1d83603dd9b365ef7b97afb6133d782 100644
--- a/testing/adios2/engine/bp/TestBPWriteProfilingJSON.cpp
+++ b/testing/adios2/engine/bp/TestBPWriteProfilingJSON.cpp
@@ -38,77 +38,118 @@ public:
 //******************************************************************************
 
 // ADIOS2 write, native ADIOS1 read
-TEST_F(BPWriteProfilingJSONTest, ADIOS2BPWriteProfilingJSON)
+TEST_F(BPWriteProfilingJSONTest, DISABLED_ADIOS2BPWriteProfilingJSON)
 {
-    std::string fname = "ADIOS2BPWriteProfilingJSON.bp";
+    // Use a relative path + file name to test path in file name capability
+    std::string fname;
+    fname = "foo/ADIOS2BPWriteProfilingJSON.bp";
+
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 8;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
 
     // Write test data and profiling.json using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 1D variables (NumOfProcesses * Nx)
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{8});
+            adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank)};
+            adios2::Dims count{static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
             auto &var_u8 =
-                io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{8});
-            auto &var_u16 = io.DefineVariable<unsigned short>("u16", {}, {},
-                                                              adios2::Dims{8});
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
             auto &var_u32 =
-                io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{8});
-            auto &var_u64 = io.DefineVariable<unsigned long>("u64", {}, {},
-                                                             adios2::Dims{8});
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
         io.SetEngine("BPFileWriter");
         io.SetParameters({{"Threads", "2"}});
-        io.AddTransport("File", {{"Library", "POSIX"}});
+        io.AddTransport("file", {{"Library", "POSIX"}});
 
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 1D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel({mpiRank * Nx}, {Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
         }
-
         // Close the file
         engine->Close();
     }
@@ -116,25 +157,43 @@ TEST_F(BPWriteProfilingJSONTest, ADIOS2BPWriteProfilingJSON)
     // open json file, parse it to a json structure, and verify a few things
     {
         std::ifstream profilingJSONFile(fname + ".dir/profiling.json");
-        std::stringstream buffer;
-        buffer << profilingJSONFile.rdbuf();
-
-        const json profilingJSON = json::parse(buffer);
+        const json profilingJSON = json::parse(profilingJSONFile);
 
         // check rank is zero
-        const int rank = profilingJSON[0].value("rank", -1);
-        ASSERT_EQ(rank, 0);
+        const int rank = profilingJSON[mpiRank].value("rank", -1);
+        ASSERT_EQ(rank, mpiRank);
 
         // check threads
-        const int threads = profilingJSON[0].value("threads", 0);
+        const int threads = profilingJSON[mpiRank].value("threads", 0);
         ASSERT_EQ(threads, 2);
 
         // check bytes
-        const unsigned long int bytes = profilingJSON[0].value("bytes", 0UL);
+        const unsigned long int bytes =
+            profilingJSON[mpiRank].value("bytes", 0UL);
         ASSERT_EQ(bytes, 6536);
 
         const auto transportType =
-            profilingJSON[0]["transport_0"].value("type", "0");
+            profilingJSON[mpiRank]["transport_0"].value("type", "0");
         ASSERT_EQ(transportType, "File_POSIX");
     }
 }
+
+//******************************************************************************
+// main
+//******************************************************************************
+
+int main(int argc, char **argv)
+{
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Init(nullptr, nullptr);
+#endif
+
+    ::testing::InitGoogleTest(&argc, argv);
+    int result = RUN_ALL_TESTS();
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Finalize();
+#endif
+
+    return result;
+}
diff --git a/testing/adios2/engine/bp/TestBPWriteRead.cpp b/testing/adios2/engine/bp/TestBPWriteRead.cpp
index 3857839916b6267b266a1dcfbb805d71915d0439..ed5ad92aaf1cea03057ccf7bc8ddd2691e69b0df 100644
--- a/testing/adios2/engine/bp/TestBPWriteRead.cpp
+++ b/testing/adios2/engine/bp/TestBPWriteRead.cpp
@@ -27,72 +27,121 @@ public:
 // 1D 1x8 test data
 //******************************************************************************
 
-// ADIOS2 write, native ADIOS1 read
+// ADIOS2 BP write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8)
 {
+    // Each process would write a 1x8 array and all processes would
+    // form a mpiSize * Nx 1D array
     std::string fname = "ADIOS2BPWriteADIOS1Read1D8.bp";
 
-    // Write test data using ADIOS2
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 8;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
+    // Write test data using BP
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 1D variables (NumOfProcesses * Nx)
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{8});
+            adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank)};
+            adios2::Dims count{static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
             auto &var_u8 =
-                io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{8});
-            auto &var_u16 = io.DefineVariable<unsigned short>("u16", {}, {},
-                                                              adios2::Dims{8});
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
             auto &var_u32 =
-                io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{8});
-            auto &var_u64 = io.DefineVariable<unsigned long>("u64", {}, {},
-                                                             adios2::Dims{8});
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
         io.SetEngine("BPFileWriter");
-        io.AddTransport("File");
 
+        io.AddTransport("file");
+
+        // QUESTION: It seems that BPFilterWriter cannot overwrite existing
+        // files
+        // Ex. if you tune Nx and NSteps, the test would fail. But if you clear
+        // the cache in
+        // ${adios2Build}/testing/adios2/engine/bp/ADIOS2BPWriteADIOS1Read1D8.bp.dir,
+        // then it works
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 1D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel({mpiRank * Nx}, {Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -102,83 +151,105 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 1);
-        ASSERT_EQ(var_i8->dims[0], 8);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 1);
-        ASSERT_EQ(var_i16->dims[0], 8);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 1);
-        ASSERT_EQ(var_i32->dims[0], 8);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 1);
-        ASSERT_EQ(var_i64->dims[0], 8);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 1);
-        ASSERT_EQ(var_u8->dims[0], 8);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 1);
-        ASSERT_EQ(var_u16->dims[0], 8);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 1);
-        ASSERT_EQ(var_u32->dims[0], 8);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 1);
-        ASSERT_EQ(var_u64->dims[0], 8);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 1);
-        ASSERT_EQ(var_r32->dims[0], 8);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 1);
-        ASSERT_EQ(var_r64->dims[0], 8);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[1] = {0};
-        uint64_t count[1] = {8};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], mpiSize * Nx);
+
+        std::array<int8_t, Nx> I8;
+        std::array<int16_t, Nx> I16;
+        std::array<int32_t, Nx> I32;
+        std::array<int64_t, Nx> I64;
+        std::array<uint8_t, Nx> U8;
+        std::array<uint16_t, Nx> U16;
+        std::array<uint32_t, Nx> U32;
+        std::array<uint64_t, Nx> U64;
+        std::array<float, Nx> R32;
+        std::array<double, Nx> R64;
+
+        uint64_t start[1] = {mpiRank * Nx};
+        uint64_t count[1] = {Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(1, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -193,22 +264,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -228,6 +299,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
@@ -243,38 +316,66 @@ TEST_F(BPWriteReadTest, DISABLED_ADIOS2BPWriteADIOS2BPRead1D8)
 // 2D 2x4 test data
 //******************************************************************************
 
-// ADIOS2 write, native ADIOS1 read
+// ADIOS2 BP write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4)
 {
+    // Each process would write a 2x4 array and all processes would
+    // form a 2D 2 * (numberOfProcess*Nx) matrix where Nx is 4 here
     std::string fname = "ADIOS2BPWriteADIOS1Read2D2x4Test.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 4;
+
+    // Number of rows
+    const std::size_t Ny = 2;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (Ny * (NumOfProcesses * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{2, 4});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{2, 4});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{2, 4});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
@@ -284,31 +385,52 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4)
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -318,93 +440,118 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 2);
-        ASSERT_EQ(var_i8->dims[0], 2);
-        ASSERT_EQ(var_i8->dims[1], 4);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], Ny);
+        ASSERT_EQ(var_i8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 2);
-        ASSERT_EQ(var_i16->dims[0], 2);
-        ASSERT_EQ(var_i16->dims[1], 4);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], Ny);
+        ASSERT_EQ(var_i16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 2);
-        ASSERT_EQ(var_i32->dims[0], 2);
-        ASSERT_EQ(var_i32->dims[1], 4);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], Ny);
+        ASSERT_EQ(var_i32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 2);
-        ASSERT_EQ(var_i64->dims[0], 2);
-        ASSERT_EQ(var_i64->dims[1], 4);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], Ny);
+        ASSERT_EQ(var_i64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 2);
-        ASSERT_EQ(var_u8->dims[0], 2);
-        ASSERT_EQ(var_u8->dims[1], 4);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], Ny);
+        ASSERT_EQ(var_u8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 2);
-        ASSERT_EQ(var_u16->dims[0], 2);
-        ASSERT_EQ(var_u16->dims[1], 4);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], Ny);
+        ASSERT_EQ(var_u16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 2);
-        ASSERT_EQ(var_u32->dims[0], 2);
-        ASSERT_EQ(var_u32->dims[1], 4);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], Ny);
+        ASSERT_EQ(var_u32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 2);
-        ASSERT_EQ(var_u64->dims[0], 2);
-        ASSERT_EQ(var_u64->dims[1], 4);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], Ny);
+        ASSERT_EQ(var_u64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 2);
-        ASSERT_EQ(var_r32->dims[0], 2);
-        ASSERT_EQ(var_r32->dims[1], 4);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], Ny);
+        ASSERT_EQ(var_r32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 2);
-        ASSERT_EQ(var_r64->dims[0], 2);
-        ASSERT_EQ(var_r64->dims[1], 4);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[2] = {0, 0};
-        uint64_t count[2] = {2, 4};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], Ny);
+        ASSERT_EQ(var_r64->dims[1], mpiSize * Nx);
+
+        // If the size of the array is smaller than the data
+        // the result is weird... double and uint64_t would get completely
+        // garbage data
+        std::array<int8_t, Nx * Ny> I8;
+        std::array<int16_t, Nx * Ny> I16;
+        std::array<int32_t, Nx * Ny> I32;
+        std::array<int64_t, Nx * Ny> I64;
+        std::array<uint8_t, Nx * Ny> U8;
+        std::array<uint16_t, Nx * Ny> U16;
+        std::array<uint32_t, Nx * Ny> U32;
+        std::array<uint64_t, Nx * Ny> U64;
+        std::array<float, Nx * Ny> R32;
+        std::array<double, Nx * Ny> R64;
+
+        uint64_t start[2] = {0, mpiRank * Nx};
+        uint64_t count[2] = {Ny, Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(2, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -419,22 +566,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -454,6 +601,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
@@ -472,69 +621,118 @@ TEST_F(BPWriteReadTest, DISABLED_ADIOS2BPWriteADIOS2BPRead2D2x4)
 // ADIOS2 write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2)
 {
+    // Each process would write a 4x2 array and all processes would
+    // form a 2D 4 * (NumberOfProcess * Nx) matrix where Nx is 2 here
     std::string fname = "ADIOS2BPWriteADIOS1Read2D4x2Test.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 2;
+    // Number of cols
+    const std::size_t Ny = 4;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (4 * (NumberOfProcess * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{4, 2});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(mpiSize * Nx)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{4, 2});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{4, 2});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
         io.SetEngine("BPFileWriter");
+
         io.AddTransport("file");
 
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -544,93 +742,118 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 2);
-        ASSERT_EQ(var_i8->dims[0], 4);
-        ASSERT_EQ(var_i8->dims[1], 2);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], Ny);
+        ASSERT_EQ(var_i8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 2);
-        ASSERT_EQ(var_i16->dims[0], 4);
-        ASSERT_EQ(var_i16->dims[1], 2);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], Ny);
+        ASSERT_EQ(var_i16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 2);
-        ASSERT_EQ(var_i32->dims[0], 4);
-        ASSERT_EQ(var_i32->dims[1], 2);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], Ny);
+        ASSERT_EQ(var_i32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 2);
-        ASSERT_EQ(var_i64->dims[0], 4);
-        ASSERT_EQ(var_i64->dims[1], 2);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], Ny);
+        ASSERT_EQ(var_i64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 2);
-        ASSERT_EQ(var_u8->dims[0], 4);
-        ASSERT_EQ(var_u8->dims[1], 2);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], Ny);
+        ASSERT_EQ(var_u8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 2);
-        ASSERT_EQ(var_u16->dims[0], 4);
-        ASSERT_EQ(var_u16->dims[1], 2);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], Ny);
+        ASSERT_EQ(var_u16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 2);
-        ASSERT_EQ(var_u32->dims[0], 4);
-        ASSERT_EQ(var_u32->dims[1], 2);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], Ny);
+        ASSERT_EQ(var_u32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 2);
-        ASSERT_EQ(var_u64->dims[0], 4);
-        ASSERT_EQ(var_u64->dims[1], 2);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], Ny);
+        ASSERT_EQ(var_u64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 2);
-        ASSERT_EQ(var_r32->dims[0], 4);
-        ASSERT_EQ(var_r32->dims[1], 2);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], Ny);
+        ASSERT_EQ(var_r32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 2);
-        ASSERT_EQ(var_r64->dims[0], 4);
-        ASSERT_EQ(var_r64->dims[1], 2);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[2] = {0, 0};
-        uint64_t count[2] = {4, 2};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], Ny);
+        ASSERT_EQ(var_r64->dims[1], mpiSize * Nx);
+
+        // If the size of the array is smaller than the data
+        // the result is weird... double and uint64_t would get completely
+        // garbage data
+        std::array<int8_t, Nx * Ny> I8;
+        std::array<int16_t, Nx * Ny> I16;
+        std::array<int32_t, Nx * Ny> I32;
+        std::array<int64_t, Nx * Ny> I64;
+        std::array<uint8_t, Nx * Ny> U8;
+        std::array<uint16_t, Nx * Ny> U16;
+        std::array<uint32_t, Nx * Ny> U32;
+        std::array<uint64_t, Nx * Ny> U64;
+        std::array<float, Nx * Ny> R32;
+        std::array<double, Nx * Ny> R64;
+
+        uint64_t start[2] = {0, mpiRank * Nx};
+        uint64_t count[2] = {Ny, Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(2, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -645,22 +868,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -680,6 +903,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
diff --git a/testing/adios2/engine/bp/TestBPWriteReadAttributes.cpp b/testing/adios2/engine/bp/TestBPWriteReadAttributes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2bb5bfed14c791ef0ad49a89dc0d022fd13c8c7
--- /dev/null
+++ b/testing/adios2/engine/bp/TestBPWriteReadAttributes.cpp
@@ -0,0 +1,430 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ */
+#include <cstdint>
+#include <cstring>
+
+#include <iostream>
+#include <stdexcept>
+
+#include <adios2.h>
+#include <adios_read.h>
+
+#include <gtest/gtest.h>
+
+#include "../SmallTestData.h"
+
+class BPWriteReadAttributeTest : public ::testing::Test
+{
+public:
+    BPWriteReadAttributeTest() = default;
+
+    SmallTestData m_TestData;
+};
+
+//******************************************************************************
+// 1D 1x8 test data
+//******************************************************************************
+
+// ADIOS2 write, native ADIOS1 read for single value attributes
+TEST_F(BPWriteReadAttributeTest, ADIOS2BPWriteADIOS1ReadSingleTypes)
+{
+    std::string fname = "foo/ADIOS2BPWriteAttributeADIOS1ReadSingleTypes.bp";
+    std::string fRootName = "ADIOS2BPWriteAttributeADIOS1ReadSingleTypes.bp";
+
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
+    // FIXME: Since collective meta generation has not landed yet, so there is
+    // no way for us to gather different attribute data per process. Ideally we
+    // should use unique attribute data per process. Ex:
+    // std::to_string(mpiRank);
+    std::string mpiRankString = std::to_string(0);
+    std::string s1_Single = std::string("s1_Single_") + mpiRankString;
+    std::string i8_Single = std::string("i8_Single_") + mpiRankString;
+    std::string i16_Single = std::string("i16_Single_") + mpiRankString;
+    std::string i32_Single = std::string("i32_Single_") + mpiRankString;
+    std::string i64_Single = std::string("i64_Single_") + mpiRankString;
+    std::string u8_Single = std::string("u8_Single_") + mpiRankString;
+    std::string u16_Single = std::string("u16_Single_") + mpiRankString;
+    std::string u32_Single = std::string("u32_Single_") + mpiRankString;
+    std::string u64_Single = std::string("u64_Single_") + mpiRankString;
+    std::string float_Single = std::string("float_Single_") + mpiRankString;
+    std::string double_Single = std::string("double_Single_") + mpiRankString;
+
+    // When collective meta generation has landed, use
+    // generateNewSmallTestData(m_TestData, 0, mpiRank, mpiSize);
+    // Generate current testing data
+    SmallTestData currentTestData =
+        generateNewSmallTestData(m_TestData, 0, 0, 0);
+
+    // Write test data using BP
+    {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
+        adios2::ADIOS adios(true);
+#endif
+        adios2::IO &io = adios.DeclareIO("TestIO");
+
+        // Declare Single Value Attributes
+        {
+            io.DefineAttribute<std::string>(s1_Single, currentTestData.S1);
+            io.DefineAttribute<int8_t>(i8_Single, currentTestData.I8.front());
+            io.DefineAttribute<int16_t>(i16_Single,
+                                        currentTestData.I16.front());
+            io.DefineAttribute<int32_t>(i32_Single,
+                                        currentTestData.I32.front());
+            io.DefineAttribute<int64_t>(i64_Single,
+                                        currentTestData.I64.front());
+
+            io.DefineAttribute<uint8_t>(u8_Single, currentTestData.U8.front());
+            io.DefineAttribute<uint16_t>(u16_Single,
+                                         currentTestData.U16.front());
+            io.DefineAttribute<uint32_t>(u32_Single,
+                                         currentTestData.U32.front());
+            io.DefineAttribute<uint64_t>(u64_Single,
+                                         currentTestData.U64.front());
+
+            io.DefineAttribute<float>(float_Single,
+                                      currentTestData.R32.front());
+            io.DefineAttribute<double>(double_Single,
+                                       currentTestData.R64.front());
+        }
+
+        // Create the BP Engine
+        io.SetEngine("BPFileWriter");
+
+        io.AddTransport("file");
+
+        auto engine = io.Open(fname, adios2::OpenMode::Write);
+        ASSERT_NE(engine.get(), nullptr);
+
+        // Close the file
+        engine->Close();
+    }
+
+    {
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
+                               "verbose=3");
+
+        // Open the file for reading
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fRootName + "." + mpiRankString).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
+        ASSERT_NE(f, nullptr);
+
+        int size, status;
+        enum ADIOS_DATATYPES type;
+        void *data = nullptr;
+
+        // status = adios_get_attr(f, "s1_Single_0", &type, &size, &data);
+        status = adios_get_attr(f, s1_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_string);
+        std::string singleStringAttribute(reinterpret_cast<char *>(data), size);
+        ASSERT_STREQ(singleStringAttribute.c_str(), currentTestData.S1.c_str());
+
+        status = adios_get_attr(f, i8_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_byte);
+        ASSERT_EQ(*reinterpret_cast<int8_t *>(data),
+                  currentTestData.I8.front());
+
+        status = adios_get_attr(f, i16_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_short);
+        ASSERT_EQ(*reinterpret_cast<int16_t *>(data),
+                  currentTestData.I16.front());
+
+        status = adios_get_attr(f, i32_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_integer);
+        ASSERT_EQ(*reinterpret_cast<int32_t *>(data),
+                  currentTestData.I32.front());
+
+        status = adios_get_attr(f, i64_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_long);
+        ASSERT_EQ(*reinterpret_cast<int64_t *>(data),
+                  currentTestData.I64.front());
+
+        status = adios_get_attr(f, u8_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_unsigned_byte);
+        ASSERT_EQ(*reinterpret_cast<uint8_t *>(data),
+                  currentTestData.U8.front());
+
+        status = adios_get_attr(f, u16_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_unsigned_short);
+        ASSERT_EQ(*reinterpret_cast<uint16_t *>(data),
+                  currentTestData.U16.front());
+
+        status = adios_get_attr(f, u32_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_unsigned_integer);
+        ASSERT_EQ(*reinterpret_cast<uint32_t *>(data),
+                  currentTestData.U32.front());
+
+        status = adios_get_attr(f, u64_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_unsigned_long);
+        ASSERT_EQ(*reinterpret_cast<uint64_t *>(data),
+                  currentTestData.U64.front());
+
+        status = adios_get_attr(f, float_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_real);
+        ASSERT_EQ(*reinterpret_cast<float *>(data),
+                  currentTestData.R32.front());
+
+        status = adios_get_attr(f, double_Single.c_str(), &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_double);
+        ASSERT_EQ(*reinterpret_cast<double *>(data),
+                  currentTestData.R64.front());
+
+        // Cleanup file
+        adios_read_close(f);
+    }
+}
+
+// ADIOS2 write, native ADIOS1 read for array attributes
+TEST_F(BPWriteReadAttributeTest, ADIOS2BPWriteADIOS1ReadArrayTypes)
+{
+    std::string fname = "foo/bar/ADIOS2BPWriteAttributeADIOS1ReadArrayTypes.bp";
+    std::string fRootName = "ADIOS2BPWriteAttributeADIOS1ReadArrayTypes.bp";
+
+    // Write test data using ADIOS2
+    {
+        adios2::ADIOS adios(true);
+        adios2::IO &io = adios.DeclareIO("TestIO");
+
+        // Declare Array Attributes
+        {
+            io.DefineAttribute<std::string>("s3_Array", m_TestData.S3.data(),
+                                            m_TestData.S3.size());
+            io.DefineAttribute<int8_t>("i8_Array", m_TestData.I8.data(),
+                                       m_TestData.I8.size());
+            io.DefineAttribute<int16_t>("i16_Array", m_TestData.I16.data(),
+                                        m_TestData.I16.size());
+            io.DefineAttribute<int32_t>("i32_Array", m_TestData.I32.data(),
+                                        m_TestData.I32.size());
+            io.DefineAttribute<int64_t>("i64_Array", m_TestData.I64.data(),
+                                        m_TestData.I64.size());
+
+            io.DefineAttribute<uint8_t>("u8_Array", m_TestData.U8.data(),
+                                        m_TestData.U8.size());
+            io.DefineAttribute<uint16_t>("u16_Array", m_TestData.U16.data(),
+                                         m_TestData.U16.size());
+            io.DefineAttribute<uint32_t>("u32_Array", m_TestData.U32.data(),
+                                         m_TestData.U32.size());
+            io.DefineAttribute<uint64_t>("u64_Array", m_TestData.U64.data(),
+                                         m_TestData.U64.size());
+
+            io.DefineAttribute<float>("float_Array", m_TestData.R32.data(),
+                                      m_TestData.R32.size());
+            io.DefineAttribute<double>("double_Array", m_TestData.R64.data(),
+                                       m_TestData.R64.size());
+        }
+
+        // Create the BP Engine
+        io.SetEngine("BPFileWriter");
+
+        io.AddTransport("file");
+
+        auto engine = io.Open(fname, adios2::OpenMode::Write);
+        ASSERT_NE(engine.get(), nullptr);
+
+        // Close the file
+        engine->Close();
+    }
+
+    {
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+                               "verbose=3");
+
+        // Open the file for reading
+        ADIOS_FILE *f =
+            adios_read_open_file((fname + ".dir/" + fRootName + ".0").c_str(),
+                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        ASSERT_NE(f, nullptr);
+
+        int size, status;
+        enum ADIOS_DATATYPES type;
+        void *data = nullptr;
+
+        status = adios_get_attr(f, "s3_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_string_array);
+        char **stringArray = reinterpret_cast<char **>(data);
+
+        for (unsigned int i = 0; i < 3; ++i)
+        {
+            ASSERT_STREQ(std::string(stringArray[i]).c_str(),
+                         m_TestData.S3[i].c_str());
+        }
+
+        status = adios_get_attr(f, "i8_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(int8_t));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_byte);
+        int8_t *I8 = reinterpret_cast<int8_t *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(I8[i], m_TestData.I8[i]);
+        }
+
+        status = adios_get_attr(f, "i16_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(int16_t));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_short);
+        int16_t *I16 = reinterpret_cast<int16_t *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(I16[i], m_TestData.I16[i]);
+        }
+
+        status = adios_get_attr(f, "i32_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(int32_t));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_integer);
+
+        int32_t *I32 = reinterpret_cast<int32_t *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(I32[i], m_TestData.I32[i]);
+        }
+
+        status = adios_get_attr(f, "i64_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(int64_t));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_long);
+        int64_t *I64 = reinterpret_cast<int64_t *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(I64[i], m_TestData.I64[i]);
+        }
+
+        // uint
+        status = adios_get_attr(f, "u8_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(uint8_t));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_unsigned_byte);
+        uint8_t *U8 = reinterpret_cast<uint8_t *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(U8[i], m_TestData.U8[i]);
+        }
+
+        status = adios_get_attr(f, "u16_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(uint16_t));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_unsigned_short);
+        uint16_t *U16 = reinterpret_cast<uint16_t *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(U16[i], m_TestData.U16[i]);
+        }
+
+        status = adios_get_attr(f, "u32_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(uint32_t));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_unsigned_integer);
+        uint32_t *U32 = reinterpret_cast<uint32_t *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(U32[i], m_TestData.U32[i]);
+        }
+
+        status = adios_get_attr(f, "u64_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(uint64_t));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_unsigned_long);
+        uint64_t *U64 = reinterpret_cast<uint64_t *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(U64[i], m_TestData.U64[i]);
+        }
+
+        status = adios_get_attr(f, "float_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(float));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_real);
+        float *R32 = reinterpret_cast<float *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(R32[i], m_TestData.R32[i]);
+        }
+
+        status = adios_get_attr(f, "double_Array", &type, &size, &data);
+        ASSERT_EQ(status, 0);
+        ASSERT_EQ(size, 10 * sizeof(double));
+        ASSERT_NE(data, nullptr);
+        ASSERT_EQ(type, adios_double);
+        double *R64 = reinterpret_cast<double *>(data);
+
+        for (unsigned int i = 0; i < 10; ++i)
+        {
+            ASSERT_EQ(R64[i], m_TestData.R64[i]);
+        }
+
+        // Cleanup file
+        adios_read_close(f);
+    }
+}
+
+//******************************************************************************
+// main
+//******************************************************************************
+
+int main(int argc, char **argv)
+{
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Init(nullptr, nullptr);
+#endif
+
+    ::testing::InitGoogleTest(&argc, argv);
+    int result = RUN_ALL_TESTS();
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Finalize();
+#endif
+
+    return result;
+}
diff --git a/testing/adios2/engine/bp/TestBPWriteReadfstream.cpp b/testing/adios2/engine/bp/TestBPWriteReadfstream.cpp
index b979663588eb7feac3f33db1dedb1bb029a8cce0..faaaf1fd9f0bd9555e02ab41831e4ca29b6b3cb9 100644
--- a/testing/adios2/engine/bp/TestBPWriteReadfstream.cpp
+++ b/testing/adios2/engine/bp/TestBPWriteReadfstream.cpp
@@ -27,72 +27,120 @@ public:
 // 1D 1x8 test data
 //******************************************************************************
 
-// ADIOS2 write, native ADIOS1 read
+// ADIOS2 BP write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8fstream)
 {
+    // Each process would write a 1x8 array and all processes would
+    // form a mpiSize * Nx 1D array
     std::string fname = "ADIOS2BPWriteADIOS1Read1D8fstream.bp";
 
-    // Write test data using ADIOS2
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 8;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
+    // Write test data using BP
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 1D variables (NumOfProcesses * Nx)
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{8});
+            adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank)};
+            adios2::Dims count{static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
             auto &var_u8 =
-                io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{8});
-            auto &var_u16 = io.DefineVariable<unsigned short>("u16", {}, {},
-                                                              adios2::Dims{8});
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
             auto &var_u32 =
-                io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{8});
-            auto &var_u64 = io.DefineVariable<unsigned long>("u64", {}, {},
-                                                             adios2::Dims{8});
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
         io.SetEngine("BPFileWriter");
-        io.AddTransport("File", {{"Library", "fstream"}});
+        io.AddTransport("file", {{"Library", "fstream"}});
 
+        // QUESTION: It seems that BPFilterWriter cannot overwrite existing
+        // files
+        // Ex. if you tune Nx and NSteps, the test would fail. But if you clear
+        // the cache in
+        // ${adios2Build}/testing/adios2/engine/bp/ADIOS2BPWriteADIOS1Read1D8.bp.dir,
+        // then it works
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 1D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel({mpiRank * Nx}, {Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -102,83 +150,105 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8fstream)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 1);
-        ASSERT_EQ(var_i8->dims[0], 8);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 1);
-        ASSERT_EQ(var_i16->dims[0], 8);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 1);
-        ASSERT_EQ(var_i32->dims[0], 8);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 1);
-        ASSERT_EQ(var_i64->dims[0], 8);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 1);
-        ASSERT_EQ(var_u8->dims[0], 8);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 1);
-        ASSERT_EQ(var_u16->dims[0], 8);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 1);
-        ASSERT_EQ(var_u32->dims[0], 8);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 1);
-        ASSERT_EQ(var_u64->dims[0], 8);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 1);
-        ASSERT_EQ(var_r32->dims[0], 8);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 1);
-        ASSERT_EQ(var_r64->dims[0], 8);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[1] = {0};
-        uint64_t count[1] = {8};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], mpiSize * Nx);
+
+        std::array<int8_t, Nx> I8;
+        std::array<int16_t, Nx> I16;
+        std::array<int32_t, Nx> I32;
+        std::array<int64_t, Nx> I64;
+        std::array<uint8_t, Nx> U8;
+        std::array<uint16_t, Nx> U16;
+        std::array<uint32_t, Nx> U32;
+        std::array<uint64_t, Nx> U64;
+        std::array<float, Nx> R32;
+        std::array<double, Nx> R64;
+
+        uint64_t start[1] = {mpiRank * Nx};
+        uint64_t count[1] = {Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(1, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -193,22 +263,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8fstream)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -228,6 +298,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8fstream)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
@@ -243,38 +315,66 @@ TEST_F(BPWriteReadTest, DISABLED_ADIOS2BPWriteADIOS2BPRead1D8fstream)
 // 2D 2x4 test data
 //******************************************************************************
 
-// ADIOS2 write, native ADIOS1 read
+// ADIOS2 BP write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4fstream)
 {
+    // Each process would write a 2x4 array and all processes would
+    // form a 2D 2 * (numberOfProcess*Nx) matrix where Nx is 4 here
     std::string fname = "ADIOS2BPWriteADIOS1Read2D2x4Testfstream.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 4;
+
+    // Number of rows
+    const std::size_t Ny = 2;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (Ny * (NumOfProcesses * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{2, 4});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{2, 4});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{2, 4});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
@@ -284,31 +384,52 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4fstream)
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -318,93 +439,118 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4fstream)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 2);
-        ASSERT_EQ(var_i8->dims[0], 2);
-        ASSERT_EQ(var_i8->dims[1], 4);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], Ny);
+        ASSERT_EQ(var_i8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 2);
-        ASSERT_EQ(var_i16->dims[0], 2);
-        ASSERT_EQ(var_i16->dims[1], 4);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], Ny);
+        ASSERT_EQ(var_i16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 2);
-        ASSERT_EQ(var_i32->dims[0], 2);
-        ASSERT_EQ(var_i32->dims[1], 4);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], Ny);
+        ASSERT_EQ(var_i32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 2);
-        ASSERT_EQ(var_i64->dims[0], 2);
-        ASSERT_EQ(var_i64->dims[1], 4);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], Ny);
+        ASSERT_EQ(var_i64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 2);
-        ASSERT_EQ(var_u8->dims[0], 2);
-        ASSERT_EQ(var_u8->dims[1], 4);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], Ny);
+        ASSERT_EQ(var_u8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 2);
-        ASSERT_EQ(var_u16->dims[0], 2);
-        ASSERT_EQ(var_u16->dims[1], 4);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], Ny);
+        ASSERT_EQ(var_u16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 2);
-        ASSERT_EQ(var_u32->dims[0], 2);
-        ASSERT_EQ(var_u32->dims[1], 4);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], Ny);
+        ASSERT_EQ(var_u32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 2);
-        ASSERT_EQ(var_u64->dims[0], 2);
-        ASSERT_EQ(var_u64->dims[1], 4);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], Ny);
+        ASSERT_EQ(var_u64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 2);
-        ASSERT_EQ(var_r32->dims[0], 2);
-        ASSERT_EQ(var_r32->dims[1], 4);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], Ny);
+        ASSERT_EQ(var_r32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 2);
-        ASSERT_EQ(var_r64->dims[0], 2);
-        ASSERT_EQ(var_r64->dims[1], 4);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[2] = {0, 0};
-        uint64_t count[2] = {2, 4};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], Ny);
+        ASSERT_EQ(var_r64->dims[1], mpiSize * Nx);
+
+        // If the size of the array is smaller than the data
+        // the result is weird... double and uint64_t would get completely
+        // garbage data
+        std::array<int8_t, Nx * Ny> I8;
+        std::array<int16_t, Nx * Ny> I16;
+        std::array<int32_t, Nx * Ny> I32;
+        std::array<int64_t, Nx * Ny> I64;
+        std::array<uint8_t, Nx * Ny> U8;
+        std::array<uint16_t, Nx * Ny> U16;
+        std::array<uint32_t, Nx * Ny> U32;
+        std::array<uint64_t, Nx * Ny> U64;
+        std::array<float, Nx * Ny> R32;
+        std::array<double, Nx * Ny> R64;
+
+        uint64_t start[2] = {0, mpiRank * Nx};
+        uint64_t count[2] = {Ny, Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(2, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -419,22 +565,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4fstream)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -454,6 +600,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4fstream)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
@@ -472,35 +620,62 @@ TEST_F(BPWriteReadTest, DISABLED_ADIOS2BPWriteADIOS2BPRead2D2x4fstream)
 // ADIOS2 write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2fstream)
 {
+    // Each process would write a 4x2 array and all processes would
+    // form a 2D 4 * (NumberOfProcess * Nx) matrix where Nx is 2 here
     std::string fname = "ADIOS2BPWriteADIOS1Read2D4x2Testfstream.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 2;
+    // Number of cols
+    const std::size_t Ny = 4;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (4 * (NumberOfProcess * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{4, 2});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(mpiSize * Nx)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{4, 2});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{4, 2});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
@@ -510,31 +685,52 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2fstream)
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -544,93 +740,118 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2fstream)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 2);
-        ASSERT_EQ(var_i8->dims[0], 4);
-        ASSERT_EQ(var_i8->dims[1], 2);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], Ny);
+        ASSERT_EQ(var_i8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 2);
-        ASSERT_EQ(var_i16->dims[0], 4);
-        ASSERT_EQ(var_i16->dims[1], 2);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], Ny);
+        ASSERT_EQ(var_i16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 2);
-        ASSERT_EQ(var_i32->dims[0], 4);
-        ASSERT_EQ(var_i32->dims[1], 2);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], Ny);
+        ASSERT_EQ(var_i32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 2);
-        ASSERT_EQ(var_i64->dims[0], 4);
-        ASSERT_EQ(var_i64->dims[1], 2);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], Ny);
+        ASSERT_EQ(var_i64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 2);
-        ASSERT_EQ(var_u8->dims[0], 4);
-        ASSERT_EQ(var_u8->dims[1], 2);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], Ny);
+        ASSERT_EQ(var_u8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 2);
-        ASSERT_EQ(var_u16->dims[0], 4);
-        ASSERT_EQ(var_u16->dims[1], 2);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], Ny);
+        ASSERT_EQ(var_u16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 2);
-        ASSERT_EQ(var_u32->dims[0], 4);
-        ASSERT_EQ(var_u32->dims[1], 2);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], Ny);
+        ASSERT_EQ(var_u32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 2);
-        ASSERT_EQ(var_u64->dims[0], 4);
-        ASSERT_EQ(var_u64->dims[1], 2);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], Ny);
+        ASSERT_EQ(var_u64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 2);
-        ASSERT_EQ(var_r32->dims[0], 4);
-        ASSERT_EQ(var_r32->dims[1], 2);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], Ny);
+        ASSERT_EQ(var_r32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 2);
-        ASSERT_EQ(var_r64->dims[0], 4);
-        ASSERT_EQ(var_r64->dims[1], 2);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[2] = {0, 0};
-        uint64_t count[2] = {4, 2};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], Ny);
+        ASSERT_EQ(var_r64->dims[1], mpiSize * Nx);
+
+        // If the size of the array is smaller than the data
+        // the result is weird... double and uint64_t would get completely
+        // garbage data
+        std::array<int8_t, Nx * Ny> I8;
+        std::array<int16_t, Nx * Ny> I16;
+        std::array<int32_t, Nx * Ny> I32;
+        std::array<int64_t, Nx * Ny> I64;
+        std::array<uint8_t, Nx * Ny> U8;
+        std::array<uint16_t, Nx * Ny> U16;
+        std::array<uint32_t, Nx * Ny> U32;
+        std::array<uint64_t, Nx * Ny> U64;
+        std::array<float, Nx * Ny> R32;
+        std::array<double, Nx * Ny> R64;
+
+        uint64_t start[2] = {0, mpiRank * Nx};
+        uint64_t count[2] = {Ny, Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(2, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -645,22 +866,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2fstream)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -680,6 +901,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2fstream)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
diff --git a/testing/adios2/engine/bp/TestBPWriteReadstdio.cpp b/testing/adios2/engine/bp/TestBPWriteReadstdio.cpp
index 039601b80d4c8c1cc28bfb57337119b5166b78a7..a47653c7f1931804ce49bc9bfdac4557d9678b7c 100644
--- a/testing/adios2/engine/bp/TestBPWriteReadstdio.cpp
+++ b/testing/adios2/engine/bp/TestBPWriteReadstdio.cpp
@@ -27,72 +27,124 @@ public:
 // 1D 1x8 test data
 //******************************************************************************
 
-// ADIOS2 write, native ADIOS1 read
+// ADIOS2 BP write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8stdio)
 {
+    // Each process would write a 1x8 array and all processes would
+    // form a mpiSize * Nx 1D array
     std::string fname = "ADIOS2BPWriteADIOS1Read1D8stdio.bp";
 
-    // Write test data using ADIOS2
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 8;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
+    // Write test data using BP
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 1D variables (NumOfProcesses * Nx)
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{8});
+            adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank)};
+            adios2::Dims count{static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
             auto &var_u8 =
-                io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{8});
-            auto &var_u16 = io.DefineVariable<unsigned short>("u16", {}, {},
-                                                              adios2::Dims{8});
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
             auto &var_u32 =
-                io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{8});
-            auto &var_u64 = io.DefineVariable<unsigned long>("u64", {}, {},
-                                                             adios2::Dims{8});
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
         io.SetEngine("BPFileWriter");
-        io.AddTransport("File", {{"Library", "stdio"}});
 
+#ifdef ADIOS2_HAVE_MPI
+        io.AddTransport("file", {{"Library", "stdio"}});
+#else
+        io.AddTransport("file");
+#endif
+        // QUESTION: It seems that BPFilterWriter cannot overwrite existing
+        // files
+        // Ex. if you tune Nx and NSteps, the test would fail. But if you clear
+        // the cache in
+        // ${adios2Build}/testing/adios2/engine/bp/ADIOS2BPWriteADIOS1Read1D8.bp.dir,
+        // then it works
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 1D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel({mpiRank * Nx}, {Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -102,83 +154,105 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8stdio)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 1);
-        ASSERT_EQ(var_i8->dims[0], 8);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 1);
-        ASSERT_EQ(var_i16->dims[0], 8);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 1);
-        ASSERT_EQ(var_i32->dims[0], 8);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 1);
-        ASSERT_EQ(var_i64->dims[0], 8);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 1);
-        ASSERT_EQ(var_u8->dims[0], 8);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 1);
-        ASSERT_EQ(var_u16->dims[0], 8);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 1);
-        ASSERT_EQ(var_u32->dims[0], 8);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 1);
-        ASSERT_EQ(var_u64->dims[0], 8);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 1);
-        ASSERT_EQ(var_r32->dims[0], 8);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 1);
-        ASSERT_EQ(var_r64->dims[0], 8);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[1] = {0};
-        uint64_t count[1] = {8};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], mpiSize * Nx);
+
+        std::array<int8_t, Nx> I8;
+        std::array<int16_t, Nx> I16;
+        std::array<int32_t, Nx> I32;
+        std::array<int64_t, Nx> I64;
+        std::array<uint8_t, Nx> U8;
+        std::array<uint16_t, Nx> U16;
+        std::array<uint32_t, Nx> U32;
+        std::array<uint64_t, Nx> U64;
+        std::array<float, Nx> R32;
+        std::array<double, Nx> R64;
+
+        uint64_t start[1] = {mpiRank * Nx};
+        uint64_t count[1] = {Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(1, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -193,22 +267,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8stdio)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -228,6 +302,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read1D8stdio)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
@@ -243,72 +319,125 @@ TEST_F(BPWriteReadTest, DISABLED_ADIOS2BPWriteADIOS2BPRead1D8stdio)
 // 2D 2x4 test data
 //******************************************************************************
 
-// ADIOS2 write, native ADIOS1 read
+// ADIOS2 BP write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4stdio)
 {
+    // Each process would write a 2x4 array and all processes would
+    // form a 2D 2 * (numberOfProcess*Nx) matrix where Nx is 4 here
     std::string fname = "ADIOS2BPWriteADIOS1Read2D2x4Teststdio.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 4;
+
+    // Number of rows
+    const std::size_t Ny = 2;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (Ny * (NumOfProcesses * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{2, 4});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{2, 4});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{2, 4});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
         io.SetEngine("BPFileWriter");
+#ifdef ADIOS2_HAVE_MPI
         io.AddTransport("file", {{"Library", "stdio"}});
+#else
+        io.AddTransport("file");
+#endif
 
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -318,93 +447,118 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4stdio)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 2);
-        ASSERT_EQ(var_i8->dims[0], 2);
-        ASSERT_EQ(var_i8->dims[1], 4);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], Ny);
+        ASSERT_EQ(var_i8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 2);
-        ASSERT_EQ(var_i16->dims[0], 2);
-        ASSERT_EQ(var_i16->dims[1], 4);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], Ny);
+        ASSERT_EQ(var_i16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 2);
-        ASSERT_EQ(var_i32->dims[0], 2);
-        ASSERT_EQ(var_i32->dims[1], 4);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], Ny);
+        ASSERT_EQ(var_i32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 2);
-        ASSERT_EQ(var_i64->dims[0], 2);
-        ASSERT_EQ(var_i64->dims[1], 4);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], Ny);
+        ASSERT_EQ(var_i64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 2);
-        ASSERT_EQ(var_u8->dims[0], 2);
-        ASSERT_EQ(var_u8->dims[1], 4);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], Ny);
+        ASSERT_EQ(var_u8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 2);
-        ASSERT_EQ(var_u16->dims[0], 2);
-        ASSERT_EQ(var_u16->dims[1], 4);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], Ny);
+        ASSERT_EQ(var_u16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 2);
-        ASSERT_EQ(var_u32->dims[0], 2);
-        ASSERT_EQ(var_u32->dims[1], 4);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], Ny);
+        ASSERT_EQ(var_u32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 2);
-        ASSERT_EQ(var_u64->dims[0], 2);
-        ASSERT_EQ(var_u64->dims[1], 4);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], Ny);
+        ASSERT_EQ(var_u64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 2);
-        ASSERT_EQ(var_r32->dims[0], 2);
-        ASSERT_EQ(var_r32->dims[1], 4);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], Ny);
+        ASSERT_EQ(var_r32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 2);
-        ASSERT_EQ(var_r64->dims[0], 2);
-        ASSERT_EQ(var_r64->dims[1], 4);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[2] = {0, 0};
-        uint64_t count[2] = {2, 4};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], Ny);
+        ASSERT_EQ(var_r64->dims[1], mpiSize * Nx);
+
+        // If the size of the array is smaller than the data
+        // the result is weird... double and uint64_t would get completely
+        // garbage data
+        std::array<int8_t, Nx * Ny> I8;
+        std::array<int16_t, Nx * Ny> I16;
+        std::array<int32_t, Nx * Ny> I32;
+        std::array<int64_t, Nx * Ny> I64;
+        std::array<uint8_t, Nx * Ny> U8;
+        std::array<uint16_t, Nx * Ny> U16;
+        std::array<uint32_t, Nx * Ny> U32;
+        std::array<uint64_t, Nx * Ny> U64;
+        std::array<float, Nx * Ny> R32;
+        std::array<double, Nx * Ny> R64;
+
+        uint64_t start[2] = {0, mpiRank * Nx};
+        uint64_t count[2] = {Ny, Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(2, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -419,22 +573,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4stdio)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -454,6 +608,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D2x4stdio)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
@@ -472,69 +628,122 @@ TEST_F(BPWriteReadTest, DISABLED_ADIOS2BPWriteADIOS2BPRead2D2x4stdio)
 // ADIOS2 write, native ADIOS1 read
 TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2stdio)
 {
+    // Each process would write a 4x2 array and all processes would
+    // form a 2D 4 * (NumberOfProcess * Nx) matrix where Nx is 2 here
     std::string fname = "ADIOS2BPWriteADIOS1Read2D4x2Teststdio.bp";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 2;
+    // Number of cols
+    const std::size_t Ny = 4;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (4 * (NumberOfProcess * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{4, 2});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(mpiSize * Nx)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{4, 2});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{4, 2});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
         // Create the BP Engine
         io.SetEngine("BPFileWriter");
+
+#ifdef ADIOS2_HAVE_MPI
         io.AddTransport("file", {{"Library", "stdio"}});
+#else
+        io.AddTransport("file");
+#endif
 
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -544,93 +753,118 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2stdio)
         engine->Close();
     }
 
-// Read test data using ADIOS1
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank();
-    if (rank == 0)
-#endif
     {
-        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_WORLD,
+        adios_read_init_method(ADIOS_READ_METHOD_BP, MPI_COMM_SELF,
                                "verbose=3");
 
         // Open the file for reading
-        ADIOS_FILE *f =
-            adios_read_open_file((fname + ".dir/" + fname + ".0").c_str(),
-                                 ADIOS_READ_METHOD_BP, MPI_COMM_WORLD);
+        // Note: Since collective metadata generation is not implemented yet,
+        // SO for now we read each subfile instead of a single bp file with all
+        // metadata.
+        // Meanwhile if we open file with MPI_COMM_WORLD, then the selection
+        // bounding box should be [0, Nx]
+        std::string index = std::to_string(mpiRank);
+        ADIOS_FILE *f = adios_read_open_file(
+            (fname + ".dir/" + fname + "." + index).c_str(),
+            ADIOS_READ_METHOD_BP, MPI_COMM_SELF);
         ASSERT_NE(f, nullptr);
 
         // Check the variables exist
         ADIOS_VARINFO *var_i8 = adios_inq_var(f, "i8");
         ASSERT_NE(var_i8, nullptr);
         ASSERT_EQ(var_i8->ndim, 2);
-        ASSERT_EQ(var_i8->dims[0], 4);
-        ASSERT_EQ(var_i8->dims[1], 2);
+        ASSERT_EQ(var_i8->global, 1);
+        ASSERT_EQ(var_i8->nsteps, NSteps);
+        ASSERT_EQ(var_i8->dims[0], Ny);
+        ASSERT_EQ(var_i8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i16 = adios_inq_var(f, "i16");
         ASSERT_NE(var_i16, nullptr);
         ASSERT_EQ(var_i16->ndim, 2);
-        ASSERT_EQ(var_i16->dims[0], 4);
-        ASSERT_EQ(var_i16->dims[1], 2);
+        ASSERT_EQ(var_i16->global, 1);
+        ASSERT_EQ(var_i16->nsteps, NSteps);
+        ASSERT_EQ(var_i16->dims[0], Ny);
+        ASSERT_EQ(var_i16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i32 = adios_inq_var(f, "i32");
         ASSERT_NE(var_i32, nullptr);
         ASSERT_EQ(var_i32->ndim, 2);
-        ASSERT_EQ(var_i32->dims[0], 4);
-        ASSERT_EQ(var_i32->dims[1], 2);
+        ASSERT_EQ(var_i32->global, 1);
+        ASSERT_EQ(var_i32->nsteps, NSteps);
+        ASSERT_EQ(var_i32->dims[0], Ny);
+        ASSERT_EQ(var_i32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_i64 = adios_inq_var(f, "i64");
         ASSERT_NE(var_i64, nullptr);
         ASSERT_EQ(var_i64->ndim, 2);
-        ASSERT_EQ(var_i64->dims[0], 4);
-        ASSERT_EQ(var_i64->dims[1], 2);
+        ASSERT_EQ(var_i64->global, 1);
+        ASSERT_EQ(var_i64->nsteps, NSteps);
+        ASSERT_EQ(var_i64->dims[0], Ny);
+        ASSERT_EQ(var_i64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u8 = adios_inq_var(f, "u8");
         ASSERT_NE(var_u8, nullptr);
         ASSERT_EQ(var_u8->ndim, 2);
-        ASSERT_EQ(var_u8->dims[0], 4);
-        ASSERT_EQ(var_u8->dims[1], 2);
+        ASSERT_EQ(var_u8->global, 1);
+        ASSERT_EQ(var_u8->nsteps, NSteps);
+        ASSERT_EQ(var_u8->dims[0], Ny);
+        ASSERT_EQ(var_u8->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u16 = adios_inq_var(f, "u16");
         ASSERT_NE(var_u16, nullptr);
         ASSERT_EQ(var_u16->ndim, 2);
-        ASSERT_EQ(var_u16->dims[0], 4);
-        ASSERT_EQ(var_u16->dims[1], 2);
+        ASSERT_EQ(var_u16->global, 1);
+        ASSERT_EQ(var_u16->nsteps, NSteps);
+        ASSERT_EQ(var_u16->dims[0], Ny);
+        ASSERT_EQ(var_u16->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u32 = adios_inq_var(f, "u32");
         ASSERT_NE(var_u32, nullptr);
         ASSERT_EQ(var_u32->ndim, 2);
-        ASSERT_EQ(var_u32->dims[0], 4);
-        ASSERT_EQ(var_u32->dims[1], 2);
+        ASSERT_EQ(var_u32->global, 1);
+        ASSERT_EQ(var_u32->nsteps, NSteps);
+        ASSERT_EQ(var_u32->dims[0], Ny);
+        ASSERT_EQ(var_u32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_u64 = adios_inq_var(f, "u64");
         ASSERT_NE(var_u64, nullptr);
         ASSERT_EQ(var_u64->ndim, 2);
-        ASSERT_EQ(var_u64->dims[0], 4);
-        ASSERT_EQ(var_u64->dims[1], 2);
+        ASSERT_EQ(var_u64->global, 1);
+        ASSERT_EQ(var_u64->nsteps, NSteps);
+        ASSERT_EQ(var_u64->dims[0], Ny);
+        ASSERT_EQ(var_u64->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r32 = adios_inq_var(f, "r32");
         ASSERT_NE(var_r32, nullptr);
         ASSERT_EQ(var_r32->ndim, 2);
-        ASSERT_EQ(var_r32->dims[0], 4);
-        ASSERT_EQ(var_r32->dims[1], 2);
+        ASSERT_EQ(var_r32->global, 1);
+        ASSERT_EQ(var_r32->nsteps, NSteps);
+        ASSERT_EQ(var_r32->dims[0], Ny);
+        ASSERT_EQ(var_r32->dims[1], mpiSize * Nx);
         ADIOS_VARINFO *var_r64 = adios_inq_var(f, "r64");
         ASSERT_NE(var_r64, nullptr);
         ASSERT_EQ(var_r64->ndim, 2);
-        ASSERT_EQ(var_r64->dims[0], 4);
-        ASSERT_EQ(var_r64->dims[1], 2);
-
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        uint64_t start[2] = {0, 0};
-        uint64_t count[2] = {4, 2};
+        ASSERT_EQ(var_r64->global, 1);
+        ASSERT_EQ(var_r64->nsteps, NSteps);
+        ASSERT_EQ(var_r64->dims[0], Ny);
+        ASSERT_EQ(var_r64->dims[1], mpiSize * Nx);
+
+        // If the size of the array is smaller than the data
+        // the result is weird... double and uint64_t would get completely
+        // garbage data
+        std::array<int8_t, Nx * Ny> I8;
+        std::array<int16_t, Nx * Ny> I16;
+        std::array<int32_t, Nx * Ny> I32;
+        std::array<int64_t, Nx * Ny> I64;
+        std::array<uint8_t, Nx * Ny> U8;
+        std::array<uint16_t, Nx * Ny> U16;
+        std::array<uint32_t, Nx * Ny> U32;
+        std::array<uint64_t, Nx * Ny> U64;
+        std::array<float, Nx * Ny> R32;
+        std::array<double, Nx * Ny> R64;
+
+        uint64_t start[2] = {0, mpiRank * Nx};
+        uint64_t count[2] = {Ny, Nx};
         ADIOS_SELECTION *sel = adios_selection_boundingbox(2, start, count);
 
         // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            // Generate test data for each rank uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
             // Read the current step
             adios_schedule_read_byid(f, sel, var_i8->varid, t, 1, I8.data());
             adios_schedule_read_byid(f, sel, var_i16->varid, t, 1, I16.data());
@@ -645,22 +879,22 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2stdio)
             adios_perform_reads(f, 1);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
         }
 
@@ -680,6 +914,8 @@ TEST_F(BPWriteReadTest, ADIOS2BPWriteADIOS1Read2D4x2stdio)
 
         // Cleanup file
         adios_read_close(f);
+
+        adios_read_finalize_method(ADIOS_READ_METHOD_BP);
     }
 }
 
diff --git a/testing/adios2/engine/hdf5/CMakeLists.txt b/testing/adios2/engine/hdf5/CMakeLists.txt
index 9847eaee6b081946ccf2c98db47a23b587daa223..85a20cb648ae68c523f1ffe7c8b5a5a3873ba6b8 100644
--- a/testing/adios2/engine/hdf5/CMakeLists.txt
+++ b/testing/adios2/engine/hdf5/CMakeLists.txt
@@ -3,10 +3,23 @@
 # accompanying file Copyright.txt for details.
 #------------------------------------------------------------------------------#
 
-find_package(HDF5 REQUIRED)
-
 add_executable(TestHDF5WriteRead TestHDF5WriteRead.cpp)
-target_include_directories(TestHDF5WriteRead PRIVATE ${HDF5_C_INCLUDE_DIRS})
+
+# Workaround for multiple versions of FindHDF5
+if(HDF5_C_INCLUDE_DIRS)
+  target_include_directories(TestHDF5WriteRead PRIVATE ${HDF5_C_INCLUDE_DIRS})
+else()
+  target_include_directories(TestHDF5WriteRead PRIVATE ${HDF5_INCLUDE_DIRS})
+endif()
 target_link_libraries(TestHDF5WriteRead adios2 gtest ${HDF5_C_LIBRARIES})
 
-gtest_add_tests(TARGET TestHDF5WriteRead)
+if(ADIOS2_HAVE_MPI)
+  target_include_directories(TestHDF5WriteRead PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(TestHDF5WriteRead ${MPI_C_LIBRARIES})
+  set(extra_test_args
+    EXEC_WRAPPER
+      ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS}
+  )
+endif()
+
+gtest_add_tests(TARGET TestHDF5WriteRead ${extra_test_args})
diff --git a/testing/adios2/engine/hdf5/TestHDF5WriteRead.cpp b/testing/adios2/engine/hdf5/TestHDF5WriteRead.cpp
index e0cef14cb17924f199989464af83a89b7f977af3..dd92b35c81fc96b7a7ba7fe454285e99b79cd9c7 100644
--- a/testing/adios2/engine/hdf5/TestHDF5WriteRead.cpp
+++ b/testing/adios2/engine/hdf5/TestHDF5WriteRead.cpp
@@ -35,7 +35,11 @@ public:
 
     void GetVarInfo(const std::string varName, std::vector<hsize_t> &dims,
                     hid_t &h5Type);
-    void ReadVar(const std::string varName, void *dataArray);
+    // If offset, count and memspaceSize are provided, then variable would be
+    // read by selection
+    void ReadVar(const std::string varName, void *dataArray,
+                 hsize_t *offset = nullptr, hsize_t *count = nullptr,
+                 const size_t memsspaceSize = 0);
 
     int m_CurrentTimeStep;
     unsigned int m_TotalTimeSteps;
@@ -96,7 +100,8 @@ void HDF5NativeReader::GetVarInfo(const std::string varName,
     hid_t dataSetId = H5Dopen(m_GroupId, varName.c_str(), H5P_DEFAULT);
     if (dataSetId < 0)
     {
-        throw std::runtime_error("Unable to open dataset " + varName);
+        throw std::runtime_error("Unable to open dataset " + varName +
+                                 " when getVarInfo");
     }
 
     hid_t fileSpaceId = H5Dget_space(dataSetId);
@@ -151,7 +156,9 @@ bool HDF5NativeReader::Advance()
     return true;
 }
 
-void HDF5NativeReader::ReadVar(const std::string varName, void *dataArray)
+void HDF5NativeReader::ReadVar(const std::string varName, void *dataArray,
+                               hsize_t *offset, hsize_t *count,
+                               const size_t memspaceSize)
 {
     if (m_GroupId < 0)
     {
@@ -162,7 +169,8 @@ void HDF5NativeReader::ReadVar(const std::string varName, void *dataArray)
     hid_t dataSetId = H5Dopen(m_GroupId, varName.c_str(), H5P_DEFAULT);
     if (dataSetId < 0)
     {
-        throw std::runtime_error("Unable to open dataset " + varName);
+        throw std::runtime_error("Unable to open dataset " + varName +
+                                 "when ReadVar");
     }
 
     hid_t fileSpace = H5Dget_space(dataSetId);
@@ -173,8 +181,35 @@ void HDF5NativeReader::ReadVar(const std::string varName, void *dataArray)
     }
 
     hid_t h5type = H5Dget_type(dataSetId);
-    hid_t ret =
-        H5Dread(dataSetId, h5type, H5S_ALL, H5S_ALL, H5P_DEFAULT, dataArray);
+
+    // Extend reader to support read by hyperslab selection
+    // Reference link: https://support.hdfgroup.org/HDF5/Tutor/select.html
+    // Check if hyperspace is provided
+    if (offset && count)
+    {
+        // Get the dataspace
+        hid_t dataspace = H5Dget_space(dataSetId);
+        // Define hyperslab in the dataset
+        hid_t status = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, offset,
+                                           NULL, count, NULL);
+        if (status < 0)
+        {
+            throw std::runtime_error(
+                "Unable to create a selection for dataset" + varName);
+        }
+
+        hsize_t dimsm[1];
+        dimsm[0] = memspaceSize;
+        hid_t memspace = H5Screate_simple(1, dimsm, NULL);
+
+        hid_t ret = H5Dread(dataSetId, h5type, memspace, dataspace, H5P_DEFAULT,
+                            dataArray);
+    }
+    else
+    {
+        hid_t ret = H5Dread(dataSetId, h5type, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+                            dataArray);
+    }
 
     H5Sclose(fileSpace);
     H5Dclose(dataSetId);
@@ -187,67 +222,117 @@ void HDF5NativeReader::ReadVar(const std::string varName, void *dataArray)
 // ADIOS2 write, native HDF5 read
 TEST_F(HDF5WriteReadTest, ADIOS2HDF5WriteHDF5Read1D8)
 {
+    // Each process would write a 1x8 array and all processes would
+    // form a mpiSize * Nx 1D array
     std::string fname = "ADIOS2HDF5WriteHDF5Read1D8.h5";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 8;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
-        adios2::ADIOS adios(true); // moved up
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
+        adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
-        // Declare 1D variables
+
+        // Declare 1D variables (NumOfProcesses * Nx)
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{8});
+            adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank)};
+            adios2::Dims count{static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
             auto &var_u8 =
-                io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{8});
-            auto &var_u16 = io.DefineVariable<unsigned short>("u16", {}, {},
-                                                              adios2::Dims{8});
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
             auto &var_u32 =
-                io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{8});
-            auto &var_u64 = io.DefineVariable<unsigned long>("u64", {}, {},
-                                                             adios2::Dims{8});
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{8});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{8});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
-        // Create the HDF5 Engine
+        // Create the ADIOS 1 Engine
         io.SetEngine("HDF5Writer");
 
+        // HDf5 engine calls the HDF5 common object that calls the hDF5 library.
+        // The IO functionality, SetParameters and AddTransports will be added
+        // in the future. For now `io.AddTransport("file", {
+        // "library", "MPI"}});` is omitted.
+        // })
+        io.AddTransport("file");
+
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 1D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel({mpiRank * Nx}, {Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -257,113 +342,115 @@ TEST_F(HDF5WriteReadTest, ADIOS2HDF5WriteHDF5Read1D8)
         engine->Close();
     }
 
-// Read test data using HDF5
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
-    if (rank == 0)
-#endif
     {
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
+        const size_t arraySize = Nx;
+        std::array<int8_t, arraySize> I8;
+        std::array<int16_t, arraySize> I16;
+        std::array<int32_t, arraySize> I32;
+        std::array<int64_t, arraySize> I64;
+        std::array<uint8_t, arraySize> U8;
+        std::array<uint16_t, arraySize> U16;
+        std::array<uint32_t, arraySize> U32;
+        std::array<uint64_t, arraySize> U64;
+        std::array<float, arraySize> R32;
+        std::array<double, arraySize> R64;
 
         HDF5NativeReader hdf5Reader(fname);
-
-        // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        // 1D
+        hsize_t count[1], offset[1];
+        count[0] = mpiRank * Nx;
+        offset[0] = Nx;
+        size_t globalArraySize = Nx * mpiSize;
+
+        // For each variable, we would verify its global size and type.
+        // Then we would retrieve the data back which is written by the
+        // current process and validate the value
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
+
             std::vector<hsize_t> gDims;
             hid_t h5Type;
 
             hdf5Reader.GetVarInfo("i8", gDims, h5Type);
-
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_CHAR), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("i8", I8.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("i8", I8.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i16", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_SHORT), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("i16", I16.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("i16", I16.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_INT), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("i32", I32.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("i32", I32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_LONG), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("i64", I64.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("i64", I64.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u8", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_UCHAR), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("u8", U8.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("u8", U8.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u16", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_USHORT), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("u16", U16.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("u16", U16.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_UINT), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("u32", U32.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("u32", U32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_ULONG), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("u64", U64.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("u64", U64.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("r32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_FLOAT), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("r32", R32.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("r32", R32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("r64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_DOUBLE), 1);
             ASSERT_EQ(gDims.size(), 1);
-            ASSERT_EQ(gDims[0], 8);
-            hdf5Reader.ReadVar("r64", R64.data());
+            ASSERT_EQ(gDims[0], globalArraySize);
+            hdf5Reader.ReadVar("r64", R64.data(), count, offset, arraySize);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
-
             hdf5Reader.Advance();
         }
     }
@@ -392,69 +479,124 @@ TEST_F(HDF5WriteReadTest, DISABLED_HDF5WriteADIOS2HDF5Read1D8)
 // ADIOS2 write, native HDF5 read
 TEST_F(HDF5WriteReadTest, ADIOS2HDF5WriteHDF5Read2D2x4)
 {
+    // Each process would write a 2x4 array and all processes would
+    // form a 2D 2 * (numberOfProcess*Nx) matrix where Nx is 4 here
     std::string fname = "ADIOS2HDF5WriteHDF5Read2D2x4Test.h5";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 4;
+
+    // Number of rows
+    const std::size_t Ny = 2;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (Ny * (NumOfProcesses * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{2, 4});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx * mpiSize)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{2, 4});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{2, 4});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{2, 4});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{2, 4});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
+        // Create the ADIOS 1 Engine
         io.SetEngine("HDF5Writer");
+
+        // HDf5 engine calls the HDF5 common object that calls the hDF5 library.
+        // The IO functionality, SetParameters and AddTransports will be added
+        // in the future. For now `io.AddTransport("file", {
+        // "library", "MPI"}});` is omitted.
+        // })
         io.AddTransport("file");
 
-        // Create the HDF5 Engine
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -464,30 +606,36 @@ TEST_F(HDF5WriteReadTest, ADIOS2HDF5WriteHDF5Read2D2x4)
         engine->Close();
     }
 
-// Read test data using HDF5
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
-    if (rank == 0)
-#endif
     {
         HDF5NativeReader hdf5Reader(fname);
 
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        const size_t arraySize = Nx * Ny;
+        std::array<int8_t, arraySize> I8;
+        std::array<int16_t, arraySize> I16;
+        std::array<int32_t, arraySize> I32;
+        std::array<int64_t, arraySize> I64;
+        std::array<uint8_t, arraySize> U8;
+        std::array<uint16_t, arraySize> U16;
+        std::array<uint32_t, arraySize> U32;
+        std::array<uint64_t, arraySize> U64;
+        std::array<float, arraySize> R32;
+        std::array<double, arraySize> R64;
+        // 2D
+        hsize_t count[2], offset[2];
+        count[0] = 0;
+        count[1] = mpiRank * Nx;
+        offset[0] = Ny;
+        offset[1] = Nx;
+        size_t globalArraySize = Nx * mpiSize;
+
+        // For each variable, we would verify its global size and type.
+        // Then we would retrieve the data back which is written by the
+        // current process and validate the value
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
+
             std::vector<hsize_t> gDims;
             hid_t h5Type;
 
@@ -495,89 +643,89 @@ TEST_F(HDF5WriteReadTest, ADIOS2HDF5WriteHDF5Read2D2x4)
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_CHAR), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("i8", I8.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("i8", I8.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i16", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_SHORT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("i16", I16.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("i16", I16.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_INT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("i32", I32.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("i32", I32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_LONG), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("i64", I64.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("i64", I64.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u8", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_UCHAR), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("u8", U8.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("u8", U8.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u16", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_USHORT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("u16", U16.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("u16", U16.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_UINT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("u32", U32.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("u32", U32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_ULONG), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("u64", U64.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("u64", U64.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("r32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_FLOAT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("r32", R32.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("r32", R32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("r64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_DOUBLE), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 2);
-            ASSERT_EQ(gDims[1], 4);
-            hdf5Reader.ReadVar("r64", R64.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("r64", R64.data(), count, offset, arraySize);
 
             // Check if it's correct
-            for (size_t i = 0; i < 8; ++i)
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
             hdf5Reader.Advance();
         }
@@ -607,69 +755,124 @@ TEST_F(HDF5WriteReadTest, DISABLED_HDF5WriteADIOS2HDF5Read2D2x4)
 // ADIOS2 write, native HDF5 read
 TEST_F(HDF5WriteReadTest, ADIOS2HDF5WriteHDF5Read2D4x2)
 {
+
+    // Each process would write a 4x2 array and all processes would
+    // form a 2D 4 * (NumberOfProcess * Nx) matrix where Nx is 2 here
     std::string fname = "ADIOS2HDF5WriteHDF5Read2D4x2Test.h5";
 
+    int mpiRank = 0, mpiSize = 1;
+    // Number of rows
+    const std::size_t Nx = 2;
+    // Number of cols
+    const std::size_t Ny = 4;
+
+    // Number of steps
+    const std::size_t NSteps = 3;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
     // Write test data using ADIOS2
     {
+#ifdef ADIOS2_HAVE_MPI
+        adios2::ADIOS adios(MPI_COMM_WORLD, adios2::DebugON);
+#else
         adios2::ADIOS adios(true);
+#endif
         adios2::IO &io = adios.DeclareIO("TestIO");
 
-        // Declare 1D variables
+        // Declare 2D variables (4 * (NumberOfProcess * Nx))
+        // The local process' part (start, count) can be defined now or later
+        // before Write().
         {
-            auto &var_i8 =
-                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{4, 2});
+            adios2::Dims shape{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(mpiSize * Nx)};
+            adios2::Dims start{static_cast<unsigned int>(0),
+                               static_cast<unsigned int>(mpiRank * Nx)};
+            adios2::Dims count{static_cast<unsigned int>(Ny),
+                               static_cast<unsigned int>(Nx)};
+            auto &var_i8 = io.DefineVariable<int8_t>("i8", shape, start, count);
             auto &var_i16 =
-                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int16_t>("i16", shape, start, count);
             auto &var_i32 =
-                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int32_t>("i32", shape, start, count);
             auto &var_i64 =
-                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{4, 2});
-            auto &var_u8 = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u16 = io.DefineVariable<unsigned short>(
-                "u16", {}, {}, adios2::Dims{4, 2});
-            auto &var_u32 = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                            adios2::Dims{4, 2});
-            auto &var_u64 = io.DefineVariable<unsigned long>(
-                "u64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<int64_t>("i64", shape, start, count);
+            auto &var_u8 =
+                io.DefineVariable<uint8_t>("u8", shape, start, count);
+            auto &var_u16 =
+                io.DefineVariable<uint16_t>("u16", shape, start, count);
+            auto &var_u32 =
+                io.DefineVariable<uint32_t>("u32", shape, start, count);
+            auto &var_u64 =
+                io.DefineVariable<uint64_t>("u64", shape, start, count);
             auto &var_r32 =
-                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<float>("r32", shape, start, count);
             auto &var_r64 =
-                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{4, 2});
+                io.DefineVariable<double>("r64", shape, start, count);
         }
 
-        // Create the HDF5 Engine
+        // Create the ADIOS 1 Engine
         io.SetEngine("HDF5Writer");
+
+        // HDf5 engine calls the HDF5 common object that calls the hDF5 library.
+        // The IO functionality, SetParameters and AddTransports will be added
+        // in the future. For now `io.AddTransport("file", {
+        // "library", "MPI"}});` is omitted.
+        // })
         io.AddTransport("file");
 
         auto engine = io.Open(fname, adios2::OpenMode::Write);
         ASSERT_NE(engine.get(), nullptr);
 
-        for (size_t step = 0; step < 3; ++step)
+        for (size_t step = 0; step < NSteps; ++step)
         {
+            // Generate test data for each process uniquely
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, step, mpiRank, mpiSize);
+
             // Retrieve the variables that previously went out of scope
-            auto &var_i8 = io.GetVariable<char>("i8");
-            auto &var_i16 = io.GetVariable<short>("i16");
-            auto &var_i32 = io.GetVariable<int>("i32");
-            auto &var_i64 = io.GetVariable<long>("i64");
-            auto &var_u8 = io.GetVariable<unsigned char>("u8");
-            auto &var_u16 = io.GetVariable<unsigned short>("u16");
-            auto &var_u32 = io.GetVariable<unsigned int>("u32");
-            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_i8 = io.GetVariable<int8_t>("i8");
+            auto &var_i16 = io.GetVariable<int16_t>("i16");
+            auto &var_i32 = io.GetVariable<int32_t>("i32");
+            auto &var_i64 = io.GetVariable<int64_t>("i64");
+            auto &var_u8 = io.GetVariable<uint8_t>("u8");
+            auto &var_u16 = io.GetVariable<uint16_t>("u16");
+            auto &var_u32 = io.GetVariable<uint32_t>("u32");
+            auto &var_u64 = io.GetVariable<uint64_t>("u64");
             auto &var_r32 = io.GetVariable<float>("r32");
             auto &var_r64 = io.GetVariable<double>("r64");
 
+            // Make a 2D selection to describe the local dimensions of the
+            // variable we write and its offsets in the global spaces
+            adios2::SelectionBoundingBox sel(
+                {0, static_cast<unsigned int>(mpiRank * Nx)}, {Ny, Nx});
+            var_i8.SetSelection(sel);
+            var_i16.SetSelection(sel);
+            var_i32.SetSelection(sel);
+            var_i64.SetSelection(sel);
+            var_u8.SetSelection(sel);
+            var_u16.SetSelection(sel);
+            var_u32.SetSelection(sel);
+            var_u64.SetSelection(sel);
+            var_r32.SetSelection(sel);
+            var_r64.SetSelection(sel);
+
             // Write each one
-            engine->Write(var_i8, m_TestData.I8.data() + step);
-            engine->Write(var_i16, m_TestData.I16.data() + step);
-            engine->Write(var_i32, m_TestData.I32.data() + step);
-            engine->Write(var_i64, m_TestData.I64.data() + step);
-            engine->Write(var_u8, m_TestData.U8.data() + step);
-            engine->Write(var_u16, m_TestData.U16.data() + step);
-            engine->Write(var_u32, m_TestData.U32.data() + step);
-            engine->Write(var_u64, m_TestData.U64.data() + step);
-            engine->Write(var_r32, m_TestData.R32.data() + step);
-            engine->Write(var_r64, m_TestData.R64.data() + step);
+            // fill in the variable with values from starting index to
+            // starting index + count
+            engine->Write(var_i8, currentTestData.I8.data());
+            engine->Write(var_i16, currentTestData.I16.data());
+            engine->Write(var_i32, currentTestData.I32.data());
+            engine->Write(var_i64, currentTestData.I64.data());
+            engine->Write(var_u8, currentTestData.U8.data());
+            engine->Write(var_u16, currentTestData.U16.data());
+            engine->Write(var_u32, currentTestData.U32.data());
+            engine->Write(var_u64, currentTestData.U64.data());
+            engine->Write(var_r32, currentTestData.R32.data());
+            engine->Write(var_r64, currentTestData.R64.data());
 
             // Advance to the next time step
             engine->Advance();
@@ -679,31 +882,37 @@ TEST_F(HDF5WriteReadTest, ADIOS2HDF5WriteHDF5Read2D4x2)
         engine->Close();
     }
 
-// Read test data using HDF5
-#ifdef ADIOS2_HAVE_MPI
-    // Read everything from rank 0
-    int rank;
-    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
-    if (rank == 0)
-#endif
     {
 
         HDF5NativeReader hdf5Reader(fname);
 
-        std::array<char, 8> I8;
-        std::array<int16_t, 8> I16;
-        std::array<int32_t, 8> I32;
-        std::array<int64_t, 8> I64;
-        std::array<unsigned char, 8> U8;
-        std::array<uint16_t, 8> U16;
-        std::array<uint32_t, 8> U32;
-        std::array<uint64_t, 8> U64;
-        std::array<float, 8> R32;
-        std::array<double, 8> R64;
-
-        // Read stuff
-        for (size_t t = 0; t < 3; ++t)
+        const size_t arraySize = Nx * Ny;
+        std::array<int8_t, arraySize> I8;
+        std::array<int16_t, arraySize> I16;
+        std::array<int32_t, arraySize> I32;
+        std::array<int64_t, arraySize> I64;
+        std::array<uint8_t, arraySize> U8;
+        std::array<uint16_t, arraySize> U16;
+        std::array<uint32_t, arraySize> U32;
+        std::array<uint64_t, arraySize> U64;
+        std::array<float, arraySize> R32;
+        std::array<double, arraySize> R64;
+        // 2D
+        hsize_t count[2], offset[2];
+        count[0] = 0;
+        count[1] = mpiRank * Nx;
+        offset[0] = Ny;
+        offset[1] = Nx;
+        size_t globalArraySize = Nx * mpiSize;
+
+        // For each variable, we would verify its global size and type.
+        // Then we would retrieve the data back which is written by the
+        // current process and validate the value
+        for (size_t t = 0; t < NSteps; ++t)
         {
+            SmallTestData currentTestData =
+                generateNewSmallTestData(m_TestData, t, mpiRank, mpiSize);
+
             std::vector<hsize_t> gDims;
             hid_t h5Type;
 
@@ -711,88 +920,89 @@ TEST_F(HDF5WriteReadTest, ADIOS2HDF5WriteHDF5Read2D4x2)
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_CHAR), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("i8", I8.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("i8", I8.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i16", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_SHORT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("i16", I16.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("i16", I16.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_INT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("i32", I32.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("i32", I32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("i64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_LONG), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("i64", I64.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("i64", I64.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u8", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_UCHAR), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("u8", U8.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("u8", U8.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u16", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_USHORT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("u16", U16.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("u16", U16.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_UINT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("u32", U32.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("u32", U32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("u64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_ULONG), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("u64", U64.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("u64", U64.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("r32", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_FLOAT), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("r32", R32.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("r32", R32.data(), count, offset, arraySize);
 
             hdf5Reader.GetVarInfo("r64", gDims, h5Type);
             ASSERT_EQ(H5Tequal(h5Type, H5T_NATIVE_DOUBLE), 1);
             ASSERT_EQ(gDims.size(), 2);
             ASSERT_EQ(gDims[0], 4);
-            ASSERT_EQ(gDims[1], 2);
-            hdf5Reader.ReadVar("r64", R64.data());
+            ASSERT_EQ(gDims[1], globalArraySize);
+            hdf5Reader.ReadVar("r64", R64.data(), count, offset, arraySize);
 
-            for (size_t i = 0; i < 8; ++i)
+            // Check if it's correct
+            for (size_t i = 0; i < Nx; ++i)
             {
                 std::stringstream ss;
-                ss << "t=" << t << " i=" << i;
+                ss << "t=" << t << " i=" << i << " rank=" << mpiRank;
                 std::string msg = ss.str();
 
-                EXPECT_EQ(I8[i], m_TestData.I8[i + t]) << msg;
-                EXPECT_EQ(I16[i], m_TestData.I16[i + t]) << msg;
-                EXPECT_EQ(I32[i], m_TestData.I32[i + t]) << msg;
-                EXPECT_EQ(I64[i], m_TestData.I64[i + t]) << msg;
-                EXPECT_EQ(U8[i], m_TestData.U8[i + t]) << msg;
-                EXPECT_EQ(U16[i], m_TestData.U16[i + t]) << msg;
-                EXPECT_EQ(U32[i], m_TestData.U32[i + t]) << msg;
-                EXPECT_EQ(U64[i], m_TestData.U64[i + t]) << msg;
-                EXPECT_EQ(R32[i], m_TestData.R32[i + t]) << msg;
-                EXPECT_EQ(R64[i], m_TestData.R64[i + t]) << msg;
+                EXPECT_EQ(I8[i], currentTestData.I8[i]) << msg;
+                EXPECT_EQ(I16[i], currentTestData.I16[i]) << msg;
+                EXPECT_EQ(I32[i], currentTestData.I32[i]) << msg;
+                EXPECT_EQ(I64[i], currentTestData.I64[i]) << msg;
+                EXPECT_EQ(U8[i], currentTestData.U8[i]) << msg;
+                EXPECT_EQ(U16[i], currentTestData.U16[i]) << msg;
+                EXPECT_EQ(U32[i], currentTestData.U32[i]) << msg;
+                EXPECT_EQ(U64[i], currentTestData.U64[i]) << msg;
+                EXPECT_EQ(R32[i], currentTestData.R32[i]) << msg;
+                EXPECT_EQ(R64[i], currentTestData.R64[i]) << msg;
             }
             hdf5Reader.Advance();
         }
diff --git a/testing/adios2/interface/CMakeLists.txt b/testing/adios2/interface/CMakeLists.txt
index e68ccfa6187ab17710e4bca93579ede6ded95079..986133796b28597df33ed0591e9feab92cee400b 100644
--- a/testing/adios2/interface/CMakeLists.txt
+++ b/testing/adios2/interface/CMakeLists.txt
@@ -9,5 +9,25 @@ target_link_libraries(TestADIOSInterfaceWrite adios2 gtest gtest_main)
 add_executable(TestADIOSDefineVariable TestADIOSDefineVariable.cpp)
 target_link_libraries(TestADIOSDefineVariable adios2 gtest gtest_main)
 
-gtest_add_tests(TARGET TestADIOSInterfaceWrite)
-gtest_add_tests(TARGET TestADIOSDefineVariable)
\ No newline at end of file
+add_executable(TestADIOSDefineAttribute TestADIOSDefineAttribute.cpp)
+target_link_libraries(TestADIOSDefineAttribute adios2 gtest gtest_main)
+
+if(ADIOS2_HAVE_MPI)
+  target_include_directories(TestADIOSInterfaceWrite PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(TestADIOSInterfaceWrite ${MPI_C_LIBRARIES})
+
+  target_include_directories(TestADIOSDefineVariable PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(TestADIOSDefineVariable ${MPI_C_LIBRARIES})
+
+  target_include_directories(TestADIOSDefineAttribute PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(TestADIOSDefineAttribute ${MPI_C_LIBRARIES})
+
+  set(extra_test_args
+    EXEC_WRAPPER
+      ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS}
+  )
+endif()
+
+gtest_add_tests(TARGET TestADIOSInterfaceWrite ${extra_test_args})
+gtest_add_tests(TARGET TestADIOSDefineVariable ${extra_test_args})
+gtest_add_tests(TARGET TestADIOSDefineAttribute ${extra_test_args})
diff --git a/testing/adios2/interface/TestADIOSDefineAttribute.cpp b/testing/adios2/interface/TestADIOSDefineAttribute.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70d25a10d0a37a1d68c4c9bf039ad2922a655781
--- /dev/null
+++ b/testing/adios2/interface/TestADIOSDefineAttribute.cpp
@@ -0,0 +1,555 @@
+#include <cstdint>
+
+#include <iostream>
+#include <stdexcept>
+
+#include <adios2.h>
+
+#include <gtest/gtest.h>
+
+#include "../engine/SmallTestData.h"
+
+class ADIOSDefineAttributeTest : public ::testing::Test
+{
+public:
+    ADIOSDefineAttributeTest() : adios(true), io(adios.DeclareIO("TestIO")) {}
+
+    SmallTestData m_TestData;
+
+protected:
+    adios2::ADIOS adios;
+    adios2::IO &io;
+};
+
+TEST_F(ADIOSDefineAttributeTest, DefineAttributeNameException)
+{
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("attributeString") + std::to_string(mpiRank);
+
+    // Attribute should be unique per process
+    auto &attributeString1 = io.DefineAttribute<std::string>(name, "-1");
+
+    EXPECT_THROW(auto &attributeString2 =
+                     io.DefineAttribute<std::string>(name, "0"),
+                 std::invalid_argument);
+
+    EXPECT_THROW(auto &attributeString2 =
+                     io.GetAttribute<std::string>("NoExistingAttribute"),
+                 std::invalid_argument);
+
+    EXPECT_NO_THROW(auto &attributeString3 =
+                        io.GetAttribute<std::string>(name));
+}
+
+TEST_F(ADIOSDefineAttributeTest, DefineAttributeTypeByValue)
+{
+
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
+    // Define unique data for each process
+    SmallTestData currentTestData =
+        generateNewSmallTestData(m_TestData, 0, mpiRank, mpiSize);
+
+    std::string mpiRankString = std::to_string(mpiRank);
+    std::string s1_Single = std::string("s1_Single_") + mpiRankString;
+    std::string i8_Single = std::string("i8_Single_") + mpiRankString;
+    std::string i16_Single = std::string("i16_Single_") + mpiRankString;
+    std::string i32_Single = std::string("i32_Single_") + mpiRankString;
+    std::string i64_Single = std::string("i64_Single_") + mpiRankString;
+    std::string u8_Single = std::string("u8_Single_") + mpiRankString;
+    std::string u16_Single = std::string("u16_Single_") + mpiRankString;
+    std::string u32_Single = std::string("u32_Single_") + mpiRankString;
+    std::string u64_Single = std::string("u64_Single_") + mpiRankString;
+    std::string float_Single = std::string("float_Single_") + mpiRankString;
+    std::string double_Single = std::string("double_Single_") + mpiRankString;
+
+    // Define ADIOS global value
+    auto &attributeS1 =
+        io.DefineAttribute<std::string>(s1_Single, currentTestData.S1);
+    auto &attributeI8 =
+        io.DefineAttribute<int8_t>(i8_Single, currentTestData.I8.front());
+    auto &attributeI16 =
+        io.DefineAttribute<int16_t>(i16_Single, currentTestData.I16.front());
+    auto &attributeI32 =
+        io.DefineAttribute<int32_t>(i32_Single, currentTestData.I32.front());
+    auto &attributeI64 =
+        io.DefineAttribute<int64_t>(i64_Single, currentTestData.I64.front());
+
+    auto &attributeU8 =
+        io.DefineAttribute<uint8_t>(u8_Single, currentTestData.U8.front());
+    auto &attributeU16 =
+        io.DefineAttribute<uint16_t>(u16_Single, currentTestData.U16.front());
+    auto &attributeU32 =
+        io.DefineAttribute<uint32_t>(u32_Single, currentTestData.U32.front());
+    auto &attributeU64 =
+        io.DefineAttribute<uint64_t>(u64_Single, currentTestData.U64.front());
+
+    auto &attributeFloat =
+        io.DefineAttribute<float>(float_Single, currentTestData.R32.front());
+    auto &attributeDouble =
+        io.DefineAttribute<double>(double_Single, currentTestData.R64.front());
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(attributeS1),
+                                  adios2::Attribute<std::string> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI8),
+                                  adios2::Attribute<int8_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI16),
+                                  adios2::Attribute<int16_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI32),
+                                  adios2::Attribute<int32_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI64),
+                                  adios2::Attribute<int64_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU8),
+                                  adios2::Attribute<uint8_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU16),
+                                  adios2::Attribute<uint16_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU32),
+                                  adios2::Attribute<uint32_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU64),
+                                  adios2::Attribute<uint64_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeFloat),
+                                  adios2::Attribute<float> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeDouble),
+                                  adios2::Attribute<double> &>();
+
+    // Verify the members are correct
+    ASSERT_EQ(attributeS1.m_IsSingleValue, true);
+    ASSERT_EQ(attributeS1.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeS1.m_Name, s1_Single);
+    EXPECT_EQ(attributeS1.m_DataSingleValue, currentTestData.S1);
+    EXPECT_EQ(attributeS1.m_Elements, 1);
+    EXPECT_EQ(attributeS1.m_Type, "string");
+
+    ASSERT_EQ(attributeI8.m_IsSingleValue, true);
+    ASSERT_EQ(attributeI8.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeI8.m_Name, i8_Single);
+    EXPECT_EQ(attributeI8.m_DataSingleValue, currentTestData.I8.front());
+    EXPECT_EQ(attributeI8.m_Elements, 1);
+    EXPECT_EQ(attributeI8.m_Type, "signed char");
+
+    ASSERT_EQ(attributeI16.m_IsSingleValue, true);
+    ASSERT_EQ(attributeI16.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeI16.m_Name, i16_Single);
+    EXPECT_EQ(attributeI16.m_DataSingleValue, currentTestData.I16.front());
+    EXPECT_EQ(attributeI16.m_Elements, 1);
+    EXPECT_EQ(attributeI16.m_Type, "short");
+
+    ASSERT_EQ(attributeI32.m_IsSingleValue, true);
+    ASSERT_EQ(attributeI32.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeI32.m_Name, i32_Single);
+    EXPECT_EQ(attributeI32.m_DataSingleValue, currentTestData.I32.front());
+    EXPECT_EQ(attributeI32.m_Elements, 1);
+    EXPECT_EQ(attributeI32.m_Type, "int");
+
+    ASSERT_EQ(attributeI64.m_IsSingleValue, true);
+    ASSERT_EQ(attributeI64.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeI64.m_Name, i64_Single);
+    EXPECT_EQ(attributeI64.m_DataSingleValue, currentTestData.I64.front());
+    EXPECT_EQ(attributeI64.m_Elements, 1);
+    EXPECT_EQ(sizeof(attributeI64.m_DataSingleValue), 8);
+
+    ASSERT_EQ(attributeU8.m_IsSingleValue, true);
+    ASSERT_EQ(attributeU8.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeU8.m_Name, u8_Single);
+    EXPECT_EQ(attributeU8.m_DataSingleValue, currentTestData.U8.front());
+    EXPECT_EQ(attributeU8.m_Elements, 1);
+    EXPECT_EQ(attributeU8.m_Type, "unsigned char");
+
+    ASSERT_EQ(attributeU16.m_IsSingleValue, true);
+    ASSERT_EQ(attributeU16.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeU16.m_Name, u16_Single);
+    EXPECT_EQ(attributeU16.m_DataSingleValue, currentTestData.U16.front());
+    EXPECT_EQ(attributeU16.m_Elements, 1);
+    EXPECT_EQ(attributeU16.m_Type, "unsigned short");
+
+    ASSERT_EQ(attributeU32.m_IsSingleValue, true);
+    ASSERT_EQ(attributeU32.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeU32.m_Name, u32_Single);
+    EXPECT_EQ(attributeU32.m_DataSingleValue, currentTestData.U32.front());
+    EXPECT_EQ(attributeU32.m_Elements, 1);
+    EXPECT_EQ(attributeU32.m_Type, "unsigned int");
+
+    ASSERT_EQ(attributeU64.m_IsSingleValue, true);
+    ASSERT_EQ(attributeU64.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeU64.m_Name, u64_Single);
+    EXPECT_EQ(attributeU64.m_DataSingleValue, currentTestData.U64.front());
+    EXPECT_EQ(attributeU64.m_Elements, 1);
+    EXPECT_EQ(sizeof(attributeU64.m_DataSingleValue), 8);
+
+    ASSERT_EQ(attributeFloat.m_IsSingleValue, true);
+    ASSERT_EQ(attributeFloat.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeFloat.m_Name, float_Single);
+    EXPECT_EQ(attributeFloat.m_DataSingleValue, currentTestData.R32.front());
+    EXPECT_EQ(attributeFloat.m_Elements, 1);
+    EXPECT_EQ(attributeFloat.m_Type, "float");
+
+    ASSERT_EQ(attributeDouble.m_IsSingleValue, true);
+    ASSERT_EQ(attributeDouble.m_DataArray.empty(), true);
+    EXPECT_EQ(attributeDouble.m_Name, double_Single);
+    EXPECT_EQ(attributeDouble.m_DataSingleValue, currentTestData.R64.front());
+    EXPECT_EQ(attributeDouble.m_Elements, 1);
+    EXPECT_EQ(attributeDouble.m_Type, "double");
+}
+
+TEST_F(ADIOSDefineAttributeTest, DefineAttributeTypeByReference)
+{
+    int mpiRank = 0, mpiSize = 1;
+    size_t numberOfElements = 10;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
+    // Define unique data for each process
+    SmallTestData currentTestData =
+        generateNewSmallTestData(m_TestData, 0, mpiRank, mpiSize);
+
+    std::string mpiRankString = std::to_string(mpiRank);
+    std::string s3_Single = std::string("s3_Single_") + mpiRankString;
+    std::string i8_Single = std::string("i8_Single_") + mpiRankString;
+    std::string i16_Single = std::string("i16_Single_") + mpiRankString;
+    std::string i32_Single = std::string("i32_Single_") + mpiRankString;
+    std::string i64_Single = std::string("i64_Single_") + mpiRankString;
+    std::string u8_Single = std::string("u8_Single_") + mpiRankString;
+    std::string u16_Single = std::string("u16_Single_") + mpiRankString;
+    std::string u32_Single = std::string("u32_Single_") + mpiRankString;
+    std::string u64_Single = std::string("u64_Single_") + mpiRankString;
+    std::string float_Single = std::string("float_Single_") + mpiRankString;
+    std::string double_Single = std::string("double_Single_") + mpiRankString;
+
+    // Define ADIOS global value
+    auto &attributeS3 = io.DefineAttribute<std::string>(
+        s3_Single, currentTestData.S3.data(), 3);
+    auto &attributeI8 = io.DefineAttribute<int8_t>(
+        i8_Single, currentTestData.I8.data(), numberOfElements);
+    auto &attributeI16 = io.DefineAttribute<int16_t>(
+        i16_Single, currentTestData.I16.data(), numberOfElements);
+    auto &attributeI32 = io.DefineAttribute<int32_t>(
+        i32_Single, currentTestData.I32.data(), numberOfElements);
+    auto &attributeI64 = io.DefineAttribute<int64_t>(
+        i64_Single, currentTestData.I64.data(), numberOfElements);
+
+    auto &attributeU8 = io.DefineAttribute<uint8_t>(
+        u8_Single, currentTestData.U8.data(), numberOfElements);
+    auto &attributeU16 = io.DefineAttribute<uint16_t>(
+        u16_Single, currentTestData.U16.data(), numberOfElements);
+    auto &attributeU32 = io.DefineAttribute<uint32_t>(
+        u32_Single, currentTestData.U32.data(), numberOfElements);
+    auto &attributeU64 = io.DefineAttribute<uint64_t>(
+        u64_Single, currentTestData.U64.data(), numberOfElements);
+
+    auto &attributeFloat = io.DefineAttribute<float>(
+        float_Single, currentTestData.R32.data(), numberOfElements);
+    auto &attributeDouble = io.DefineAttribute<double>(
+        double_Single, currentTestData.R64.data(), numberOfElements);
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(attributeS3),
+                                  adios2::Attribute<std::string> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI8),
+                                  adios2::Attribute<int8_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI16),
+                                  adios2::Attribute<int16_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI32),
+                                  adios2::Attribute<int32_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI64),
+                                  adios2::Attribute<int64_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU8),
+                                  adios2::Attribute<uint8_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU16),
+                                  adios2::Attribute<uint16_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU32),
+                                  adios2::Attribute<uint32_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU64),
+                                  adios2::Attribute<uint64_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeFloat),
+                                  adios2::Attribute<float> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeDouble),
+                                  adios2::Attribute<double> &>();
+
+    // Verify the members are correct
+    ASSERT_EQ(attributeS3.m_IsSingleValue, false);
+    ASSERT_EQ(attributeS3.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeS3.m_Name, s3_Single);
+    EXPECT_EQ(attributeS3.m_Elements, 3);
+    EXPECT_EQ(attributeS3.m_Type, "string");
+
+    ASSERT_EQ(attributeI8.m_IsSingleValue, false);
+    ASSERT_EQ(attributeI8.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeI8.m_Name, i8_Single);
+    EXPECT_EQ(attributeI8.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeI8.m_Type, "signed char");
+
+    ASSERT_EQ(attributeI16.m_IsSingleValue, false);
+    ASSERT_EQ(attributeI16.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeI16.m_Name, i16_Single);
+    EXPECT_EQ(attributeI16.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeI16.m_Type, "short");
+
+    ASSERT_EQ(attributeI32.m_IsSingleValue, false);
+    ASSERT_EQ(attributeI32.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeI32.m_Name, i32_Single);
+    EXPECT_EQ(attributeI32.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeI32.m_Type, "int");
+
+    ASSERT_EQ(attributeI64.m_IsSingleValue, false);
+    ASSERT_EQ(attributeI64.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeI64.m_Name, i64_Single);
+    EXPECT_EQ(attributeI64.m_Elements, numberOfElements);
+    EXPECT_EQ(sizeof(attributeI64.m_DataSingleValue), 8);
+
+    ASSERT_EQ(attributeU8.m_IsSingleValue, false);
+    ASSERT_EQ(attributeU8.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeU8.m_Name, u8_Single);
+    EXPECT_EQ(attributeU8.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeU8.m_Type, "unsigned char");
+
+    ASSERT_EQ(attributeU16.m_IsSingleValue, false);
+    ASSERT_EQ(attributeU16.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeU16.m_Name, u16_Single);
+    EXPECT_EQ(attributeU16.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeU16.m_Type, "unsigned short");
+
+    ASSERT_EQ(attributeU32.m_IsSingleValue, false);
+    ASSERT_EQ(attributeU32.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeU32.m_Name, u32_Single);
+    EXPECT_EQ(attributeU32.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeU32.m_Type, "unsigned int");
+
+    ASSERT_EQ(attributeU64.m_IsSingleValue, false);
+    ASSERT_EQ(attributeU64.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeU64.m_Name, u64_Single);
+    EXPECT_EQ(attributeU64.m_Elements, numberOfElements);
+    EXPECT_EQ(sizeof(attributeU64.m_DataSingleValue), 8);
+
+    ASSERT_EQ(attributeFloat.m_IsSingleValue, false);
+    ASSERT_EQ(attributeFloat.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeFloat.m_Name, float_Single);
+    EXPECT_EQ(attributeFloat.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeFloat.m_Type, "float");
+
+    ASSERT_EQ(attributeDouble.m_IsSingleValue, false);
+    ASSERT_EQ(attributeDouble.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeDouble.m_Name, double_Single);
+    EXPECT_EQ(attributeDouble.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeDouble.m_Type, "double");
+
+    // Verify data
+    for (size_t index = 0; index < numberOfElements; index++)
+    {
+        EXPECT_EQ(attributeI8.m_DataArray[index], currentTestData.I8.at(index));
+        EXPECT_EQ(attributeI16.m_DataArray[index],
+                  currentTestData.I16.at(index));
+        EXPECT_EQ(attributeI32.m_DataArray[index],
+                  currentTestData.I32.at(index));
+        EXPECT_EQ(attributeU8.m_DataArray[index], currentTestData.U8.at(index));
+        EXPECT_EQ(attributeU16.m_DataArray[index],
+                  currentTestData.U16.at(index));
+        EXPECT_EQ(attributeU32.m_DataArray[index],
+                  currentTestData.U32.at(index));
+        EXPECT_EQ(attributeFloat.m_DataArray[index],
+                  currentTestData.R32.at(index));
+        EXPECT_EQ(attributeDouble.m_DataArray[index],
+                  currentTestData.R64.at(index));
+    }
+}
+
+TEST_F(ADIOSDefineAttributeTest, GetAttribute)
+{
+    int mpiRank = 0, mpiSize = 1;
+    size_t numberOfElements = 10;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+
+    // Define unique data for each process
+    SmallTestData currentTestData =
+        generateNewSmallTestData(m_TestData, 0, mpiRank, mpiSize);
+
+    std::string mpiRankString = std::to_string(mpiRank);
+    std::string s3_Single = std::string("s3_Array_") + mpiRankString;
+    std::string i8_Single = std::string("i8_Array_") + mpiRankString;
+    std::string i16_Single = std::string("i16_Array_") + mpiRankString;
+    std::string i32_Single = std::string("i32_Array_") + mpiRankString;
+    std::string i64_Single = std::string("i64_Array_") + mpiRankString;
+    std::string u8_Single = std::string("u8_Array_") + mpiRankString;
+    std::string u16_Single = std::string("u16_Array_") + mpiRankString;
+    std::string u32_Single = std::string("u32_Array_") + mpiRankString;
+    std::string u64_Single = std::string("u64_Array_") + mpiRankString;
+    std::string float_Single = std::string("float_Array_") + mpiRankString;
+    std::string double_Single = std::string("double_Array_") + mpiRankString;
+
+    // Define ADIOS global value
+    {
+        io.DefineAttribute<std::string>(s3_Single, currentTestData.S3.data(),
+                                        3);
+        io.DefineAttribute<int8_t>(i8_Single, currentTestData.I8.data(),
+                                   numberOfElements);
+        io.DefineAttribute<int16_t>(i16_Single, currentTestData.I16.data(),
+                                    numberOfElements);
+        io.DefineAttribute<int32_t>(i32_Single, currentTestData.I32.data(),
+                                    numberOfElements);
+        io.DefineAttribute<int64_t>(i64_Single, currentTestData.I64.data(),
+                                    numberOfElements);
+        io.DefineAttribute<uint8_t>(u8_Single, currentTestData.U8.data(),
+                                    numberOfElements);
+        io.DefineAttribute<uint16_t>(u16_Single, currentTestData.U16.data(),
+                                     numberOfElements);
+        io.DefineAttribute<uint32_t>(u32_Single, currentTestData.U32.data(),
+                                     numberOfElements);
+        io.DefineAttribute<uint64_t>(u64_Single, currentTestData.U64.data(),
+                                     numberOfElements);
+        io.DefineAttribute<float>(float_Single, currentTestData.R32.data(),
+                                  numberOfElements);
+        io.DefineAttribute<double>(double_Single, currentTestData.R64.data(),
+                                   numberOfElements);
+    }
+
+    auto &attributeS3 = io.GetAttribute<std::string>(s3_Single);
+    auto &attributeI8 = io.GetAttribute<int8_t>(i8_Single);
+    auto &attributeI16 = io.GetAttribute<int16_t>(i16_Single);
+    auto &attributeI32 = io.GetAttribute<int32_t>(i32_Single);
+    auto &attributeI64 = io.GetAttribute<int64_t>(i64_Single);
+    auto &attributeU8 = io.GetAttribute<uint8_t>(i8_Single);
+    auto &attributeU16 = io.GetAttribute<uint16_t>(i16_Single);
+    auto &attributeU32 = io.GetAttribute<uint32_t>(i32_Single);
+    auto &attributeU64 = io.GetAttribute<uint64_t>(i64_Single);
+    auto &attributeFloat = io.GetAttribute<float>(float_Single);
+    auto &attributeDouble = io.GetAttribute<double>(double_Single);
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(attributeS3),
+                                  adios2::Attribute<std::string> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI8),
+                                  adios2::Attribute<int8_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI16),
+                                  adios2::Attribute<int16_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI32),
+                                  adios2::Attribute<int32_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeI64),
+                                  adios2::Attribute<int64_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU8),
+                                  adios2::Attribute<uint8_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU16),
+                                  adios2::Attribute<uint16_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU32),
+                                  adios2::Attribute<uint32_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeU64),
+                                  adios2::Attribute<uint64_t> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeFloat),
+                                  adios2::Attribute<float> &>();
+    ::testing::StaticAssertTypeEq<decltype(attributeDouble),
+                                  adios2::Attribute<double> &>();
+
+    // Verify the members are correct
+    ASSERT_EQ(attributeS3.m_IsSingleValue, false);
+    ASSERT_EQ(attributeS3.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeS3.m_Name, s3_Single);
+    EXPECT_EQ(attributeS3.m_Elements, 3);
+    EXPECT_EQ(attributeS3.m_Type, "string");
+
+    ASSERT_EQ(attributeI8.m_IsSingleValue, false);
+    ASSERT_EQ(attributeI8.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeI8.m_Name, i8_Single);
+    EXPECT_EQ(attributeI8.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeI8.m_Type, "signed char");
+
+    ASSERT_EQ(attributeI16.m_IsSingleValue, false);
+    ASSERT_EQ(attributeI16.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeI16.m_Name, i16_Single);
+    EXPECT_EQ(attributeI16.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeI16.m_Type, "short");
+
+    ASSERT_EQ(attributeI32.m_IsSingleValue, false);
+    ASSERT_EQ(attributeI32.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeI32.m_Name, i32_Single);
+    EXPECT_EQ(attributeI32.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeI32.m_Type, "int");
+
+    ASSERT_EQ(attributeI64.m_IsSingleValue, false);
+    ASSERT_EQ(attributeI64.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeI64.m_Name, i64_Single);
+    EXPECT_EQ(attributeI64.m_Elements, numberOfElements);
+    EXPECT_EQ(sizeof(attributeI64.m_DataSingleValue), 8);
+
+    ASSERT_EQ(attributeU8.m_IsSingleValue, false);
+    ASSERT_EQ(attributeU8.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeU8.m_Name, u8_Single);
+    EXPECT_EQ(attributeU8.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeU8.m_Type, "unsigned char");
+
+    ASSERT_EQ(attributeU16.m_IsSingleValue, false);
+    ASSERT_EQ(attributeU16.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeU16.m_Name, u16_Single);
+    EXPECT_EQ(attributeU16.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeU16.m_Type, "unsigned short");
+
+    ASSERT_EQ(attributeU32.m_IsSingleValue, false);
+    ASSERT_EQ(attributeU32.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeU32.m_Name, u32_Single);
+    EXPECT_EQ(attributeU32.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeU32.m_Type, "unsigned int");
+
+    ASSERT_EQ(attributeU64.m_IsSingleValue, false);
+    ASSERT_EQ(attributeU64.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeU64.m_Name, u64_Single);
+    EXPECT_EQ(attributeU64.m_Elements, numberOfElements);
+    EXPECT_EQ(sizeof(attributeU64.m_DataSingleValue), 8);
+
+    ASSERT_EQ(attributeFloat.m_IsSingleValue, false);
+    ASSERT_EQ(attributeFloat.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeFloat.m_Name, float_Single);
+    EXPECT_EQ(attributeFloat.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeFloat.m_Type, "float");
+
+    ASSERT_EQ(attributeDouble.m_IsSingleValue, false);
+    ASSERT_EQ(attributeDouble.m_DataArray.empty(), false);
+    EXPECT_EQ(attributeDouble.m_Name, double_Single);
+    EXPECT_EQ(attributeDouble.m_Elements, numberOfElements);
+    EXPECT_EQ(attributeDouble.m_Type, "double");
+
+    // Verify data
+    for (size_t index = 0; index < numberOfElements; index++)
+    {
+        EXPECT_EQ(attributeI8.m_DataArray[index], currentTestData.I8.at(index));
+        EXPECT_EQ(attributeI16.m_DataArray[index],
+                  currentTestData.I16.at(index));
+        EXPECT_EQ(attributeI32.m_DataArray[index],
+                  currentTestData.I32.at(index));
+        EXPECT_EQ(attributeU8.m_DataArray[index], currentTestData.U8.at(index));
+        EXPECT_EQ(attributeU16.m_DataArray[index],
+                  currentTestData.U16.at(index));
+        EXPECT_EQ(attributeU32.m_DataArray[index],
+                  currentTestData.U32.at(index));
+        EXPECT_EQ(attributeFloat.m_DataArray[index],
+                  currentTestData.R32.at(index));
+        EXPECT_EQ(attributeDouble.m_DataArray[index],
+                  currentTestData.R64.at(index));
+    }
+}
+
+int main(int argc, char **argv)
+{
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Init(nullptr, nullptr);
+#endif
+
+    ::testing::InitGoogleTest(&argc, argv);
+    int result = RUN_ALL_TESTS();
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Finalize();
+#endif
+
+    return result;
+}
diff --git a/testing/adios2/interface/TestADIOSDefineVariable.cpp b/testing/adios2/interface/TestADIOSDefineVariable.cpp
index 100f05fc4e7df69a6512a749cf7b3cf1eb705260..e46c826d295680b4f809ae346ef0d644d4d68c42 100644
--- a/testing/adios2/interface/TestADIOSDefineVariable.cpp
+++ b/testing/adios2/interface/TestADIOSDefineVariable.cpp
@@ -23,8 +23,15 @@ protected:
 
 TEST_F(ADIOSDefineVariableTest, DefineGlobalValue)
 {
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("globalValue");
+
     // Define ADIOS global value
-    auto &globalvalue = io.DefineVariable<int>("globalvalue");
+    auto &globalvalue = io.DefineVariable<int>(name);
 
     // Verify the return type is as expected
     ::testing::StaticAssertTypeEq<decltype(globalvalue),
@@ -34,7 +41,7 @@ TEST_F(ADIOSDefineVariableTest, DefineGlobalValue)
     ASSERT_EQ(globalvalue.m_Shape.size(), 0);
     EXPECT_EQ(globalvalue.m_Start.size(), 0);
     EXPECT_EQ(globalvalue.m_Count.size(), 0);
-    EXPECT_EQ(globalvalue.m_Name, "globalvalue");
+    EXPECT_EQ(globalvalue.m_Name, name);
     EXPECT_EQ(globalvalue.m_Type, "int");
 }
 
@@ -59,10 +66,25 @@ TEST_F(ADIOSDefineVariableTest, DefineLocalValue)
 
 TEST_F(ADIOSDefineVariableTest, DefineGlobalArray)
 {
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    const std::size_t Nx(10), Ny(20), Nz(30);
+
+    adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize),
+                       static_cast<unsigned int>(Ny * mpiSize),
+                       static_cast<unsigned int>(Nz * mpiSize)};
+    adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank),
+                       static_cast<unsigned int>(Ny * mpiRank),
+                       static_cast<unsigned int>(Nz * mpiRank)};
+    adios2::Dims count{static_cast<unsigned int>(Nx),
+                       static_cast<unsigned int>(Ny),
+                       static_cast<unsigned int>(Nz)};
     // Define ADIOS global array
-    std::size_t n = 50;
-    auto &globalarray = io.DefineVariable<int>("globalarray", {100, n, 30},
-                                               {50, n / 2, 0}, {10, n / 2, 30});
+    auto &globalarray =
+        io.DefineVariable<int>("globalarray", shape, start, count);
 
     // Verify the return type is as expected
     ::testing::StaticAssertTypeEq<decltype(globalarray),
@@ -70,17 +92,17 @@ TEST_F(ADIOSDefineVariableTest, DefineGlobalArray)
 
     // Verify the dimensions, name, and type are correct
     ASSERT_EQ(globalarray.m_Shape.size(), 3);
-    EXPECT_EQ(globalarray.m_Shape[0], 100);
-    EXPECT_EQ(globalarray.m_Shape[1], n);
-    EXPECT_EQ(globalarray.m_Shape[2], 30);
+    EXPECT_EQ(globalarray.m_Shape[0], Nx * mpiSize);
+    EXPECT_EQ(globalarray.m_Shape[1], Ny * mpiSize);
+    EXPECT_EQ(globalarray.m_Shape[2], Nz * mpiSize);
     EXPECT_EQ(globalarray.m_Start.size(), 3);
-    EXPECT_EQ(globalarray.m_Start[0], 50);
-    EXPECT_EQ(globalarray.m_Start[1], n / 2);
-    EXPECT_EQ(globalarray.m_Start[2], 0);
+    EXPECT_EQ(globalarray.m_Start[0], Nx * mpiRank);
+    EXPECT_EQ(globalarray.m_Start[1], Ny * mpiRank);
+    EXPECT_EQ(globalarray.m_Start[2], Nz * mpiRank);
     EXPECT_EQ(globalarray.m_Count.size(), 3);
-    EXPECT_EQ(globalarray.m_Count[0], 10);
-    EXPECT_EQ(globalarray.m_Count[1], n / 2);
-    EXPECT_EQ(globalarray.m_Count[2], 30);
+    EXPECT_EQ(globalarray.m_Count[0], Nx);
+    EXPECT_EQ(globalarray.m_Count[1], Ny);
+    EXPECT_EQ(globalarray.m_Count[2], Nz);
     EXPECT_EQ(globalarray.m_Name, "globalarray");
     EXPECT_EQ(globalarray.m_Type, "int");
 }
@@ -88,8 +110,25 @@ TEST_F(ADIOSDefineVariableTest, DefineGlobalArray)
 TEST_F(ADIOSDefineVariableTest, DefineGlobalArrayWithSelections)
 {
     // Define ADIOS global array with postponed size definition in SetSelection
-    std::size_t n = 50;
-    auto &globalarray = io.DefineVariable<int>("globalarray", {100, n, 30});
+
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    const std::size_t Nx(10), Ny(20), Nz(30);
+
+    adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize),
+                       static_cast<unsigned int>(Ny * mpiSize),
+                       static_cast<unsigned int>(Nz * mpiSize)};
+    adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank),
+                       static_cast<unsigned int>(Ny * mpiRank),
+                       static_cast<unsigned int>(Nz * mpiRank)};
+    adios2::Dims count{static_cast<unsigned int>(Nx),
+                       static_cast<unsigned int>(Ny),
+                       static_cast<unsigned int>(Nz)};
+    // Define ADIOS global array
+    auto &globalarray = io.DefineVariable<int>("globalarray", shape);
 
     // Verify the return type is as expected
     ::testing::StaticAssertTypeEq<decltype(globalarray),
@@ -97,22 +136,22 @@ TEST_F(ADIOSDefineVariableTest, DefineGlobalArrayWithSelections)
 
     // Make a 3D selection to describe the local dimensions of the
     // variable we write and its offsets in the global spaces
-    adios2::SelectionBoundingBox sel({50, n / 2, 0}, {10, n / 2, 30});
+    adios2::SelectionBoundingBox sel(start, count);
     globalarray.SetSelection(sel);
 
     // Verify the dimensions, name, and type are correct
     ASSERT_EQ(globalarray.m_Shape.size(), 3);
-    EXPECT_EQ(globalarray.m_Shape[0], 100);
-    EXPECT_EQ(globalarray.m_Shape[1], n);
-    EXPECT_EQ(globalarray.m_Shape[2], 30);
+    EXPECT_EQ(globalarray.m_Shape[0], Nx * mpiSize);
+    EXPECT_EQ(globalarray.m_Shape[1], Ny * mpiSize);
+    EXPECT_EQ(globalarray.m_Shape[2], Nz * mpiSize);
     EXPECT_EQ(globalarray.m_Start.size(), 3);
-    EXPECT_EQ(globalarray.m_Start[0], 50);
-    EXPECT_EQ(globalarray.m_Start[1], n / 2);
-    EXPECT_EQ(globalarray.m_Start[2], 0);
+    EXPECT_EQ(globalarray.m_Start[0], Nx * mpiRank);
+    EXPECT_EQ(globalarray.m_Start[1], Ny * mpiRank);
+    EXPECT_EQ(globalarray.m_Start[2], Nz * mpiRank);
     EXPECT_EQ(globalarray.m_Count.size(), 3);
-    EXPECT_EQ(globalarray.m_Count[0], 10);
-    EXPECT_EQ(globalarray.m_Count[1], n / 2);
-    EXPECT_EQ(globalarray.m_Count[2], 30);
+    EXPECT_EQ(globalarray.m_Count[0], Nx);
+    EXPECT_EQ(globalarray.m_Count[1], Ny);
+    EXPECT_EQ(globalarray.m_Count[2], Nz);
     EXPECT_EQ(globalarray.m_Name, "globalarray");
     EXPECT_EQ(globalarray.m_Type, "int");
 }
@@ -120,30 +159,48 @@ TEST_F(ADIOSDefineVariableTest, DefineGlobalArrayWithSelections)
 TEST_F(ADIOSDefineVariableTest, DefineGlobalArrayConstantDims)
 {
     // Define ADIOS global array with locked-down dimensions
-    std::size_t n = 50;
-    auto &globalarray = io.DefineVariable<int>(
-        "globalarray", {100, n, 30}, {50, n / 2, 0}, {10, n / 2, 30}, true);
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    const std::size_t Nx(10), Ny(20), Nz(30);
+
+    adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize),
+                       static_cast<unsigned int>(Ny * mpiSize),
+                       static_cast<unsigned int>(Nz * mpiSize)};
+    adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank),
+                       static_cast<unsigned int>(Ny * mpiRank),
+                       static_cast<unsigned int>(Nz * mpiRank)};
+    adios2::Dims count{static_cast<unsigned int>(Nx),
+                       static_cast<unsigned int>(Ny),
+                       static_cast<unsigned int>(Nz)};
+    // Define ADIOS global array
+    auto &globalarray =
+        io.DefineVariable<int>("globalarray", shape, start, count, true);
 
     // Verify the return type is as expected
     ::testing::StaticAssertTypeEq<decltype(globalarray),
                                   adios2::Variable<int> &>();
 
-    adios2::SelectionBoundingBox sel({50, n / 2, 0}, {10, n / 2, 30});
+    // Make a 3D selection to describe the local dimensions of the
+    // variable we write and its offsets in the global spaces
+    adios2::SelectionBoundingBox sel(start, count);
     EXPECT_THROW(globalarray.SetSelection(sel), std::invalid_argument);
 
     // Verify the dimensions, name, and type are correct
     ASSERT_EQ(globalarray.m_Shape.size(), 3);
-    EXPECT_EQ(globalarray.m_Shape[0], 100);
-    EXPECT_EQ(globalarray.m_Shape[1], n);
-    EXPECT_EQ(globalarray.m_Shape[2], 30);
+    EXPECT_EQ(globalarray.m_Shape[0], Nx * mpiSize);
+    EXPECT_EQ(globalarray.m_Shape[1], Ny * mpiSize);
+    EXPECT_EQ(globalarray.m_Shape[2], Nz * mpiSize);
     EXPECT_EQ(globalarray.m_Start.size(), 3);
-    EXPECT_EQ(globalarray.m_Start[0], 50);
-    EXPECT_EQ(globalarray.m_Start[1], n / 2);
-    EXPECT_EQ(globalarray.m_Start[2], 0);
+    EXPECT_EQ(globalarray.m_Start[0], Nx * mpiRank);
+    EXPECT_EQ(globalarray.m_Start[1], Ny * mpiRank);
+    EXPECT_EQ(globalarray.m_Start[2], Nz * mpiRank);
     EXPECT_EQ(globalarray.m_Count.size(), 3);
-    EXPECT_EQ(globalarray.m_Count[0], 10);
-    EXPECT_EQ(globalarray.m_Count[1], n / 2);
-    EXPECT_EQ(globalarray.m_Count[2], 30);
+    EXPECT_EQ(globalarray.m_Count[0], Nx);
+    EXPECT_EQ(globalarray.m_Count[1], Ny);
+    EXPECT_EQ(globalarray.m_Count[2], Nz);
     EXPECT_EQ(globalarray.m_Name, "globalarray");
     EXPECT_EQ(globalarray.m_Type, "int");
 }
@@ -184,70 +241,92 @@ TEST_F(ADIOSDefineVariableTest, DefineLocalArray)
 
 TEST_F(ADIOSDefineVariableTest, DefineLocalArrayWithSelection)
 {
-    // Define ADIOS local array with postponed size definition in SetSelection
-    std::size_t n = 50;
-    auto &localarray = io.DefineVariable<int>(
-        "localarray", {}, {},
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    const std::size_t Nx(10), Ny(20), Nz(30);
+
+    adios2::Dims shape{static_cast<unsigned int>(Nx * mpiSize),
+                       static_cast<unsigned int>(Ny * mpiSize),
+                       static_cast<unsigned int>(Nz * mpiSize)};
+    adios2::Dims start{static_cast<unsigned int>(Nx * mpiRank),
+                       static_cast<unsigned int>(Ny * mpiRank),
+                       static_cast<unsigned int>(Nz * mpiRank)};
+    adios2::Dims count{static_cast<unsigned int>(Nx),
+                       static_cast<unsigned int>(Ny),
+                       static_cast<unsigned int>(Nz)};
+    // Define ADIOS global array
+    auto &localArray = io.DefineVariable<int>(
+        "localArray", {}, {},
         {adios2::UnknownDim, adios2::UnknownDim, adios2::UnknownDim});
 
     // Verify the return type is as expected
-    ::testing::StaticAssertTypeEq<decltype(localarray),
+    ::testing::StaticAssertTypeEq<decltype(localArray),
                                   adios2::Variable<int> &>();
-
-    ASSERT_EQ(localarray.m_Shape.size(), 0);
-    EXPECT_EQ(localarray.m_Start.size(), 0);
-    EXPECT_EQ(localarray.m_Count.size(), 3);
-    EXPECT_EQ(localarray.m_Count[0], 0);
-    EXPECT_EQ(localarray.m_Count[1], 0);
-    EXPECT_EQ(localarray.m_Count[2], 0);
-    EXPECT_EQ(localarray.m_Name, "localarray");
-    EXPECT_EQ(localarray.m_Type, "int");
-    EXPECT_EQ(localarray.m_ShapeID, adios2::ShapeID::LocalArray);
+    ASSERT_EQ(localArray.m_Shape.size(), 0);
+    EXPECT_EQ(localArray.m_Start.size(), 0);
+    EXPECT_EQ(localArray.m_Count.size(), 3);
+    EXPECT_EQ(localArray.m_Count[0], 0);
+    EXPECT_EQ(localArray.m_Count[1], 0);
+    EXPECT_EQ(localArray.m_Count[2], 0);
+    EXPECT_EQ(localArray.m_Name, "localArray");
+    EXPECT_EQ(localArray.m_Type, "int");
+    EXPECT_EQ(localArray.m_ShapeID, adios2::ShapeID::LocalArray);
 
     // Make a 3D selection to describe the local dimensions of the
     // variable we write
-    adios2::SelectionBoundingBox sel({}, {10, n / 2, 30});
-    localarray.SetSelection(sel);
+    adios2::SelectionBoundingBox sel({}, {Nx, Ny, Nz});
+    localArray.SetSelection(sel);
 
-    adios2::SelectionBoundingBox selbad({50, n / 2, 0}, {10, n / 2, 30});
-    EXPECT_THROW(localarray.SetSelection(selbad), std::invalid_argument);
+    adios2::SelectionBoundingBox selbad(start, count);
+    EXPECT_THROW(localArray.SetSelection(selbad), std::invalid_argument);
 
     // Verify the dimensions, name, and type are correct
-    ASSERT_EQ(localarray.m_Shape.size(), 0);
-    EXPECT_EQ(localarray.m_Start.size(), 0);
-    EXPECT_EQ(localarray.m_Count.size(), 3);
-    EXPECT_EQ(localarray.m_Count[0], 10);
-    EXPECT_EQ(localarray.m_Count[1], n / 2);
-    EXPECT_EQ(localarray.m_Count[2], 30);
-    EXPECT_EQ(localarray.m_Name, "localarray");
-    EXPECT_EQ(localarray.m_Type, "int");
-    EXPECT_EQ(localarray.m_ShapeID, adios2::ShapeID::LocalArray);
+    ASSERT_EQ(localArray.m_Shape.size(), 0);
+    EXPECT_EQ(localArray.m_Start.size(), 0);
+    EXPECT_EQ(localArray.m_Count.size(), 3);
+    EXPECT_EQ(localArray.m_Count[0], Nx);
+    EXPECT_EQ(localArray.m_Count[1], Ny);
+    EXPECT_EQ(localArray.m_Count[2], Nz);
+    EXPECT_EQ(localArray.m_Name, "localArray");
+    EXPECT_EQ(localArray.m_Type, "int");
+    EXPECT_EQ(localArray.m_ShapeID, adios2::ShapeID::LocalArray);
 }
 
 TEST_F(ADIOSDefineVariableTest, DefineLocalArrayConstantDims)
 {
-    // Define ADIOS local array with locked down dimensions
-    std::size_t n = 50;
-    auto &localarray =
-        io.DefineVariable<int>("localarray", {}, {}, {10, n / 2, 30}, true);
+    int mpiRank = 0, mpiSize = 1;
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    const std::size_t Nx(10), Ny(20), Nz(30);
+
+    adios2::Dims count{static_cast<unsigned int>(Nx),
+                       static_cast<unsigned int>(Ny),
+                       static_cast<unsigned int>(Nz)};
+    // Define ADIOS global array
+    auto &localArray =
+        io.DefineVariable<int>("localArray", {}, {}, count, true);
 
     // Verify the return type is as expected
-    ::testing::StaticAssertTypeEq<decltype(localarray),
+    ::testing::StaticAssertTypeEq<decltype(localArray),
                                   adios2::Variable<int> &>();
 
-    adios2::SelectionBoundingBox sel({}, {10, n / 2, 30});
-    EXPECT_THROW(localarray.SetSelection(sel), std::invalid_argument);
-
-    // Verify the dimensions, name, and type are correct
-    ASSERT_EQ(localarray.m_Shape.size(), 0);
-    EXPECT_EQ(localarray.m_Start.size(), 0);
-    EXPECT_EQ(localarray.m_Count.size(), 3);
-    EXPECT_EQ(localarray.m_Count[0], 10);
-    EXPECT_EQ(localarray.m_Count[1], n / 2);
-    EXPECT_EQ(localarray.m_Count[2], 30);
-    EXPECT_EQ(localarray.m_Name, "localarray");
-    EXPECT_EQ(localarray.m_Type, "int");
-    EXPECT_EQ(localarray.m_ShapeID, adios2::ShapeID::LocalArray);
+    adios2::SelectionBoundingBox sel({}, count);
+    EXPECT_THROW(localArray.SetSelection(sel), std::invalid_argument);
+
+    ASSERT_EQ(localArray.m_Shape.size(), 0);
+    EXPECT_EQ(localArray.m_Start.size(), 0);
+    EXPECT_EQ(localArray.m_Count.size(), 3);
+    EXPECT_EQ(localArray.m_Count[0], Nx);
+    EXPECT_EQ(localArray.m_Count[1], Ny);
+    EXPECT_EQ(localArray.m_Count[2], Nz);
+    EXPECT_EQ(localArray.m_Name, "localArray");
+    EXPECT_EQ(localArray.m_Type, "int");
+    EXPECT_EQ(localArray.m_ShapeID, adios2::ShapeID::LocalArray);
 }
 
 TEST_F(ADIOSDefineVariableTest, DefineLocalArrayInvalidOffsets)
diff --git a/testing/adios2/interface/TestADIOSInterfaceWrite.cpp b/testing/adios2/interface/TestADIOSInterfaceWrite.cpp
index 03f3132640587da5b8912fa3ff92060cb9c65e0d..ff5513afe6235ae2907ed947101dc804deae8f45 100644
--- a/testing/adios2/interface/TestADIOSInterfaceWrite.cpp
+++ b/testing/adios2/interface/TestADIOSInterfaceWrite.cpp
@@ -21,347 +21,544 @@ protected:
     adios2::IO &io;
 };
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarChar1x10)
+// 1x10
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_int8_t_1x10)
 {
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("i8_") + std::to_string(mpiRank);
+
     // Define ADIOS variables for each type
-    auto &var_i8 = io.DefineVariable<char>("i8", {}, {}, adios2::Dims{10});
+
+    auto &var_int8_t =
+        io.DefineVariable<int8_t>(name, {}, {}, adios2::Dims{10});
 
     // Verify the return type is as expected
-    ::testing::StaticAssertTypeEq<decltype(var_i8), adios2::Variable<char> &>();
+    ::testing::StaticAssertTypeEq<decltype(var_int8_t),
+                                  adios2::Variable<int8_t> &>();
 
     // Verify exceptions are thrown upon duplicate variable names
     EXPECT_THROW(auto &foo =
-                     io.DefineVariable<char>("i8", {}, {}, adios2::Dims{10}),
+                     io.DefineVariable<int8_t>(name, {}, {}, adios2::Dims{10}),
                  std::invalid_argument);
 
     // Verify the dimensions, name, and type are correct
-    ASSERT_EQ(var_i8.m_Shape.size(), 0);
-    EXPECT_EQ(var_i8.m_Start.size(), 0);
-    EXPECT_EQ(var_i8.m_Count.size(), 1);
-    EXPECT_EQ(var_i8.m_Count[0], 10);
-    EXPECT_EQ(var_i8.m_Name, "i8");
-    EXPECT_EQ(var_i8.m_Type, "char");
+    ASSERT_EQ(var_int8_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_int8_t.m_Start.size(), 0);
+    EXPECT_EQ(var_int8_t.m_Count.size(), 1);
+    EXPECT_EQ(var_int8_t.m_Count[0], 10);
+    EXPECT_EQ(var_int8_t.m_Name, name);
+    EXPECT_EQ(var_int8_t.m_Type, "signed char");
 }
 
-// Rinse  and repeat for remaining types
-TEST_F(ADIOSInterfaceWriteTest, DefineVarShort1x10)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_int16_t_1x10)
 {
-    auto &var_i16 = io.DefineVariable<short>("i16", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_i16),
-                                  adios2::Variable<short> &>();
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("i16_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_int16_t =
+        io.DefineVariable<int16_t>(name, {}, {}, adios2::Dims{10});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_int16_t),
+                                  adios2::Variable<int16_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
     EXPECT_THROW(auto &foo =
-                     io.DefineVariable<short>("i16", {}, {}, adios2::Dims{10}),
+                     io.DefineVariable<int16_t>(name, {}, {}, adios2::Dims{10}),
                  std::invalid_argument);
-    ASSERT_EQ(var_i16.m_Shape.size(), 0);
-    EXPECT_EQ(var_i16.m_Start.size(), 0);
-    EXPECT_EQ(var_i16.m_Count.size(), 1);
-    EXPECT_EQ(var_i16.m_Count[0], 10);
-    EXPECT_EQ(var_i16.m_Name, "i16");
-    EXPECT_EQ(var_i16.m_Type, "short");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_int16_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_int16_t.m_Start.size(), 0);
+    EXPECT_EQ(var_int16_t.m_Count.size(), 1);
+    EXPECT_EQ(var_int16_t.m_Count[0], 10);
+    EXPECT_EQ(var_int16_t.m_Name, name);
+    EXPECT_EQ(var_int16_t.m_Type, "short");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarInt1x10)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_int32_t_1x10)
 {
-    auto &var_i32 = io.DefineVariable<int>("i32", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_i32), adios2::Variable<int> &>();
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("i32_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_int32_t =
+        io.DefineVariable<int32_t>(name, {}, {}, adios2::Dims{10});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_int32_t),
+                                  adios2::Variable<int32_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
     EXPECT_THROW(auto &foo =
-                     io.DefineVariable<int>("i32", {}, {}, adios2::Dims{10}),
+                     io.DefineVariable<int32_t>(name, {}, {}, adios2::Dims{10}),
                  std::invalid_argument);
-    ASSERT_EQ(var_i32.m_Shape.size(), 0);
-    EXPECT_EQ(var_i32.m_Start.size(), 0);
-    EXPECT_EQ(var_i32.m_Count.size(), 1);
-    EXPECT_EQ(var_i32.m_Count[0], 10);
-    EXPECT_EQ(var_i32.m_Name, "i32");
-    EXPECT_EQ(var_i32.m_Type, "int");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_int32_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_int32_t.m_Start.size(), 0);
+    EXPECT_EQ(var_int32_t.m_Count.size(), 1);
+    EXPECT_EQ(var_int32_t.m_Count[0], 10);
+    EXPECT_EQ(var_int32_t.m_Name, name);
+    EXPECT_EQ(var_int32_t.m_Type, "int");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarLong1x10)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_int64_t_1x10)
 {
-    auto &var_u16 = io.DefineVariable<long>("u16", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_u16),
-                                  adios2::Variable<long> &>();
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("i64_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_int64_t =
+        io.DefineVariable<int64_t>(name, {}, {}, adios2::Dims{10});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_int64_t),
+                                  adios2::Variable<int64_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
     EXPECT_THROW(auto &foo =
-                     io.DefineVariable<long>("u16", {}, {}, adios2::Dims{10}),
+                     io.DefineVariable<int64_t>(name, {}, {}, adios2::Dims{10}),
                  std::invalid_argument);
-    ASSERT_EQ(var_u16.m_Shape.size(), 0);
-    EXPECT_EQ(var_u16.m_Start.size(), 0);
-    EXPECT_EQ(var_u16.m_Count.size(), 1);
-    EXPECT_EQ(var_u16.m_Count[0], 10);
-    EXPECT_EQ(var_u16.m_Name, "u16");
-    EXPECT_EQ(var_u16.m_Type, "long int");
-}
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarUChar1x10)
-{
-    auto &var_u8 =
-        io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_u8),
-                                  adios2::Variable<unsigned char> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<unsigned char>("u8", {}, {},
-                                                              adios2::Dims{10}),
-                 std::invalid_argument);
-    ASSERT_EQ(var_u8.m_Shape.size(), 0);
-    EXPECT_EQ(var_u8.m_Start.size(), 0);
-    EXPECT_EQ(var_u8.m_Count.size(), 1);
-    EXPECT_EQ(var_u8.m_Count[0], 10);
-    EXPECT_EQ(var_u8.m_Name, "u8");
-    EXPECT_EQ(var_u8.m_Type, "unsigned char");
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_int64_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_int64_t.m_Start.size(), 0);
+    EXPECT_EQ(var_int64_t.m_Count.size(), 1);
+    EXPECT_EQ(var_int64_t.m_Count[0], 10);
+    EXPECT_EQ(var_int64_t.m_Name, name);
+    EXPECT_EQ(var_int64_t.m_ElementSize, 8);
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarUShort1x10)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_uint8_t_1x10)
 {
-    auto &var_u16 =
-        io.DefineVariable<unsigned short>("u16", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_u16),
-                                  adios2::Variable<unsigned short> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<unsigned short>(
-                     "u16", {}, {}, adios2::Dims{10}),
-                 std::invalid_argument);
-    ASSERT_EQ(var_u16.m_Shape.size(), 0);
-    EXPECT_EQ(var_u16.m_Start.size(), 0);
-    EXPECT_EQ(var_u16.m_Count.size(), 1);
-    EXPECT_EQ(var_u16.m_Count[0], 10);
-    EXPECT_EQ(var_u16.m_Name, "u16");
-    EXPECT_EQ(var_u16.m_Type, "unsigned short");
-}
+    int mpiRank = 0, mpiSize = 1;
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarUInt1x10)
-{
-    auto &var_u32 =
-        io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_u32),
-                                  adios2::Variable<unsigned int> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<unsigned int>("u32", {}, {},
-                                                             adios2::Dims{10}),
-                 std::invalid_argument);
-    ASSERT_EQ(var_u32.m_Shape.size(), 0);
-    EXPECT_EQ(var_u32.m_Start.size(), 0);
-    EXPECT_EQ(var_u32.m_Count.size(), 1);
-    EXPECT_EQ(var_u32.m_Count[0], 10);
-    EXPECT_EQ(var_u32.m_Name, "u32");
-    EXPECT_EQ(var_u32.m_Type, "unsigned int");
-}
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("u8_") + std::to_string(mpiRank);
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarULong1x10)
-{
-    auto &var_u64 =
-        io.DefineVariable<unsigned long>("u64", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_u64),
-                                  adios2::Variable<unsigned long> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<unsigned long>("u64", {}, {},
-                                                              adios2::Dims{10}),
-                 std::invalid_argument);
-    ASSERT_EQ(var_u64.m_Shape.size(), 0);
-    EXPECT_EQ(var_u64.m_Start.size(), 0);
-    EXPECT_EQ(var_u64.m_Count.size(), 1);
-    EXPECT_EQ(var_u64.m_Count[0], 10);
-    EXPECT_EQ(var_u64.m_Name, "u64");
-    EXPECT_EQ(var_u64.m_Type, "unsigned long int");
-}
+    // Define ADIOS variables for each type
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarFloat1x10)
-{
-    auto &var_r32 = io.DefineVariable<float>("r32", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_r32),
-                                  adios2::Variable<float> &>();
+    auto &var_uint8_t =
+        io.DefineVariable<uint8_t>(name, {}, {}, adios2::Dims{10});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_uint8_t),
+                                  adios2::Variable<uint8_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
     EXPECT_THROW(auto &foo =
-                     io.DefineVariable<float>("r32", {}, {}, adios2::Dims{10}),
+                     io.DefineVariable<uint8_t>(name, {}, {}, adios2::Dims{10}),
                  std::invalid_argument);
-    ASSERT_EQ(var_r32.m_Shape.size(), 0);
-    EXPECT_EQ(var_r32.m_Start.size(), 0);
-    EXPECT_EQ(var_r32.m_Count.size(), 1);
-    EXPECT_EQ(var_r32.m_Count[0], 10);
-    EXPECT_EQ(var_r32.m_Name, "r32");
-    EXPECT_EQ(var_r32.m_Type, "float");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_uint8_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_uint8_t.m_Start.size(), 0);
+    EXPECT_EQ(var_uint8_t.m_Count.size(), 1);
+    EXPECT_EQ(var_uint8_t.m_Count[0], 10);
+    EXPECT_EQ(var_uint8_t.m_Name, name);
+    EXPECT_EQ(var_uint8_t.m_Type, "unsigned char");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarDouble1x10)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_uint16_t_1x10)
 {
-    auto &var_r64 = io.DefineVariable<double>("r64", {}, {}, adios2::Dims{10});
-    ::testing::StaticAssertTypeEq<decltype(var_r64),
-                                  adios2::Variable<double> &>();
-    EXPECT_THROW(auto &foo =
-                     io.DefineVariable<double>("r64", {}, {}, adios2::Dims{10}),
-                 std::invalid_argument);
-    ASSERT_EQ(var_r64.m_Shape.size(), 0);
-    EXPECT_EQ(var_r64.m_Start.size(), 0);
-    EXPECT_EQ(var_r64.m_Count.size(), 1);
-    EXPECT_EQ(var_r64.m_Count[0], 10);
-    EXPECT_EQ(var_r64.m_Name, "r64");
-    EXPECT_EQ(var_r64.m_Type, "double");
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("u16_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_uint16_t =
+        io.DefineVariable<uint16_t>(name, {}, {}, adios2::Dims{10});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_uint16_t),
+                                  adios2::Variable<uint16_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(
+        auto &foo = io.DefineVariable<uint16_t>(name, {}, {}, adios2::Dims{10}),
+        std::invalid_argument);
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_uint16_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_uint16_t.m_Start.size(), 0);
+    EXPECT_EQ(var_uint16_t.m_Count.size(), 1);
+    EXPECT_EQ(var_uint16_t.m_Count[0], 10);
+    EXPECT_EQ(var_uint16_t.m_Name, name);
+    EXPECT_EQ(var_uint16_t.m_Type, "unsigned short");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarChar2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_uint32_t_1x10)
 {
-    auto &var_i8 = io.DefineVariable<char>("i8", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_i8), adios2::Variable<char> &>();
-    EXPECT_THROW(auto &foo =
-                     io.DefineVariable<char>("i8", {}, {}, adios2::Dims{2, 5}),
-                 std::invalid_argument);
-    ASSERT_EQ(var_i8.m_Shape.size(), 0);
-    EXPECT_EQ(var_i8.m_Start.size(), 0);
-    EXPECT_EQ(var_i8.m_Count.size(), 2);
-    EXPECT_EQ(var_i8.m_Count[0], 2);
-    EXPECT_EQ(var_i8.m_Count[1], 5);
-    EXPECT_EQ(var_i8.m_Name, "i8");
-    EXPECT_EQ(var_i8.m_Type, "char");
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("u32_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_uint32_t =
+        io.DefineVariable<uint32_t>(name, {}, {}, adios2::Dims{10});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_uint32_t),
+                                  adios2::Variable<uint32_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(
+        auto &foo = io.DefineVariable<uint32_t>(name, {}, {}, adios2::Dims{10}),
+        std::invalid_argument);
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_uint32_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_uint32_t.m_Start.size(), 0);
+    EXPECT_EQ(var_uint32_t.m_Count.size(), 1);
+    EXPECT_EQ(var_uint32_t.m_Count[0], 10);
+    EXPECT_EQ(var_uint32_t.m_Name, name);
+    EXPECT_EQ(var_uint32_t.m_Type, "unsigned int");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarShort2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_uint64_t_1x10)
 {
-    auto &var_i16 = io.DefineVariable<short>("i16", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_i16),
-                                  adios2::Variable<short> &>();
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("u64_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_uint64_t =
+        io.DefineVariable<uint64_t>(name, {}, {}, adios2::Dims{10});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_uint64_t),
+                                  adios2::Variable<uint64_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
     EXPECT_THROW(
-        auto &foo = io.DefineVariable<short>("i16", {}, {}, adios2::Dims{2, 5}),
+        auto &foo = io.DefineVariable<uint64_t>(name, {}, {}, adios2::Dims{10}),
         std::invalid_argument);
-    ASSERT_EQ(var_i16.m_Shape.size(), 0);
-    EXPECT_EQ(var_i16.m_Start.size(), 0);
-    EXPECT_EQ(var_i16.m_Count.size(), 2);
-    EXPECT_EQ(var_i16.m_Count[0], 2);
-    EXPECT_EQ(var_i16.m_Count[1], 5);
-    EXPECT_EQ(var_i16.m_Name, "i16");
-    EXPECT_EQ(var_i16.m_Type, "short");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_uint64_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_uint64_t.m_Start.size(), 0);
+    EXPECT_EQ(var_uint64_t.m_Count.size(), 1);
+    EXPECT_EQ(var_uint64_t.m_Count[0], 10);
+    EXPECT_EQ(var_uint64_t.m_Name, name);
+    EXPECT_EQ(var_uint64_t.m_ElementSize, 8);
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarInt2x5)
+// Rinse  and repeat for remaining types
+
+// 2x5
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_int8_t_2x5)
 {
-    auto &var_i32 = io.DefineVariable<int>("i32", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_i32), adios2::Variable<int> &>();
-    EXPECT_THROW(auto &foo =
-                     io.DefineVariable<int>("i32", {}, {}, adios2::Dims{2, 5}),
-                 std::invalid_argument);
-    ASSERT_EQ(var_i32.m_Shape.size(), 0);
-    EXPECT_EQ(var_i32.m_Start.size(), 0);
-    EXPECT_EQ(var_i32.m_Count.size(), 2);
-    EXPECT_EQ(var_i32.m_Count[0], 2);
-    EXPECT_EQ(var_i32.m_Count[1], 5);
-    EXPECT_EQ(var_i32.m_Name, "i32");
-    EXPECT_EQ(var_i32.m_Type, "int");
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("i8_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_int8_t =
+        io.DefineVariable<int8_t>(name, {}, {}, adios2::Dims{2, 5});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_int8_t),
+                                  adios2::Variable<int8_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(
+        auto &foo = io.DefineVariable<int8_t>(name, {}, {}, adios2::Dims{2, 5}),
+        std::invalid_argument);
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_int8_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_int8_t.m_Start.size(), 0);
+    EXPECT_EQ(var_int8_t.m_Count.size(), 2);
+    EXPECT_EQ(var_int8_t.m_Count[0], 2);
+    EXPECT_EQ(var_int8_t.m_Count[1], 5);
+    EXPECT_EQ(var_int8_t.m_Name, name);
+    EXPECT_EQ(var_int8_t.m_Type, "signed char");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarLong2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_int16_t_2x5)
 {
-    auto &var_u16 = io.DefineVariable<long>("u16", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_u16),
-                                  adios2::Variable<long> &>();
-    EXPECT_THROW(auto &foo =
-                     io.DefineVariable<long>("u16", {}, {}, adios2::Dims{2, 5}),
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("i16_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_int16_t =
+        io.DefineVariable<int16_t>(name, {}, {}, adios2::Dims{2, 5});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_int16_t),
+                                  adios2::Variable<int16_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(auto &foo = io.DefineVariable<int16_t>(name, {}, {},
+                                                        adios2::Dims{2, 5}),
                  std::invalid_argument);
-    ASSERT_EQ(var_u16.m_Shape.size(), 0);
-    EXPECT_EQ(var_u16.m_Start.size(), 0);
-    EXPECT_EQ(var_u16.m_Count.size(), 2);
-    EXPECT_EQ(var_u16.m_Count[0], 2);
-    EXPECT_EQ(var_u16.m_Count[1], 5);
-    EXPECT_EQ(var_u16.m_Name, "u16");
-    EXPECT_EQ(var_u16.m_Type, "long int");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_int16_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_int16_t.m_Start.size(), 0);
+    EXPECT_EQ(var_int16_t.m_Count.size(), 2);
+    EXPECT_EQ(var_int16_t.m_Count[0], 2);
+    EXPECT_EQ(var_int16_t.m_Count[1], 5);
+    EXPECT_EQ(var_int16_t.m_Name, name);
+    EXPECT_EQ(var_int16_t.m_Type, "short");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarUChar2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_int32_t_2x5)
 {
-    auto &var_u8 =
-        io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_u8),
-                                  adios2::Variable<unsigned char> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<unsigned char>(
-                     "u8", {}, {}, adios2::Dims{2, 5}),
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("i32_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_int32_t =
+        io.DefineVariable<int32_t>(name, {}, {}, adios2::Dims{2, 5});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_int32_t),
+                                  adios2::Variable<int32_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(auto &foo = io.DefineVariable<int32_t>(name, {}, {},
+                                                        adios2::Dims{2, 5}),
                  std::invalid_argument);
-    ASSERT_EQ(var_u8.m_Shape.size(), 0);
-    EXPECT_EQ(var_u8.m_Start.size(), 0);
-    EXPECT_EQ(var_u8.m_Count.size(), 2);
-    EXPECT_EQ(var_u8.m_Count[0], 2);
-    EXPECT_EQ(var_u8.m_Count[1], 5);
-    EXPECT_EQ(var_u8.m_Name, "u8");
-    EXPECT_EQ(var_u8.m_Type, "unsigned char");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_int32_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_int32_t.m_Start.size(), 0);
+    EXPECT_EQ(var_int32_t.m_Count.size(), 2);
+    EXPECT_EQ(var_int32_t.m_Count[0], 2);
+    EXPECT_EQ(var_int32_t.m_Count[1], 5);
+    EXPECT_EQ(var_int32_t.m_Name, name);
+    EXPECT_EQ(var_int32_t.m_Type, "int");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarUShort2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_int64_t_2x5)
 {
-    auto &var_u16 =
-        io.DefineVariable<unsigned short>("u16", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_u16),
-                                  adios2::Variable<unsigned short> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<unsigned short>(
-                     "u16", {}, {}, adios2::Dims{2, 5}),
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("i64_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_int64_t =
+        io.DefineVariable<int64_t>(name, {}, {}, adios2::Dims{2, 5});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_int64_t),
+                                  adios2::Variable<int64_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(auto &foo = io.DefineVariable<int64_t>(name, {}, {},
+                                                        adios2::Dims{2, 5}),
                  std::invalid_argument);
-    ASSERT_EQ(var_u16.m_Shape.size(), 0);
-    EXPECT_EQ(var_u16.m_Start.size(), 0);
-    EXPECT_EQ(var_u16.m_Count.size(), 2);
-    EXPECT_EQ(var_u16.m_Count[0], 2);
-    EXPECT_EQ(var_u16.m_Count[1], 5);
-    EXPECT_EQ(var_u16.m_Name, "u16");
-    EXPECT_EQ(var_u16.m_Type, "unsigned short");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_int64_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_int64_t.m_Start.size(), 0);
+    EXPECT_EQ(var_int64_t.m_Count.size(), 2);
+    EXPECT_EQ(var_int64_t.m_Count[0], 2);
+    EXPECT_EQ(var_int64_t.m_Count[1], 5);
+    EXPECT_EQ(var_int64_t.m_Name, name);
+    EXPECT_EQ(var_int64_t.m_ElementSize, 8);
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarUInt2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_uint8_t_2x5)
 {
-    auto &var_u32 =
-        io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_u32),
-                                  adios2::Variable<unsigned int> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<unsigned int>(
-                     "u32", {}, {}, adios2::Dims{2, 5}),
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("u8_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_uint8_t =
+        io.DefineVariable<uint8_t>(name, {}, {}, adios2::Dims{2, 5});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_uint8_t),
+                                  adios2::Variable<uint8_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(auto &foo = io.DefineVariable<uint8_t>(name, {}, {},
+                                                        adios2::Dims{2, 5}),
                  std::invalid_argument);
-    ASSERT_EQ(var_u32.m_Shape.size(), 0);
-    EXPECT_EQ(var_u32.m_Start.size(), 0);
-    EXPECT_EQ(var_u32.m_Count.size(), 2);
-    EXPECT_EQ(var_u32.m_Count[0], 2);
-    EXPECT_EQ(var_u32.m_Count[1], 5);
-    EXPECT_EQ(var_u32.m_Name, "u32");
-    EXPECT_EQ(var_u32.m_Type, "unsigned int");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_uint8_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_uint8_t.m_Start.size(), 0);
+    EXPECT_EQ(var_uint8_t.m_Count.size(), 2);
+    EXPECT_EQ(var_uint8_t.m_Count[0], 2);
+    EXPECT_EQ(var_uint8_t.m_Count[1], 5);
+    EXPECT_EQ(var_uint8_t.m_Name, name);
+    EXPECT_EQ(var_uint8_t.m_Type, "unsigned char");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarULong2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_uint16_t_2x5)
 {
-    auto &var_u64 =
-        io.DefineVariable<unsigned long>("u64", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_u64),
-                                  adios2::Variable<unsigned long> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<unsigned long>(
-                     "u64", {}, {}, adios2::Dims{2, 5}),
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("u16_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_uint16_t =
+        io.DefineVariable<uint16_t>(name, {}, {}, adios2::Dims{2, 5});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_uint16_t),
+                                  adios2::Variable<uint16_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(auto &foo = io.DefineVariable<uint16_t>(name, {}, {},
+                                                         adios2::Dims{2, 5}),
                  std::invalid_argument);
-    ASSERT_EQ(var_u64.m_Shape.size(), 0);
-    EXPECT_EQ(var_u64.m_Start.size(), 0);
-    EXPECT_EQ(var_u64.m_Count.size(), 2);
-    EXPECT_EQ(var_u64.m_Count[0], 2);
-    EXPECT_EQ(var_u64.m_Count[1], 5);
-    EXPECT_EQ(var_u64.m_Name, "u64");
-    EXPECT_EQ(var_u64.m_Type, "unsigned long int");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_uint16_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_uint16_t.m_Start.size(), 0);
+    EXPECT_EQ(var_uint16_t.m_Count.size(), 2);
+    EXPECT_EQ(var_uint16_t.m_Count[0], 2);
+    EXPECT_EQ(var_uint16_t.m_Count[1], 5);
+    EXPECT_EQ(var_uint16_t.m_Name, name);
+    EXPECT_EQ(var_uint16_t.m_Type, "unsigned short");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarFloat2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_uint32_t_2x5)
 {
-    auto &var_r32 = io.DefineVariable<float>("r32", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_r32),
-                                  adios2::Variable<float> &>();
-    EXPECT_THROW(
-        auto &foo = io.DefineVariable<float>("r32", {}, {}, adios2::Dims{2, 5}),
-        std::invalid_argument);
-    ASSERT_EQ(var_r32.m_Shape.size(), 0);
-    EXPECT_EQ(var_r32.m_Start.size(), 0);
-    EXPECT_EQ(var_r32.m_Count.size(), 2);
-    EXPECT_EQ(var_r32.m_Count[0], 2);
-    EXPECT_EQ(var_r32.m_Count[1], 5);
-    EXPECT_EQ(var_r32.m_Name, "r32");
-    EXPECT_EQ(var_r32.m_Type, "float");
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("u32_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_uint32_t =
+        io.DefineVariable<uint32_t>(name, {}, {}, adios2::Dims{2, 5});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_uint32_t),
+                                  adios2::Variable<uint32_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(auto &foo = io.DefineVariable<uint32_t>(name, {}, {},
+                                                         adios2::Dims{2, 5}),
+                 std::invalid_argument);
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_uint32_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_uint32_t.m_Start.size(), 0);
+    EXPECT_EQ(var_uint32_t.m_Count.size(), 2);
+    EXPECT_EQ(var_uint32_t.m_Count[0], 2);
+    EXPECT_EQ(var_uint32_t.m_Count[1], 5);
+    EXPECT_EQ(var_uint32_t.m_Name, name);
+    EXPECT_EQ(var_uint32_t.m_Type, "unsigned int");
 }
 
-TEST_F(ADIOSInterfaceWriteTest, DefineVarDouble2x5)
+TEST_F(ADIOSInterfaceWriteTest, DefineVar_uint64_t_2x5)
 {
-    auto &var_r64 =
-        io.DefineVariable<double>("r64", {}, {}, adios2::Dims{2, 5});
-    ::testing::StaticAssertTypeEq<decltype(var_r64),
-                                  adios2::Variable<double> &>();
-    EXPECT_THROW(auto &foo = io.DefineVariable<double>("r64", {}, {},
-                                                       adios2::Dims{2, 5}),
+    int mpiRank = 0, mpiSize = 1;
+
+#ifdef ADIOS2_HAVE_MPI
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
+#endif
+    std::string name = std::string("u64_") + std::to_string(mpiRank);
+
+    // Define ADIOS variables for each type
+
+    auto &var_uint64_t =
+        io.DefineVariable<uint64_t>(name, {}, {}, adios2::Dims{2, 5});
+
+    // Verify the return type is as expected
+    ::testing::StaticAssertTypeEq<decltype(var_uint64_t),
+                                  adios2::Variable<uint64_t> &>();
+
+    // Verify exceptions are thrown upon duplicate variable names
+    EXPECT_THROW(auto &foo = io.DefineVariable<uint64_t>(name, {}, {},
+                                                         adios2::Dims{2, 5}),
                  std::invalid_argument);
-    ASSERT_EQ(var_r64.m_Shape.size(), 0);
-    EXPECT_EQ(var_r64.m_Start.size(), 0);
-    EXPECT_EQ(var_r64.m_Count.size(), 2);
-    EXPECT_EQ(var_r64.m_Count[0], 2);
-    EXPECT_EQ(var_r64.m_Count[1], 5);
-    EXPECT_EQ(var_r64.m_Name, "r64");
-    EXPECT_EQ(var_r64.m_Type, "double");
+
+    // Verify the dimensions, name, and type are correct
+    ASSERT_EQ(var_uint64_t.m_Shape.size(), 0);
+    EXPECT_EQ(var_uint64_t.m_Start.size(), 0);
+    EXPECT_EQ(var_uint64_t.m_Count.size(), 2);
+    EXPECT_EQ(var_uint64_t.m_Count[0], 2);
+    EXPECT_EQ(var_uint64_t.m_Count[1], 5);
+    EXPECT_EQ(var_uint64_t.m_Name, name);
+    EXPECT_EQ(var_uint64_t.m_ElementSize, 8);
 }
 
 int main(int argc, char **argv)
diff --git a/testing/adios2/xml/TestXMLConfig.cpp b/testing/adios2/xml/TestXMLConfig.cpp
index 5d3cb161b01080cd33a19083516deba70d3caaaa..72bbdffe52e229c78536220221a52d0058064dfb 100644
--- a/testing/adios2/xml/TestXMLConfig.cpp
+++ b/testing/adios2/xml/TestXMLConfig.cpp
@@ -25,6 +25,7 @@ protected:
 TEST_F(XMLConfigTest, TwoIOs)
 {
     std::string configFile = configDir + "/config1.xml";
+
 #ifdef ADIOS2_HAVE_MPI
     adios2::ADIOS adios(configFile, MPI_COMM_WORLD, adios2::DebugON);
 #else
diff --git a/testing/adios2/xml/config1.xml b/testing/adios2/xml/config1.xml
index 9afe2d91037b232930a37ad48ad962d88dd27d07..f4d644631d02988df4d6c31248f3bab323e3c415 100644
--- a/testing/adios2/xml/config1.xml
+++ b/testing/adios2/xml/config1.xml
@@ -9,7 +9,7 @@
             <parameter key="BufferGrowthFactor" value="2"/>
         </engine>
         <transport type="File">
-            <parameter key="Library" value="POSIX"/>
+            <parameter key="Library" value="fstream"/>
             <parameter key="ProfileUnits" value="Milliseconds"/>
         </transport>
     </io>
diff --git a/thirdparty/GTest/CMakeLists.txt b/thirdparty/GTest/CMakeLists.txt
index bdac080ca6617d26fd27180c2420717e5f8d152b..8e7b4e21bf144f336ebf4acb2bec6f9f79a2fb33 100644
--- a/thirdparty/GTest/CMakeLists.txt
+++ b/thirdparty/GTest/CMakeLists.txt
@@ -1,5 +1,6 @@
-set(BUILD_GTEST ON)
-set(BUILD_GMOCK OFF)
+set(BUILD_GTEST ON CACHE INTERNAL "")
+set(BUILD_GMOCK OFF CACHE INTERNAL "")
 set(gtest_disable_pthreads ON)
 
+set(CMAKE_POLICY_DEFAULT_CMP0042 NEW)
 add_subdirectory(googletest EXCLUDE_FROM_ALL)
diff --git a/thirdparty/KWSys/CMakeLists.txt b/thirdparty/KWSys/CMakeLists.txt
index 600c5c9b4b33e07316486b5f2aaba8ac85d00fe8..952fd5e0ca8ce201a9e7c17ee9e6365574e32f2e 100644
--- a/thirdparty/KWSys/CMakeLists.txt
+++ b/thirdparty/KWSys/CMakeLists.txt
@@ -2,9 +2,14 @@ set(KWSYS_NAMESPACE adios2sys)
 set(KWSYS_USE_DynamicLoader ON)
 set(KWSYS_USE_RegularExpression ON)
 set(KWSYS_USE_SystemTools ON)
-set(KWSYS_BUILD_SHARED ${ADIOS2_BUILD_SHARED_LIBS})
+if(WIN32)
+  set(KWSYS_BUILD_SHARED OFF)
+else()
+  set(KWSYS_BUILD_SHARED ${BUILD_SHARED_LIBS})
+endif()
 set(KWSYS_SPLIT_OBJECTS_FROM_INTERFACE ON)
 set(KWSYS_INSTALL_EXPORT_NAME adios2Exports)
 set(KWSYS_INSTALL_LIB_DIR ${CMAKE_INSTALL_LIBDIR})
 
 add_subdirectory(adios2sys)
+add_library(adios2sys_interface ALIAS adios2sys_private)
diff --git a/thirdparty/KWSys/adios2sys/CMakeLists.txt b/thirdparty/KWSys/adios2sys/CMakeLists.txt
index e9bae831c62bfbac1020833ef1cae45303be1a67..13375df81790e5f5589220f749a171d9a920624e 100644
--- a/thirdparty/KWSys/adios2sys/CMakeLists.txt
+++ b/thirdparty/KWSys/adios2sys/CMakeLists.txt
@@ -23,18 +23,18 @@
 #  KWSYS_SPLIT_OBJECTS_FROM_INTERFACE
 #                    = Instead of creating a single ${KWSYS_NAMESPACE} library
 #                      target, create three separate targets:
+#                        ${KWSYS_NAMESPACE}
+#                          - An INTERFACE library only containing usage
+#                            requirements.
 #                        ${KWSYS_NAMESPACE}_objects
 #                          - An OBJECT library for the built kwsys objects.
-#                        ${KWSYS_NAMESPACE}_interface
-#                          - An INTERFACE library for the usage requirements.
-#                        ${KWSYS_NAMESPACE}
+#                            Note: This is omitted from the install rules
+#                        ${KWSYS_NAMESPACE}_private
 #                          - An INTERFACE library combining both that is
 #                            appropriate for use with PRIVATE linking in
 #                            target_link_libraries. Because of how interface
 #                            properties propagate, this target is not suitable
 #                            for use with PUBLIC or INTERFACE linking.
-#                      Note: only the ${KWSYS_NAMESPACE}_interface library will
-#                      be installed
 #
 #    Example:
 #
@@ -438,6 +438,10 @@ ELSE()
   SET(KWSYS_LIBRARY_TYPE STATIC)
 ENDIF()
 
+IF(NOT KWSYS_DEFINE_SYMBOL)
+  SET(KWSYS_DEFINE_SYMBOL ${KWSYS_NAMESPACE}_EXPORTS)
+ENDIF()
+
 #-----------------------------------------------------------------------------
 # Configure some implementation details.
 
@@ -449,6 +453,11 @@ SET_SOURCE_FILES_PROPERTIES(ProcessUNIX.c System.c PROPERTIES
   COMPILE_FLAGS "-DKWSYS_C_HAS_PTRDIFF_T=${KWSYS_C_HAS_PTRDIFF_T} -DKWSYS_C_HAS_SSIZE_T=${KWSYS_C_HAS_SSIZE_T}"
   )
 
+IF(DEFINED KWSYS_PROCESS_USE_SELECT)
+  GET_PROPERTY(ProcessUNIX_FLAGS SOURCE ProcessUNIX.c PROPERTY COMPILE_FLAGS)
+  SET_PROPERTY(SOURCE ProcessUNIX.c PROPERTY COMPILE_FLAGS "${ProcessUNIX_FLAGS} -DKWSYSPE_USE_SELECT=${KWSYSPE_USE_SELECT}")
+ENDIF()
+
 IF(KWSYS_USE_DynamicLoader)
   GET_PROPERTY(KWSYS_SUPPORTS_SHARED_LIBS GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS)
   IF(KWSYS_SUPPORTS_SHARED_LIBS)
@@ -815,15 +824,18 @@ ENDFOREACH()
 # Add the library with the configured name and list of sources.
 IF(KWSYS_C_SRCS OR KWSYS_CXX_SRCS)
   IF(KWSYS_SPLIT_OBJECTS_FROM_INTERFACE)
-    SET(KWSYS_TARGET_INTERFACE ${KWSYS_NAMESPACE}_interface)
+    SET(KWSYS_TARGET_INTERFACE ${KWSYS_NAMESPACE})
     SET(KWSYS_TARGET_OBJECT ${KWSYS_NAMESPACE}_objects)
-    SET(KWSYS_TARGET_LINK ${KWSYS_NAMESPACE})
+    SET(KWSYS_TARGET_LINK ${KWSYS_NAMESPACE}_private)
+    SET(KWSYS_TARGET_INSTALL ${KWSYS_TARGET_INTERFACE} ${KWSYS_TARGET_LINK})
     SET(KWSYS_LINK_DEPENDENCY INTERFACE)
     ADD_LIBRARY(${KWSYS_TARGET_OBJECT} OBJECT
       ${KWSYS_C_SRCS} ${KWSYS_CXX_SRCS})
     IF(KWSYS_BUILD_SHARED)
       SET_PROPERTY(TARGET ${KWSYS_TARGET_OBJECT} PROPERTY
         POSITION_INDEPENDENT_CODE TRUE)
+      TARGET_COMPILE_DEFINITIONS(${KWSYS_TARGET_OBJECT} PRIVATE
+        ${KWSYS_DEFINE_SYMBOL})
     ENDIF()
     ADD_LIBRARY(${KWSYS_TARGET_INTERFACE} INTERFACE)
     ADD_LIBRARY(${KWSYS_TARGET_LINK} INTERFACE)
@@ -835,6 +847,7 @@ IF(KWSYS_C_SRCS OR KWSYS_CXX_SRCS)
     SET(KWSYS_TARGET_INTERFACE ${KWSYS_NAMESPACE})
     SET(KWSYS_TARGET_OBJECT ${KWSYS_NAMESPACE})
     SET(KWSYS_TARGET_LINK ${KWSYS_NAMESPACE})
+    set(KWSYS_TARGET_INSTALL ${KWSYS_TARGET_LINK})
     SET(KWSYS_LINK_DEPENDENCY PUBLIC)
     ADD_LIBRARY(${KWSYS_TARGET_INTERFACE} ${KWSYS_LIBRARY_TYPE}
       ${KWSYS_C_SRCS} ${KWSYS_CXX_SRCS})
@@ -895,21 +908,25 @@ IF(KWSYS_C_SRCS OR KWSYS_CXX_SRCS)
 
   # Create an install target for the library.
   IF(KWSYS_INSTALL_LIBRARY_RULE)
-    INSTALL(TARGETS ${KWSYS_TARGET_LINK} ${KWSYS_TARGET_INTERFACE} ${KWSYS_INSTALL_LIBRARY_RULE})
+    INSTALL(TARGETS ${KWSYS_TARGET_INSTALL} ${KWSYS_INSTALL_LIBRARY_RULE})
   ENDIF()
 ENDIF()
 
 # Add a C-only library if requested.
 IF(KWSYS_ENABLE_C AND KWSYS_C_SRCS)
  IF(KWSYS_SPLIT_OBJECTS_FROM_INTERFACE)
-    SET(KWSYS_TARGET_C_INTERFACE ${KWSYS_NAMESPACE}_c_interface)
-    SET(KWSYS_TARGET_C_OBJECT ${KWSYS_NAMESPACE}_c_object)
-    SET(KWSYS_TARGET_C_LINK ${KWSYS_NAMESPACE}_c)
+    SET(KWSYS_TARGET_C_INTERFACE ${KWSYS_NAMESPACE}_c)
+    SET(KWSYS_TARGET_C_OBJECT ${KWSYS_NAMESPACE}_c_objects)
+    SET(KWSYS_TARGET_C_LINK ${KWSYS_NAMESPACE}_c_private)
+    SET(KWSYS_TARGET_C_INSTALL
+      ${KWSYS_TARGET_C_INTERFACE} ${KWSYS_TARGET_C_LINK})
     SET(KWSYS_LINK_DEPENDENCY INTERFACE)
     ADD_LIBRARY(${KWSYS_TARGET_C_OBJECT} OBJECT ${KWSYS_C_SRCS})
     IF(KWSYS_BUILD_SHARED)
       SET_PROPERTY(TARGET ${KWSYS_TARGET_C_OBJECT} PROPERTY
         POSITION_INDEPENDENT_CODE TRUE)
+      TARGET_COMPILE_DEFINITIONS(${KWSYS_TARGET_C_OBJECT} PRIVATE
+        ${KWSYS_DEFINE_SYMBOL})
     ENDIF()
     ADD_LIBRARY(${KWSYS_TARGET_C_INTERFACE} INTERFACE)
     ADD_LIBRARY(${KWSYS_TARGET_C_LINK} INTERFACE)
@@ -921,6 +938,7 @@ IF(KWSYS_ENABLE_C AND KWSYS_C_SRCS)
     SET(KWSYS_TARGET_C_INTERFACE ${KWSYS_NAMESPACE}_c)
     SET(KWSYS_TARGET_C_OBJECT ${KWSYS_NAMESPACE}_c)
     SET(KWSYS_TARGET_C_LINK ${KWSYS_NAMESPACE}_c)
+    SET(KWSYS_TARGET_C_INSTALL ${KWSYS_TARGET_C_LINK})
     SET(KWSYS_LINK_DEPENDENCY PUBLIC)
     ADD_LIBRARY(${KWSYS_TARGET_C_INTERFACE} ${KWSYS_LIBRARY_TYPE}
       ${KWSYS_C_SRCS})
@@ -946,8 +964,8 @@ IF(KWSYS_ENABLE_C AND KWSYS_C_SRCS)
 
   # Create an install target for the library.
   IF(KWSYS_INSTALL_LIBRARY_RULE)
-    INSTALL(TARGETS ${KWSYS_TARGET_C_INTERFACE} ${KWSYS_INSTALL_LIBRARY_RULE})
-   ENDIF()
+    INSTALL(TARGETS ${KWSYS_TARGET_C_INSTALL})
+  ENDIF()
 ENDIF()
 
 # For building kwsys itself, we use a macro defined on the command
@@ -1022,6 +1040,7 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
       testSystemTools
       testCommandLineArguments
       testCommandLineArguments1
+      testDirectory
       )
     IF(KWSYS_STL_HAS_WSTRING)
       SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS}
diff --git a/thirdparty/KWSys/adios2sys/Configure.h.in b/thirdparty/KWSys/adios2sys/Configure.h.in
index 0afcae781d6ae46d6e27b3864948e6915119e542..921e1ae599a414a66ed05ee4c72dd1e8a9205d9b 100644
--- a/thirdparty/KWSys/adios2sys/Configure.h.in
+++ b/thirdparty/KWSys/adios2sys/Configure.h.in
@@ -71,7 +71,7 @@
 /* Setup the export macro.  */
 #if @KWSYS_BUILD_SHARED@
 #if defined(_WIN32) || defined(__CYGWIN__)
-#if defined(@KWSYS_NAMESPACE@_EXPORTS)
+#if defined(@KWSYS_DEFINE_SYMBOL@)
 #define @KWSYS_NAMESPACE@_EXPORT __declspec(dllexport)
 #else
 #define @KWSYS_NAMESPACE@_EXPORT __declspec(dllimport)
diff --git a/thirdparty/KWSys/adios2sys/Directory.cxx b/thirdparty/KWSys/adios2sys/Directory.cxx
index 5141d451929bc2499fa1e45e046b25063ff479a5..69068aaf88d7e96ae159430d0644bedd8f391d69 100644
--- a/thirdparty/KWSys/adios2sys/Directory.cxx
+++ b/thirdparty/KWSys/adios2sys/Directory.cxx
@@ -118,8 +118,8 @@ bool Directory::Load(const std::string& name)
   struct _wfinddata_t data; // data of current file
 
   // Now put them into the file array
-  srchHandle =
-    _wfindfirst_func((wchar_t*)Encoding::ToWide(buf).c_str(), &data);
+  srchHandle = _wfindfirst_func(
+    (wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data);
   delete[] buf;
 
   if (srchHandle == -1) {
diff --git a/thirdparty/KWSys/adios2sys/ProcessUNIX.c b/thirdparty/KWSys/adios2sys/ProcessUNIX.c
index 9ebcfce592759922ce8fdb5cc02882ea2aae5e10..3b32ca7d0a26b388e7a88cebb2e2e76747157b9e 100644
--- a/thirdparty/KWSys/adios2sys/ProcessUNIX.c
+++ b/thirdparty/KWSys/adios2sys/ProcessUNIX.c
@@ -99,7 +99,8 @@ static inline void kwsysProcess_usleep(unsigned int msec)
  * pipes' file handles to be non-blocking and just poll them directly
  * without select().
  */
-#if !defined(__BEOS__) && !defined(__VMS) && !defined(__MINT__)
+#if !defined(__BEOS__) && !defined(__VMS) && !defined(__MINT__) &&            \
+  !defined(KWSYSPE_USE_SELECT)
 #define KWSYSPE_USE_SELECT 1
 #endif
 
diff --git a/thirdparty/KWSys/adios2sys/RegularExpression.cxx b/thirdparty/KWSys/adios2sys/RegularExpression.cxx
index 6d7f832955911e5352e6f60e246b743c37c3ebdb..26e84e04eb1a98912aeee699f6f3d80f23bed3b6 100644
--- a/thirdparty/KWSys/adios2sys/RegularExpression.cxx
+++ b/thirdparty/KWSys/adios2sys/RegularExpression.cxx
@@ -258,11 +258,6 @@ const unsigned char MAGIC = 0234;
 
 #define UCHARAT(p) (reinterpret_cast<const unsigned char*>(p))[0]
 
-#define FAIL(m)                                                               \
-  {                                                                           \
-    regerror(m);                                                              \
-    return (0);                                                               \
-  }
 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
 #define META "^$.[()|?+*\\"
 
diff --git a/thirdparty/KWSys/adios2sys/SystemTools.cxx b/thirdparty/KWSys/adios2sys/SystemTools.cxx
index c5bbd416a39e4ee92ad9e08b4cac7a77c85b00f9..560c19c0292ee01d3fc9dc18f2f129cc7fa0dc6a 100644
--- a/thirdparty/KWSys/adios2sys/SystemTools.cxx
+++ b/thirdparty/KWSys/adios2sys/SystemTools.cxx
@@ -847,6 +847,8 @@ void SystemTools::ReplaceString(std::string& source, const char* replace,
   free(orig);
 }
 
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
 #if defined(KEY_WOW64_32KEY) && defined(KEY_WOW64_64KEY)
 #define KWSYS_ST_KEY_WOW64_32KEY KEY_WOW64_32KEY
 #define KWSYS_ST_KEY_WOW64_64KEY KEY_WOW64_64KEY
@@ -855,7 +857,6 @@ void SystemTools::ReplaceString(std::string& source, const char* replace,
 #define KWSYS_ST_KEY_WOW64_64KEY 0x0100
 #endif
 
-#if defined(_WIN32) && !defined(__CYGWIN__)
 static bool SystemToolsParseRegistryKey(const std::string& key,
                                         HKEY& primaryKey, std::string& second,
                                         std::string& valuename)
@@ -2268,11 +2269,7 @@ bool SystemTools::CopyADirectory(const std::string& source,
                                  const std::string& destination, bool always)
 {
   Directory dir;
-#ifdef _WIN32
-  dir.Load(Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)));
-#else
   dir.Load(source);
-#endif
   size_t fileNum;
   if (!SystemTools::MakeDirectory(destination)) {
     return false;
@@ -2625,11 +2622,7 @@ bool SystemTools::RemoveADirectory(const std::string& source)
   }
 
   Directory dir;
-#ifdef _WIN32
-  dir.Load(Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)));
-#else
   dir.Load(source);
-#endif
   size_t fileNum;
   for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
     if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") &&
@@ -3796,11 +3789,7 @@ std::string SystemTools::GetFilenamePath(const std::string& filename)
  */
 std::string SystemTools::GetFilenameName(const std::string& filename)
 {
-#if defined(_WIN32)
   std::string::size_type slash_pos = filename.find_last_of("/\\");
-#else
-  std::string::size_type slash_pos = filename.rfind('/');
-#endif
   if (slash_pos != std::string::npos) {
     return filename.substr(slash_pos + 1);
   } else {
diff --git a/thirdparty/KWSys/adios2sys/testDirectory.cxx b/thirdparty/KWSys/adios2sys/testDirectory.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..983f2c6314aed869954606ca82c8ae0c13014cdd
--- /dev/null
+++ b/thirdparty/KWSys/adios2sys/testDirectory.cxx
@@ -0,0 +1,79 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
+#include "kwsysPrivate.h"
+#include KWSYS_HEADER(Directory.hxx)
+#include KWSYS_HEADER(Encoding.hxx)
+#include KWSYS_HEADER(SystemTools.hxx)
+
+// Work-around CMake dependency scanning limitation.  This must
+// duplicate the above list of headers.
+#if 0
+#include "Directory.hxx.in"
+#include "Encoding.hxx.in"
+#include "SystemTools.hxx.in"
+#endif
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include <testSystemTools.h>
+
+int _doLongPathTest()
+{
+  using namespace kwsys;
+  static const int LONG_PATH_THRESHOLD = 512;
+  int res = 0;
+  std::string topdir(TEST_SYSTEMTOOLS_BINARY_DIR "/directory_testing/");
+  std::stringstream testpathstrm;
+  std::string testdirpath;
+  std::string extendedtestdirpath;
+
+  testpathstrm << topdir;
+  size_t pathlen = testpathstrm.str().length();
+  testpathstrm.seekp(0, std::ios_base::end);
+  while (pathlen < LONG_PATH_THRESHOLD) {
+    testpathstrm << "0123456789/";
+    pathlen = testpathstrm.str().length();
+  }
+
+  testdirpath = testpathstrm.str();
+#ifdef _WIN32
+  extendedtestdirpath =
+    Encoding::ToNarrow(SystemTools::ConvertToWindowsExtendedPath(testdirpath));
+#else
+  extendedtestdirpath = testdirpath;
+#endif
+
+  if (SystemTools::MakeDirectory(extendedtestdirpath)) {
+    std::ofstream testfile1(
+      (extendedtestdirpath + "longfilepathtest1.txt").c_str());
+    std::ofstream testfile2(
+      (extendedtestdirpath + "longfilepathtest2.txt").c_str());
+    testfile1 << "foo";
+    testfile2 << "bar";
+    testfile1.close();
+    testfile2.close();
+
+    Directory testdir;
+    // Set res to failure if the directory doesn't load
+    res += !testdir.Load(testdirpath);
+    // Increment res failure if the directory appears empty
+    res += testdir.GetNumberOfFiles() == 0;
+    // Increment res failures if the path has changed from
+    // what was provided.
+    res += testdirpath != testdir.GetPath();
+
+    SystemTools::RemoveADirectory(topdir);
+  } else {
+    std::cerr << "Failed to create directory with long path: "
+              << extendedtestdirpath << std::endl;
+    res += 1;
+  }
+  return res;
+}
+
+int testDirectory(int, char* [])
+{
+  return _doLongPathTest();
+}
diff --git a/thirdparty/KWSys/adios2sys/testSystemTools.cxx b/thirdparty/KWSys/adios2sys/testSystemTools.cxx
index e6fbf6cdafb8ae2197ac3b815ff8b3e5c6535b72..1871f5dd0d81767d9cab71863184000d92cfd7de 100644
--- a/thirdparty/KWSys/adios2sys/testSystemTools.cxx
+++ b/thirdparty/KWSys/adios2sys/testSystemTools.cxx
@@ -758,6 +758,30 @@ static bool CheckGetPath()
   return res;
 }
 
+static bool CheckGetFilenameName()
+{
+  const char* windowsFilepath = "C:\\somewhere\\something";
+  const char* unixFilepath = "/somewhere/something";
+
+  std::string expectedFilename = "something";
+
+  bool res = true;
+  std::string filename = kwsys::SystemTools::GetFilenameName(windowsFilepath);
+  if (filename != expectedFilename) {
+    std::cerr << "GetFilenameName(" << windowsFilepath << ") yielded "
+              << filename << " instead of " << expectedFilename << std::endl;
+    res = false;
+  }
+
+  filename = kwsys::SystemTools::GetFilenameName(unixFilepath);
+  if (filename != expectedFilename) {
+    std::cerr << "GetFilenameName(" << unixFilepath << ") yielded " << filename
+              << " instead of " << expectedFilename << std::endl;
+    res = false;
+  }
+  return res;
+}
+
 static bool CheckFind()
 {
   bool res = true;
@@ -875,5 +899,7 @@ int testSystemTools(int, char* [])
 
   res &= CheckGetLineFromStream();
 
+  res &= CheckGetFilenameName();
+
   return res ? 0 : 1;
 }
diff --git a/thirdparty/KWSys/update.sh b/thirdparty/KWSys/update.sh
index 53d24129ec3b2b9962517cc603e15079c74e8441..c10e61e75e4381a284d56b3432088c37dfb3232d 100755
--- a/thirdparty/KWSys/update.sh
+++ b/thirdparty/KWSys/update.sh
@@ -7,13 +7,8 @@ shopt -s dotglob
 readonly name="KWSys"
 readonly ownership="KWSys Upstream <kwrobot@kitware.com>"
 readonly subtree="thirdparty/KWSys/adios2sys"
-#readonly repo="https://gitlab.kitware.com/utils/kwsys.git"
-#readonly tag="master"
-
-# This commit contains a patch to allow object libraries for kwsys.
-# Use 'master' off the main repo instead once its been merged
-readonly repo="https://gitlab.kitware.com/chuck.atkins/kwsys.git"
-readonly tag="ab645c9"
+readonly repo="https://gitlab.kitware.com/utils/kwsys.git"
+readonly tag="master"
 
 readonly shortlog="true"
 readonly paths="