diff --git a/CMakeLists.txt b/CMakeLists.txt
index bbf244496dd5c2b5cba43cd19fc2f001e3e9872f..0396a1c4620a641fbd26c725875b5a2ca7ab34e4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,7 +67,7 @@ endif()
 
 option(ADIOS_USE_MPI "Enable the MPI version of ADIOS" ON)
 if(ADIOS_USE_MPI)
-  # Workaround for OpenMPI forcing th elink of C++ bindings
+  # Workaround for OpenMPI forcing the link of C++ bindings
   add_definitions(-DOMPI_SKIP_MPICXX)
 endif()
 option(ADIOS_USE_BZip2 "Enable support for BZip2 transforms" ON)
@@ -117,7 +117,8 @@ endif()
 #------------------------------------------------------------------------------#
 message("")
 message("ADIOS2 build configuration:")
-message("  C++ Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} ${CMAKE_CXX_COMPILER_WRAPPER}")
+message("  ADIOS Version: ${ADIOS_VERSION}")
+message("  C++ Compiler : ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} ${CMAKE_CXX_COMPILER_WRAPPER}")
 message("  ${CMAKE_CXX_COMPILER}")
 message("")
 message("  Installation prefix: ${CMAKE_INSTALL_PREFIX}")
diff --git a/include/ADIOS.h b/include/ADIOS.h
index 7765737c32b74be692378f52e6bd42203e036884..56a469c3ee7a7e62abc6ec10f5b61821e55fd4b0 100644
--- a/include/ADIOS.h
+++ b/include/ADIOS.h
@@ -21,6 +21,7 @@
 #include <vector>
 /// \endcond
 
+#include "ADIOSMacros.h"
 #include "ADIOS_MPI.h"
 
 #include "ADIOSTypes.h"
@@ -98,35 +99,20 @@ public:
      * @return
      */
     template <class T>
-    inline Variable<T> &DefineVariable(const std::string &name,
-                                       const Dims dimensions = Dims{1},
-                                       const Dims globalDimensions = Dims(),
-                                       const Dims globalOffsets = Dims())
-    {
-        throw std::invalid_argument("ERROR: type not supported for variable " +
-                                    name + " in call to DefineVariable\n");
-    }
+    Variable<T> &DefineVariable(const std::string &name,
+                                const Dims dimensions = Dims{1},
+                                const Dims globalDimensions = Dims(),
+                                const Dims globalOffsets = Dims());
 
     template <class T>
-    inline Variable<T> &GetVariable(const std::string &name)
-    {
-        throw std::invalid_argument("ERROR: type not supported for variable " +
-                                    name + " in call to GetVariable\n");
-    }
+    Variable<T> &GetVariable(const std::string &name);
 
     template <class T>
-    VariableCompound &DefineVariableCompound(
-        const std::string &name, const Dims dimensions = Dims{1},
-        const Dims globalDimensions = Dims(), const Dims globalOffsets = Dims())
-    {
-        CheckVariableInput(name, dimensions);
-        const unsigned int size = m_Compound.size();
-        m_Compound.emplace(size, VariableCompound(name, sizeof(T), dimensions,
-                                                  globalDimensions,
-                                                  globalOffsets, m_DebugMode));
-        m_Variables.emplace(name, std::make_pair(GetType<T>(), size));
-        return m_Compound.at(size);
-    }
+    VariableCompound &
+    DefineVariableCompound(const std::string &name,
+                           const Dims dimensions = Dims{1},
+                           const Dims globalDimensions = Dims(),
+                           const Dims globalOffsets = Dims());
 
     VariableCompound &GetVariableCompound(const std::string &name);
 
@@ -362,342 +348,26 @@ protected: // no const to allow default empty and copy constructors
                      const std::string &hint) const;
 
     template <class T>
-    unsigned int GetVariableIndex(const std::string &name)
-    {
-        auto itVariable = m_Variables.find(name);
-        CheckVariableName(itVariable, name,
-                          "in call to GetVariable<" + GetType<T>() +
-                              ">, or call to GetVariableCompound if <T> = "
-                              "<compound>\n");
-        return itVariable->second.second;
-    }
-};
-
-// template specializations of DefineVariable:
-template <>
-inline Variable<char> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_Char.size();
-    m_Char.emplace(size, Variable<char>(name, dimensions, globalDimensions,
-                                        globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<char>(), size));
-    return m_Char.at(size);
-}
-
-template <>
-inline Variable<unsigned char> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_UChar.size();
-    m_UChar.emplace(size,
-                    Variable<unsigned char>(name, dimensions, globalDimensions,
-                                            globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<unsigned char>(), size));
-    return m_UChar.at(size);
-}
-
-template <>
-inline Variable<short> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_Short.size();
-    m_Short.emplace(size, Variable<short>(name, dimensions, globalDimensions,
-                                          globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<unsigned char>(), size));
-    return m_Short.at(size);
-}
-
-template <>
-inline Variable<unsigned short> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_UShort.size();
-    m_UShort.emplace(
-        size, Variable<unsigned short>(name, dimensions, globalDimensions,
-                                       globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<unsigned short>(), size));
-    return m_UShort.at(size);
-}
-
-template <>
-inline Variable<int> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_Int.size();
-    m_Int.emplace(size, Variable<int>(name, dimensions, globalDimensions,
-                                      globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<int>(), size));
-    return m_Int.at(size);
-}
-
-template <>
-inline Variable<unsigned int> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_UInt.size();
-    m_UInt.emplace(size,
-                   Variable<unsigned int>(name, dimensions, globalDimensions,
-                                          globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<unsigned int>(), size));
-    return m_UInt.at(size);
-}
-
-template <>
-inline Variable<long int> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_LInt.size();
-    m_LInt.emplace(size, Variable<long int>(name, dimensions, globalDimensions,
-                                            globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<long int>(), size));
-    return m_LInt.at(size);
-}
-
-template <>
-inline Variable<unsigned long int> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_LInt.size();
-    m_ULInt.emplace(
-        size, Variable<unsigned long int>(name, dimensions, globalDimensions,
-                                          globalOffsets, m_DebugMode));
-    m_Variables.emplace(name,
-                        std::make_pair(GetType<unsigned long int>(), size));
-    return m_ULInt.at(size);
-}
-
-template <>
-inline Variable<long long int> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_LLInt.size();
-    m_LLInt.emplace(size,
-                    Variable<long long int>(name, dimensions, globalDimensions,
-                                            globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<long long int>(), size));
-    return m_LLInt.at(size);
-}
-
-template <>
-inline Variable<unsigned long long int> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_ULLInt.size();
-    m_ULLInt.emplace(size, Variable<unsigned long long int>(
-                               name, dimensions, globalDimensions,
-                               globalOffsets, m_DebugMode));
-    m_Variables.emplace(
-        name, std::make_pair(GetType<unsigned long long int>(), size));
-    return m_ULLInt.at(size);
-}
-
-template <>
-inline Variable<float> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_Float.size();
-    m_Float.emplace(size, Variable<float>(name, dimensions, globalDimensions,
-                                          globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<float>(), size));
-    return m_Float.at(size);
-}
-
-template <>
-inline Variable<double> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_Double.size();
-    m_Double.emplace(size, Variable<double>(name, dimensions, globalDimensions,
-                                            globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<double>(), size));
-    return m_Double.at(size);
-}
-
-template <>
-inline Variable<long double> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_LDouble.size();
-    m_LDouble.emplace(size,
-                      Variable<long double>(name, dimensions, globalDimensions,
-                                            globalOffsets, m_DebugMode));
-    m_Variables.emplace(name, std::make_pair(GetType<long double>(), size));
-    return m_LDouble.at(size);
-}
-
-template <>
-inline Variable<std::complex<float>> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_CFloat.size();
-    m_CFloat.emplace(
-        size, Variable<std::complex<float>>(name, dimensions, globalDimensions,
-                                            globalOffsets, m_DebugMode));
-    m_Variables.emplace(name,
-                        std::make_pair(GetType<std::complex<float>>(), size));
-    return m_CFloat.at(size);
-}
-
-template <>
-inline Variable<std::complex<double>> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_CDouble.size();
-    m_CDouble.emplace(
-        size, Variable<std::complex<double>>(name, dimensions, globalDimensions,
-                                             globalOffsets, m_DebugMode));
-    m_Variables.emplace(name,
-                        std::make_pair(GetType<std::complex<double>>(), size));
-    return m_CDouble.at(size);
-}
-
-template <>
-inline Variable<std::complex<long double>> &
-ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
-                      const Dims globalDimensions, const Dims globalOffsets)
-{
-    CheckVariableInput(name, dimensions);
-    const unsigned int size = m_CLDouble.size();
-    m_CLDouble.emplace(size, Variable<std::complex<long double>>(
-                                 name, dimensions, globalDimensions,
-                                 globalOffsets, m_DebugMode));
-    m_Variables.emplace(
-        name, std::make_pair(GetType<std::complex<long double>>(), size));
-    return m_CLDouble.at(size);
-}
-
-// Get template specialization
-template <>
-inline Variable<char> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_Char.at(GetVariableIndex<char>(name));
-}
-
-template <>
-inline Variable<unsigned char> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_UChar.at(GetVariableIndex<unsigned char>(name));
-}
-
-template <>
-inline Variable<short> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_Short.at(GetVariableIndex<short>(name));
-}
-
-template <>
-inline Variable<unsigned short> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_UShort.at(GetVariableIndex<unsigned short>(name));
-}
+    unsigned int GetVariableIndex(const std::string &name);
 
-template <>
-inline Variable<int> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_Int.at(GetVariableIndex<int>(name));
-}
-
-template <>
-inline Variable<unsigned int> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_UInt.at(GetVariableIndex<unsigned int>(name));
-}
-
-template <>
-inline Variable<long int> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_LInt.at(GetVariableIndex<unsigned int>(name));
-}
-
-template <>
-inline Variable<unsigned long int> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_ULInt.at(GetVariableIndex<unsigned long int>(name));
-}
-
-template <>
-inline Variable<long long int> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_LLInt.at(GetVariableIndex<long long int>(name));
-}
-
-template <>
-inline Variable<unsigned long long int> &
-ADIOS::GetVariable(const std::string &name)
-{
-    return m_ULLInt.at(GetVariableIndex<unsigned long long int>(name));
-}
-
-template <>
-inline Variable<float> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_Float.at(GetVariableIndex<float>(name));
-}
-
-template <>
-inline Variable<double> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_Double.at(GetVariableIndex<double>(name));
-}
-
-template <>
-inline Variable<long double> &ADIOS::GetVariable(const std::string &name)
-{
-    return m_LDouble.at(GetVariableIndex<long double>(name));
-}
-
-template <>
-inline Variable<std::complex<float>> &
-ADIOS::GetVariable(const std::string &name)
-{
-    return m_CFloat.at(GetVariableIndex<std::complex<float>>(name));
-}
-
-template <>
-inline Variable<std::complex<double>> &
-ADIOS::GetVariable(const std::string &name)
-{
-    return m_CDouble.at(GetVariableIndex<std::complex<double>>(name));
-}
-
-template <>
-inline Variable<std::complex<long double>> &
-ADIOS::GetVariable(const std::string &name)
-{
-    return m_CLDouble.at(GetVariableIndex<std::complex<long double>>(name));
-}
+    // Helper function for DefineVariable
+    template <class T>
+    std::map<unsigned int, Variable<T>> &GetVarMap();
+};
 
-} // end namespace
+// Explicit declaration of the template methods
+#define declare_template_instantiation(T)                                      \
+    extern template Variable<T> &ADIOS::DefineVariable<T>(                     \
+        const std::string &name, const Dims, const Dims, const Dims);          \
+    extern template Variable<T> &ADIOS::GetVariable<T>(const std::string &);   \
+    extern template unsigned int ADIOS::GetVariableIndex<T>(                   \
+        const std::string &name);                                              \
+    template <>                                                                \
+    std::map<unsigned int, Variable<T>> &ADIOS::GetVarMap<T>();
+ADIOS_FOREACH_TYPE_1ARG(declare_template_instantiation)
+extern template unsigned int ADIOS::GetVariableIndex<void>(const std::string &);
+#undef declare_template_instantiation
+
+} // end namespace adios
 
 #endif /* ADIOS_H_ */
diff --git a/include/ADIOS.tcc b/include/ADIOS.tcc
new file mode 100644
index 0000000000000000000000000000000000000000..de230eef77a0449d2bfb4d65cb8253117c878995
--- /dev/null
+++ b/include/ADIOS.tcc
@@ -0,0 +1,34 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * ADIOS.tcc
+ *   This contains the template implementations for the ADIOS class
+ */
+
+#ifndef ADIOS_TCC_
+#define ADIOS_TCC_
+
+#include "ADIOS.h"
+
+namespace adios
+{
+
+template <class T>
+VariableCompound &ADIOS::DefineVariableCompound(const std::string &name,
+                                                const Dims dimensions,
+                                                const Dims globalDimensions,
+                                                const Dims globalOffsets)
+{
+    CheckVariableInput(name, dimensions);
+    const unsigned int size = m_Compound.size();
+    m_Compound.emplace(size, VariableCompound(name, sizeof(T), dimensions,
+                                              globalDimensions, globalOffsets,
+                                              m_DebugMode));
+    m_Variables.emplace(name, std::make_pair(GetType<T>(), size));
+    return m_Compound.at(size);
+}
+
+} // end namespace adios
+
+#endif /* ADIOS_TCC_ */
diff --git a/include/ADIOSMacros.h b/include/ADIOSMacros.h
new file mode 100644
index 0000000000000000000000000000000000000000..54f8492e9e49f16797d5e52a46e3c0e86d0da600
--- /dev/null
+++ b/include/ADIOSMacros.h
@@ -0,0 +1,42 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * ADIOSMacros.h
+ *   This contains a set of helper macros used internally
+ */
+#ifndef ADIOSMACROS_H
+#define ADIOSMACROS_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
+// each of the known primitive types
+//
+// An example of this might be to instantiate a template function for each
+// known type.  For example:
+//
+//   template<typename T> int foo() { /* some implementation of foo */ }
+//
+//   #define instantiate_foo(T) template int foo<T>();
+//   ADIOS_FOREACH_TYPE_1ARG(instantiate_foo)
+//   #undef instantiate_foo
+//
+#define ADIOS_FOREACH_TYPE_1ARG(MACRO)                                         \
+    MACRO(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)                                                         \
+    MACRO(std::complex<float>)                                                 \
+    MACRO(std::complex<double>)                                                \
+    MACRO(std::complex<long double>)
+
+#endif // ADIOSMACROS_H
diff --git a/include/ADIOS_CPP.h b/include/ADIOS_CPP.h
index 5998ff78e1d2c6559608283e8e976e6f62b35533..a5a174bfdc2469b49a8b656a58d12074ae1ade3e 100644
--- a/include/ADIOS_CPP.h
+++ b/include/ADIOS_CPP.h
@@ -20,11 +20,13 @@
 #include "engine/bp/BPFileWriter.h"
 
 // Will allow to create engines directly (no polymorphism)
-#ifdef HAVE_DATAMAN
+#ifdef ADIOS_HAVE_DATAMAN
 #include "engine/dataman/DataManReader.h"
 #include "engine/dataman/DataManWriter.h"
 #endif
 
+#ifdef ADIOS_HAVE_BZIP2
 #include "transform/BZip2.h"
+#endif
 
 #endif /* ADIOS_CPP_H_ */
diff --git a/include/ADIOS_MPI.h b/include/ADIOS_MPI.h
index 9948a863609301b0b96ec38357fe0d6894d860fe..1a310f04f7c3208949774a2e6994aa1b5076295f 100644
--- a/include/ADIOS_MPI.h
+++ b/include/ADIOS_MPI.h
@@ -6,12 +6,12 @@
 #ifndef ADIOS_MPI_H_
 #define ADIOS_MPI_H_
 
-#ifdef ADIOS_NOMPI
-#include "mpidummy.h"
-#else
+#ifdef ADIOS_HAVE_MPI
 #define OMPI_SKIP_MPICXX 1 // workaround for OpenMPI forcing C++ bindings
 #include <mpi.h>
 #undef OMPI_SKIP_MPICXX
+#else
+#include "mpidummy.h"
 #endif
 
 #endif /* ADIOS_MPI_H_ */
diff --git a/include/engine/adios1/ADIOS1Writer.h b/include/engine/adios1/ADIOS1Writer.h
index 8f41ed432d566e370110775b13e90c051f51a531..a8c114b28121aa9d23d3c179a2ddf70caae723cc 100644
--- a/include/engine/adios1/ADIOS1Writer.h
+++ b/include/engine/adios1/ADIOS1Writer.h
@@ -19,9 +19,12 @@ namespace adios
 {
 
 #ifdef ADIOS_NOMPI
-#define _NOMPI 1
+#define _NOMPI
 #endif
 #include "adios.h" // this is adios 1.x header file
+#ifdef ADIOS_NOMPI
+#undef _NOMPI
+#endif
 
 class ADIOS1Writer : public Engine
 {
diff --git a/include/transform/BZip2.h b/include/transform/BZip2.h
index 0735ae2c711eec9d6774da0066192108567728e4..57f7cf25d95aec06dbf8e91b21c6df518fc481ae 100644
--- a/include/transform/BZip2.h
+++ b/include/transform/BZip2.h
@@ -18,7 +18,7 @@ namespace adios
 namespace transform
 {
 
-class BZIP2 : public Transform
+class BZip2 : public Transform
 {
 
 public:
@@ -27,9 +27,9 @@ public:
      * @param compressionLevel
      * @param variable
      */
-    BZIP2();
+    BZip2();
 
-    ~BZIP2();
+    virtual ~BZip2() = default;
 
     void Compress(const std::vector<char> &bufferIn,
                   std::vector<char> &bufferOut);
diff --git a/source/ADIOS.cpp b/source/ADIOS.cpp
index 0f26439d810c1a641dde3d89cf58d7cc1ea92b91..2d44d18c976555039e0a5abc4082c5585f2c9e3f 100644
--- a/source/ADIOS.cpp
+++ b/source/ADIOS.cpp
@@ -17,6 +17,7 @@
 /// \endcond
 
 #include "ADIOS.h"
+#include "ADIOS.tcc"
 
 #include "functions/adiosFunctions.h"
 
@@ -24,12 +25,12 @@
 #include "engine/bp/BPFileReader.h"
 #include "engine/bp/BPFileWriter.h"
 
-#ifdef HAVE_DATAMAN // external dependencies
+#ifdef ADIOS_HAVE_DATAMAN // external dependencies
 #include "engine/dataman/DataManReader.h"
 #include "engine/dataman/DataManWriter.h"
 #endif
 
-#ifdef HAVE_ADIOS1 // external dependencies
+#ifdef ADIOS_HAVE_ADIOS1 // external dependencies
 #include "engine/adios1/ADIOS1Reader.h"
 #include "engine/adios1/ADIOS1Writer.h"
 #endif
@@ -151,7 +152,7 @@ std::shared_ptr<Engine> ADIOS::Open(const std::string &name,
     }
     else if (type == "DataManWriter")
     {
-#ifdef HAVE_DATAMAN
+#ifdef ADIOS_HAVE_DATAMAN
         return std::make_shared<DataManWriter>(*this, name, accessMode, mpiComm,
                                                method, iomode, timeout_sec,
                                                m_DebugMode, method.m_nThreads);
@@ -163,7 +164,7 @@ std::shared_ptr<Engine> ADIOS::Open(const std::string &name,
     }
     else if (type == "DataManReader")
     {
-#ifdef HAVE_DATAMAN
+#ifdef ADIOS_HAVE_DATAMAN
         return std::make_shared<DataManReader>(*this, name, accessMode, mpiComm,
                                                method, iomode, timeout_sec,
                                                m_DebugMode, method.m_nThreads);
@@ -175,7 +176,7 @@ std::shared_ptr<Engine> ADIOS::Open(const std::string &name,
     }
     else if (type == "ADIOS1Writer")
     {
-#ifdef HAVE_ADIOS1
+#ifdef ADIOS_HAVE_ADIOS1
         return std::make_shared<ADIOS1Writer>(*this, name, accessMode, mpiComm,
                                               method, iomode, timeout_sec,
                                               m_DebugMode, method.m_nThreads);
diff --git a/source/ADIOS_inst.cpp b/source/ADIOS_inst.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c494bc3e286c886d983af23e1ea09bb90bff8721
--- /dev/null
+++ b/source/ADIOS_inst.cpp
@@ -0,0 +1,167 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * ADIOS.tcc
+ *   This contains the template specializatios for the ADIOS class
+ */
+
+#include "ADIOS.tcc"
+#include "ADIOSMacros.h"
+
+namespace adios
+{
+
+// -----------------------------------------------------------------------------
+// template specializations of GetVarMap helper function
+// -----------------------------------------------------------------------------
+
+template <>
+std::map<unsigned int, Variable<char>> &ADIOS::GetVarMap()
+{
+    return m_Char;
+}
+
+template <>
+std::map<unsigned int, Variable<unsigned char>> &ADIOS::GetVarMap()
+{
+    return m_UChar;
+}
+
+template <>
+std::map<unsigned int, Variable<short>> &ADIOS::GetVarMap()
+{
+    return m_Short;
+}
+
+template <>
+std::map<unsigned int, Variable<unsigned short>> &ADIOS::GetVarMap()
+{
+    return m_UShort;
+}
+
+template <>
+std::map<unsigned int, Variable<int>> &ADIOS::GetVarMap()
+{
+    return m_Int;
+}
+
+template <>
+std::map<unsigned int, Variable<unsigned int>> &ADIOS::GetVarMap()
+{
+    return m_UInt;
+}
+
+template <>
+std::map<unsigned int, Variable<long int>> &ADIOS::GetVarMap()
+{
+    return m_LInt;
+}
+
+template <>
+std::map<unsigned int, Variable<unsigned long int>> &ADIOS::GetVarMap()
+{
+    return m_ULInt;
+}
+
+template <>
+std::map<unsigned int, Variable<long long int>> &ADIOS::GetVarMap()
+{
+    return m_LLInt;
+}
+
+template <>
+std::map<unsigned int, Variable<unsigned long long int>> &ADIOS::GetVarMap()
+{
+    return m_ULLInt;
+}
+
+template <>
+std::map<unsigned int, Variable<float>> &ADIOS::GetVarMap()
+{
+    return m_Float;
+}
+
+template <>
+std::map<unsigned int, Variable<double>> &ADIOS::GetVarMap()
+{
+    return m_Double;
+}
+
+template <>
+std::map<unsigned int, Variable<long double>> &ADIOS::GetVarMap()
+{
+    return m_LDouble;
+}
+
+template <>
+std::map<unsigned int, Variable<std::complex<float>>> &ADIOS::GetVarMap()
+{
+    return m_CFloat;
+}
+
+template <>
+std::map<unsigned int, Variable<std::complex<double>>> &ADIOS::GetVarMap()
+{
+    return m_CDouble;
+}
+
+template <>
+std::map<unsigned int, Variable<std::complex<long double>>> &ADIOS::GetVarMap()
+{
+    return m_CLDouble;
+}
+
+// -----------------------------------------------------------------------------
+// explicit template instantiations of DefineVariable:
+// -----------------------------------------------------------------------------
+
+template <typename T>
+Variable<T> &
+ADIOS::DefineVariable(const std::string &name, const Dims dimensions,
+                      const Dims globalDimensions, const Dims globalOffsets)
+{
+    auto &varMap = GetVarMap<T>();
+    CheckVariableInput(name, dimensions);
+    const unsigned int size = varMap.size();
+    varMap.emplace(size, Variable<T>(name, dimensions, globalDimensions,
+                                     globalOffsets, m_DebugMode));
+    m_Variables.emplace(name, std::make_pair(GetType<T>(), size));
+    return varMap.at(size);
+}
+
+#define define_template_instantiation(T)                                       \
+    template Variable<T> &ADIOS::DefineVariable<T>(                            \
+        const std::string &, const Dims, const Dims, const Dims);
+ADIOS_FOREACH_TYPE_1ARG(define_template_instantiation)
+#undef define_template_instatiation
+
+// -----------------------------------------------------------------------------
+// template specializations of GetVariable:
+// -----------------------------------------------------------------------------
+
+template <class T>
+unsigned int ADIOS::GetVariableIndex(const std::string &name)
+{
+    auto itVariable = m_Variables.find(name);
+    CheckVariableName(
+        itVariable, name,
+        "in call to GetVariable<" + GetType<T>() +
+            ">, or call to GetVariableCompound if <T> = <compound>\n");
+    return itVariable->second.second;
+}
+
+template <typename T>
+Variable<T> &ADIOS::GetVariable(const std::string &name)
+{
+    return GetVarMap<T>().at(GetVariableIndex<T>(name));
+}
+
+#define define_template_instatiation(T)                                        \
+    template unsigned int ADIOS::GetVariableIndex<T>(const std::string &);     \
+    template Variable<T> &ADIOS::GetVariable<T>(const std::string &);
+ADIOS_FOREACH_TYPE_1ARG(define_template_instatiation)
+template unsigned int ADIOS::GetVariableIndex<void>(const std::string &);
+#undef define_template_instatiation
+
+} // end namespace adios
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index c18ec19c6e882d9de305c4a8ca8f03487f9acda4..1d3aa14a96164f6217b0068481a9942119f4773b 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -4,7 +4,7 @@
 #------------------------------------------------------------------------------#
 
 add_library(adios2
-  ADIOS.cpp
+  ADIOS.cpp ADIOS.tcc ADIOS_inst.cpp
   #ADIOS_C.cpp
 
   capsule/heap/STLVector.cpp
@@ -35,10 +35,10 @@ target_include_directories(adios2 PUBLIC ${ADIOS_SOURCE_DIR}/include)
 if(ADIOS_USE_MPI)
   find_package(MPI COMPONENTS C REQUIRED)
   target_include_directories(adios2 PUBLIC ${MPI_C_INCLUDE_PATH})
+  target_compile_definitions(adios2 PUBLIC ADIOS_HAVE_MPI)
   target_link_libraries(adios2 PUBLIC ${MPI_C_LIBRARIES})
 else()
   target_sources(adios2 PRIVATE mpidummy.cpp)
-  target_compile_definitions(adios2 PUBLIC ADIOS_NOMPI)
 endif()
 
 if(ADIOS_USE_ADIOS1)
@@ -47,7 +47,7 @@ if(ADIOS_USE_ADIOS1)
     engine/adios1/ADIOS1Reader.cpp
     engine/adios1/ADIOS1Writer.cpp
   )
-  target_compile_definitions(adios2 PRIVATE HAS_ADIOS1)
+  target_compile_definitions(adios2 PRIVATE ADIOS_HAVE_ADIOS1)
   target_link_libraries(adios2 PRIVATE adios1::adios)
 endif()
 
@@ -58,13 +58,13 @@ if(ADIOS_USE_DataMan)
     engine/dataman/DataManWriter.cpp
     transport/wan/MdtmMan.cpp
   )
-  target_compile_definitions(adios2 PRIVATE HAS_DATAMAN)
+  target_compile_definitions(adios2 PRIVATE ADIOS_HAVE_DATAMAN)
   target_link_libraries(adios2 PRIVATE DataMan::DataMan)
 endif()
 
 if(ADIOS_USE_BZip2)
   find_package(BZip2 REQUIRED)
   target_sources(adios2 PRIVATE transform/BZip2.cpp)
-  target_compile_definitions(adios2 PRIVATE HAS_BZIP2)
+  target_compile_definitions(adios2 PRIVATE ADIOS_HAVE_BZIP2)
   target_link_libraries(adios2 PRIVATE BZip2::BZip2)
 endif()
diff --git a/source/functions/adiosFunctions.cpp b/source/functions/adiosFunctions.cpp
index a24cc4c25a30b2de1d78309c0c0185f36a098330..032d3aeb6238c0d8bfd0abe324971b465f49b8fe 100644
--- a/source/functions/adiosFunctions.cpp
+++ b/source/functions/adiosFunctions.cpp
@@ -26,8 +26,8 @@
 #include "core/Support.h"
 #include "functions/adiosFunctions.h"
 
-#ifdef HAVE_BZIP2
-#include "transform/BZIP2.h"
+#ifdef ADIOS_HAVE_BZIP2
+#include "transform/BZip2.h"
 #endif
 
 namespace adios
@@ -539,8 +539,9 @@ void SetTransformsHelper(const std::vector<std::string> &transformNames,
         {
             if (transformMethod == "bzip2")
             {
-#ifdef HAVE_BZIP2
-                transforms.push_back(std::make_shared<CBZIP2>());
+#ifdef ADIOS_HAVE_BZIP2
+                transforms.push_back(
+                    std::make_shared<adios::transform::BZip2>());
 #endif
             }
 
diff --git a/source/transform/BZip2.cpp b/source/transform/BZip2.cpp
index c267af7540249968dd42d9e9bb210b2917661150..d01bea9f825d4d854a34479c1897cb068f8012a2 100644
--- a/source/transform/BZip2.cpp
+++ b/source/transform/BZip2.cpp
@@ -15,16 +15,14 @@ namespace adios
 namespace transform
 {
 
-BZIP2::BZIP2() : Transform("bzip2") {}
+BZip2::BZip2() : Transform("bzip2") {}
 
-BZIP2::~BZIP2() = default;
-
-void BZIP2::Compress(const std::vector<char> & /*bufferIn*/,
+void BZip2::Compress(const std::vector<char> & /*bufferIn*/,
                      std::vector<char> & /*bufferOut*/)
 {
 }
 
-void BZIP2::Decompress(const std::vector<char> & /*bufferIn*/,
+void BZip2::Decompress(const std::vector<char> & /*bufferIn*/,
                        std::vector<char> & /*bufferOut*/)
 {
 }