diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0930bf8853dc053268bc13403f5f7e2144b8c94e..95c94a115871a2e99ece1ca7c7ce2646d3ab0b96 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,6 +75,7 @@ set(THREADS_PREFER_PTHREAD_FLAG OFF)
 # expressions.)
 if(ARB_WITH_GPU)
     enable_language(CUDA)
+
     # Despite native CUDA support, the CUDA package is still required to find
     # the NVML library and to export the cuda library dependencies from the
     # installed target.
@@ -111,12 +112,27 @@ set(CMAKE_CXX_STANDARD 14)
 # to the 'export set', even the private ones, and this must be done
 # in the same CMakeLists.txt in which the target is defined.
 
+# Interface library `arbor-config-defs` collects configure-time defines
+# for arbor and arborenv, of the form ARB_HAVE_XXX. These defines should
+# _not_ be used in any installed public headers.
+
+add_library(arbor-config-defs INTERFACE)
+install(TARGETS arbor-config-defs EXPORT arbor-targets)
+
 # Interface library `arbor-private-deps` collects dependencies, options etc.
 # for the arbor library.
 
 add_library(arbor-private-deps INTERFACE)
+target_link_libraries(arbor-private-deps INTERFACE arbor-config-defs)
 install(TARGETS arbor-private-deps EXPORT arbor-targets)
 
+# Interface library `arborenv-private-deps` collects dependencies, options etc.
+# for the arborenv library.
+
+add_library(arborenv-private-deps INTERFACE)
+target_link_libraries(arborenv-private-deps INTERFACE arbor-config-defs)
+install(TARGETS arborenv-private-deps EXPORT arbor-targets)
+
 # Interface library `arbor-public-deps` collects requirements for the
 # users of the arbor library (e.g. mpi) that will become part
 # of arbor's PUBLIC interface.
@@ -129,11 +145,6 @@ install(TARGETS arbor-public-deps EXPORT arbor-targets)
 
 add_subdirectory(ext)
 
-# Support utiliies in `sup` are common across test executables
-# and examples. Creates interface library `arbor-sup`.
-
-add_subdirectory(sup)
-
 # Keep track of packages we need to add to the generated CMake config
 # file for arbor.
 
@@ -149,16 +160,17 @@ set(arbor_supported_components)
 if(ARB_ARCH)
     set_arch_target(ARB_CXXOPT_ARCH "${ARB_ARCH}")
     target_compile_options(arbor-private-deps INTERFACE ${ARB_CXXOPT_ARCH})
+    target_compile_options(arborenv-private-deps INTERFACE ${ARB_CXXOPT_ARCH})
 endif()
 
 # Profiling and test features
 #-----------------------------
 
 if(ARB_WITH_PROFILING)
-    target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_PROFILING)
+    target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_PROFILING)
 endif()
 if(ARB_WITH_ASSERTIONS)
-    target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_ASSERTIONS)
+    target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_ASSERTIONS)
 endif()
 
 # Threading model
@@ -175,7 +187,7 @@ list(APPEND arbor_export_dependencies "Threads")
 
 if(ARB_WITH_MPI)
     find_package(MPI REQUIRED CXX)
-    target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_MPI)
+    target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_MPI)
 
     # target_compile_definitions(MPI::MPI_CXX INTERFACE MPICH_SKIP_MPICXX=1 OMPI_SKIP_MPICXX=1)
     # target_link_libraries(arbor-public-deps INTERFACE MPI::MPI_CXX)
@@ -186,6 +198,7 @@ if(ARB_WITH_MPI)
     add_library(mpi-wrap INTERFACE)
     target_link_libraries(mpi-wrap INTERFACE MPI::MPI_CXX)
     target_compile_definitions(mpi-wrap INTERFACE MPICH_SKIP_MPICXX=1 OMPI_SKIP_MPICXX=1)
+
     target_link_libraries(arbor-public-deps INTERFACE mpi-wrap)
     install(TARGETS mpi-wrap EXPORT arbor-targets)
 
@@ -198,14 +211,38 @@ endif()
 
 if(ARB_WITH_GPU)
     set(ARB_WITH_CUDA TRUE)
+    target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_GPU)
+    if(ARB_WITH_GPU_FINE_MATRIX)
+        target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_GPU_FINE_MATRIX)
+    endif()
+
+    target_include_directories(arborenv-private-deps INTERFACE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
+
+    # The arborenv lib needs to use the CUDA NVML library for CUDA 9.
+
+    if (${CUDA_VERSION_MAJOR} LESS 10)
+        set(arborenv_requires_nvml TRUE)
+    endif()
+
+    if(arborenv_requires_nvml)
+        set(nvml_names nvidia-ml) # Note: platform dependent? e.g. nvml.lib on Windows.
+        find_library(CUDA_NVML
+                     NAMES ${nvml_names}
+                     NO_DEFAULT_PATH
+                     PATHS ${CMAKE_CUDA_IMPLICIT_DIRECTORIES} ${CUDA_TOOLKIT_ROOT_DIR}
+                     PATH_SUFFIXES lib64/stubs lib/stubs)
+
+        if (NOT CUDA_NVML)
+            message(FATAL_ERROR "Unable to find CUDA NVML library by: ${nvml_names}")
+        endif()
+
+        target_link_libraries(arborenv-private-deps INTERFACE ${CUDA_NVML})
+        target_compile_definitions(arborenv-private-deps INTERFACE ARBENV_USE_NVML)
+    endif()
 
     add_compile_options(
         "$<$<COMPILE_LANGUAGE:CUDA>:-Xcudafe=--diag_suppress=integer_sign_change>"
         "$<$<COMPILE_LANGUAGE:CUDA>:-Xcudafe=--diag_suppress=unsigned_compare_with_zero>")
-    target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_GPU)
-    if(ARB_WITH_GPU_FINE_MATRIX)
-        target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_GPU_FINE_MATRIX)
-    endif()
 
     target_compile_options(arbor-private-deps INTERFACE
         $<$<COMPILE_LANGUAGE:CUDA>:-gencode=arch=compute_35,code=sm_35>)
@@ -294,7 +331,10 @@ endforeach()
 #----------------------------------------------------------
 
 # arbor-public-headers:
-add_subdirectory(include)
+add_subdirectory(arbor/include)
+
+# arbor-sup:
+add_subdirectory(sup)
 
 # modcc, libmodcc:
 add_subdirectory(modcc)
@@ -302,6 +342,9 @@ add_subdirectory(modcc)
 # arbor, arbor-private-headers:
 add_subdirectory(arbor)
 
+# arborenv, arborenv-public-headers:
+add_subdirectory(arborenv)
+
 # unit, unit-mpi, unit-local, unit-modcc, validate
 add_subdirectory(test)
 
@@ -353,10 +396,16 @@ endif()
 
 set(arbor_override_import_lang)
 set(arbor_add_import_libs)
+set(arborenv_add_import_libs)
 
 if(ARB_WITH_GPU)
     set(arbor_override_import_lang CXX)
     set(arbor_add_import_libs ${CUDA_LIBRARIES})
+    set(arborenv_add_import_libs ${CUDA_LIBRARIES})
+
+    if(arborenv_requires_nvml)
+        list(APPEND arborenv_add_import_libs ${CUDA_NVML})
+    endif()
 endif()
 
 configure_file(
diff --git a/include/CMakeLists.txt b/arbor/include/CMakeLists.txt
similarity index 100%
rename from include/CMakeLists.txt
rename to arbor/include/CMakeLists.txt
diff --git a/include/arbor/arbexcept.hpp b/arbor/include/arbor/arbexcept.hpp
similarity index 100%
rename from include/arbor/arbexcept.hpp
rename to arbor/include/arbor/arbexcept.hpp
diff --git a/include/arbor/assert.hpp b/arbor/include/arbor/assert.hpp
similarity index 100%
rename from include/arbor/assert.hpp
rename to arbor/include/arbor/assert.hpp
diff --git a/include/arbor/assert_macro.hpp.disabled b/arbor/include/arbor/assert_macro.hpp.disabled
similarity index 100%
rename from include/arbor/assert_macro.hpp.disabled
rename to arbor/include/arbor/assert_macro.hpp.disabled
diff --git a/include/arbor/assert_macro.hpp.enabled b/arbor/include/arbor/assert_macro.hpp.enabled
similarity index 100%
rename from include/arbor/assert_macro.hpp.enabled
rename to arbor/include/arbor/assert_macro.hpp.enabled
diff --git a/include/arbor/benchmark_cell.hpp b/arbor/include/arbor/benchmark_cell.hpp
similarity index 100%
rename from include/arbor/benchmark_cell.hpp
rename to arbor/include/arbor/benchmark_cell.hpp
diff --git a/include/arbor/common_types.hpp b/arbor/include/arbor/common_types.hpp
similarity index 100%
rename from include/arbor/common_types.hpp
rename to arbor/include/arbor/common_types.hpp
diff --git a/include/arbor/communication/mpi_error.hpp b/arbor/include/arbor/communication/mpi_error.hpp
similarity index 100%
rename from include/arbor/communication/mpi_error.hpp
rename to arbor/include/arbor/communication/mpi_error.hpp
diff --git a/include/arbor/constants.hpp b/arbor/include/arbor/constants.hpp
similarity index 100%
rename from include/arbor/constants.hpp
rename to arbor/include/arbor/constants.hpp
diff --git a/include/arbor/context.hpp b/arbor/include/arbor/context.hpp
similarity index 100%
rename from include/arbor/context.hpp
rename to arbor/include/arbor/context.hpp
diff --git a/include/arbor/domain_decomposition.hpp b/arbor/include/arbor/domain_decomposition.hpp
similarity index 100%
rename from include/arbor/domain_decomposition.hpp
rename to arbor/include/arbor/domain_decomposition.hpp
diff --git a/include/arbor/event_generator.hpp b/arbor/include/arbor/event_generator.hpp
similarity index 100%
rename from include/arbor/event_generator.hpp
rename to arbor/include/arbor/event_generator.hpp
diff --git a/include/arbor/fvm_types.hpp b/arbor/include/arbor/fvm_types.hpp
similarity index 100%
rename from include/arbor/fvm_types.hpp
rename to arbor/include/arbor/fvm_types.hpp
diff --git a/include/arbor/generic_event.hpp b/arbor/include/arbor/generic_event.hpp
similarity index 100%
rename from include/arbor/generic_event.hpp
rename to arbor/include/arbor/generic_event.hpp
diff --git a/include/arbor/ion.hpp b/arbor/include/arbor/ion.hpp
similarity index 100%
rename from include/arbor/ion.hpp
rename to arbor/include/arbor/ion.hpp
diff --git a/include/arbor/lif_cell.hpp b/arbor/include/arbor/lif_cell.hpp
similarity index 100%
rename from include/arbor/lif_cell.hpp
rename to arbor/include/arbor/lif_cell.hpp
diff --git a/include/arbor/load_balance.hpp b/arbor/include/arbor/load_balance.hpp
similarity index 100%
rename from include/arbor/load_balance.hpp
rename to arbor/include/arbor/load_balance.hpp
diff --git a/include/arbor/math.hpp b/arbor/include/arbor/math.hpp
similarity index 100%
rename from include/arbor/math.hpp
rename to arbor/include/arbor/math.hpp
diff --git a/include/arbor/mc_cell.hpp b/arbor/include/arbor/mc_cell.hpp
similarity index 100%
rename from include/arbor/mc_cell.hpp
rename to arbor/include/arbor/mc_cell.hpp
diff --git a/include/arbor/mc_segment.hpp b/arbor/include/arbor/mc_segment.hpp
similarity index 100%
rename from include/arbor/mc_segment.hpp
rename to arbor/include/arbor/mc_segment.hpp
diff --git a/include/arbor/mechanism.hpp b/arbor/include/arbor/mechanism.hpp
similarity index 100%
rename from include/arbor/mechanism.hpp
rename to arbor/include/arbor/mechanism.hpp
diff --git a/include/arbor/mechcat.hpp b/arbor/include/arbor/mechcat.hpp
similarity index 100%
rename from include/arbor/mechcat.hpp
rename to arbor/include/arbor/mechcat.hpp
diff --git a/include/arbor/mechinfo.hpp b/arbor/include/arbor/mechinfo.hpp
similarity index 100%
rename from include/arbor/mechinfo.hpp
rename to arbor/include/arbor/mechinfo.hpp
diff --git a/include/arbor/morphology.hpp b/arbor/include/arbor/morphology.hpp
similarity index 100%
rename from include/arbor/morphology.hpp
rename to arbor/include/arbor/morphology.hpp
diff --git a/include/arbor/point.hpp b/arbor/include/arbor/point.hpp
similarity index 100%
rename from include/arbor/point.hpp
rename to arbor/include/arbor/point.hpp
diff --git a/include/arbor/profile/clock.hpp b/arbor/include/arbor/profile/clock.hpp
similarity index 100%
rename from include/arbor/profile/clock.hpp
rename to arbor/include/arbor/profile/clock.hpp
diff --git a/include/arbor/profile/meter.hpp b/arbor/include/arbor/profile/meter.hpp
similarity index 100%
rename from include/arbor/profile/meter.hpp
rename to arbor/include/arbor/profile/meter.hpp
diff --git a/include/arbor/profile/meter_manager.hpp b/arbor/include/arbor/profile/meter_manager.hpp
similarity index 100%
rename from include/arbor/profile/meter_manager.hpp
rename to arbor/include/arbor/profile/meter_manager.hpp
diff --git a/include/arbor/profile/profiler.hpp b/arbor/include/arbor/profile/profiler.hpp
similarity index 100%
rename from include/arbor/profile/profiler.hpp
rename to arbor/include/arbor/profile/profiler.hpp
diff --git a/include/arbor/profile/timer.hpp b/arbor/include/arbor/profile/timer.hpp
similarity index 100%
rename from include/arbor/profile/timer.hpp
rename to arbor/include/arbor/profile/timer.hpp
diff --git a/include/arbor/recipe.hpp b/arbor/include/arbor/recipe.hpp
similarity index 100%
rename from include/arbor/recipe.hpp
rename to arbor/include/arbor/recipe.hpp
diff --git a/include/arbor/sampling.hpp b/arbor/include/arbor/sampling.hpp
similarity index 100%
rename from include/arbor/sampling.hpp
rename to arbor/include/arbor/sampling.hpp
diff --git a/include/arbor/schedule.hpp b/arbor/include/arbor/schedule.hpp
similarity index 100%
rename from include/arbor/schedule.hpp
rename to arbor/include/arbor/schedule.hpp
diff --git a/include/arbor/simd/approx.hpp b/arbor/include/arbor/simd/approx.hpp
similarity index 100%
rename from include/arbor/simd/approx.hpp
rename to arbor/include/arbor/simd/approx.hpp
diff --git a/include/arbor/simd/avx.hpp b/arbor/include/arbor/simd/avx.hpp
similarity index 100%
rename from include/arbor/simd/avx.hpp
rename to arbor/include/arbor/simd/avx.hpp
diff --git a/include/arbor/simd/avx512.hpp b/arbor/include/arbor/simd/avx512.hpp
similarity index 100%
rename from include/arbor/simd/avx512.hpp
rename to arbor/include/arbor/simd/avx512.hpp
diff --git a/include/arbor/simd/generic.hpp b/arbor/include/arbor/simd/generic.hpp
similarity index 100%
rename from include/arbor/simd/generic.hpp
rename to arbor/include/arbor/simd/generic.hpp
diff --git a/include/arbor/simd/implbase.hpp b/arbor/include/arbor/simd/implbase.hpp
similarity index 100%
rename from include/arbor/simd/implbase.hpp
rename to arbor/include/arbor/simd/implbase.hpp
diff --git a/include/arbor/simd/native.hpp b/arbor/include/arbor/simd/native.hpp
similarity index 100%
rename from include/arbor/simd/native.hpp
rename to arbor/include/arbor/simd/native.hpp
diff --git a/include/arbor/simd/simd.hpp b/arbor/include/arbor/simd/simd.hpp
similarity index 100%
rename from include/arbor/simd/simd.hpp
rename to arbor/include/arbor/simd/simd.hpp
diff --git a/include/arbor/simd/simd_io.hpp b/arbor/include/arbor/simd/simd_io.hpp
similarity index 100%
rename from include/arbor/simd/simd_io.hpp
rename to arbor/include/arbor/simd/simd_io.hpp
diff --git a/include/arbor/simple_sampler.hpp b/arbor/include/arbor/simple_sampler.hpp
similarity index 100%
rename from include/arbor/simple_sampler.hpp
rename to arbor/include/arbor/simple_sampler.hpp
diff --git a/include/arbor/simulation.hpp b/arbor/include/arbor/simulation.hpp
similarity index 100%
rename from include/arbor/simulation.hpp
rename to arbor/include/arbor/simulation.hpp
diff --git a/include/arbor/spike.hpp b/arbor/include/arbor/spike.hpp
similarity index 100%
rename from include/arbor/spike.hpp
rename to arbor/include/arbor/spike.hpp
diff --git a/include/arbor/spike_event.hpp b/arbor/include/arbor/spike_event.hpp
similarity index 100%
rename from include/arbor/spike_event.hpp
rename to arbor/include/arbor/spike_event.hpp
diff --git a/include/arbor/spike_source_cell.hpp b/arbor/include/arbor/spike_source_cell.hpp
similarity index 100%
rename from include/arbor/spike_source_cell.hpp
rename to arbor/include/arbor/spike_source_cell.hpp
diff --git a/include/arbor/swcio.hpp b/arbor/include/arbor/swcio.hpp
similarity index 100%
rename from include/arbor/swcio.hpp
rename to arbor/include/arbor/swcio.hpp
diff --git a/include/arbor/symmetric_recipe.hpp b/arbor/include/arbor/symmetric_recipe.hpp
similarity index 100%
rename from include/arbor/symmetric_recipe.hpp
rename to arbor/include/arbor/symmetric_recipe.hpp
diff --git a/include/arbor/util/any.hpp b/arbor/include/arbor/util/any.hpp
similarity index 100%
rename from include/arbor/util/any.hpp
rename to arbor/include/arbor/util/any.hpp
diff --git a/include/arbor/util/any_ptr.hpp b/arbor/include/arbor/util/any_ptr.hpp
similarity index 100%
rename from include/arbor/util/any_ptr.hpp
rename to arbor/include/arbor/util/any_ptr.hpp
diff --git a/include/arbor/util/compat.hpp b/arbor/include/arbor/util/compat.hpp
similarity index 100%
rename from include/arbor/util/compat.hpp
rename to arbor/include/arbor/util/compat.hpp
diff --git a/include/arbor/util/handle_set.hpp b/arbor/include/arbor/util/handle_set.hpp
similarity index 100%
rename from include/arbor/util/handle_set.hpp
rename to arbor/include/arbor/util/handle_set.hpp
diff --git a/include/arbor/util/lexcmp_def.hpp b/arbor/include/arbor/util/lexcmp_def.hpp
similarity index 100%
rename from include/arbor/util/lexcmp_def.hpp
rename to arbor/include/arbor/util/lexcmp_def.hpp
diff --git a/include/arbor/util/optional.hpp b/arbor/include/arbor/util/optional.hpp
similarity index 100%
rename from include/arbor/util/optional.hpp
rename to arbor/include/arbor/util/optional.hpp
diff --git a/include/arbor/util/pp_util.hpp b/arbor/include/arbor/util/pp_util.hpp
similarity index 100%
rename from include/arbor/util/pp_util.hpp
rename to arbor/include/arbor/util/pp_util.hpp
diff --git a/sup/include/sup/scope_exit.hpp b/arbor/include/arbor/util/scope_exit.hpp
similarity index 96%
rename from sup/include/sup/scope_exit.hpp
rename to arbor/include/arbor/util/scope_exit.hpp
index acba9da5a96d5f842f52f1dbe577a35c7ee151b4..f353c1e2193687def0ecba3bdfb250556f60c94c 100644
--- a/sup/include/sup/scope_exit.hpp
+++ b/arbor/include/arbor/util/scope_exit.hpp
@@ -6,7 +6,8 @@
 
 // Convenience class for RAII control of resources.
 
-namespace sup {
+namespace arb {
+namespace util {
 
 // `scope_exit` guard object will call provided functional object
 // on destruction. The provided functional object must be nothrow
@@ -73,4 +74,5 @@ auto on_scope_exit(std::function<R ()> f) {
     return on_scope_exit(impl::wrap_std_function<R>(std::move(f)));
 }
 
-} // namespace sup
+} // namespace util
+} // namespace arb
diff --git a/include/arbor/util/uninitialized.hpp b/arbor/include/arbor/util/uninitialized.hpp
similarity index 100%
rename from include/arbor/util/uninitialized.hpp
rename to arbor/include/arbor/util/uninitialized.hpp
diff --git a/include/arbor/util/unique_any.hpp b/arbor/include/arbor/util/unique_any.hpp
similarity index 100%
rename from include/arbor/util/unique_any.hpp
rename to arbor/include/arbor/util/unique_any.hpp
diff --git a/include/git-source-id b/arbor/include/git-source-id
similarity index 100%
rename from include/git-source-id
rename to arbor/include/git-source-id
diff --git a/arborenv/CMakeLists.txt b/arborenv/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..921622c0d1832113afa02131a8c96ca90a306506
--- /dev/null
+++ b/arborenv/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(arborenv-sources
+    affinity.cpp
+    concurrency.cpp
+    default_gpu.cpp
+    private_gpu.cpp
+)
+
+if(ARB_WITH_GPU)
+    list(APPEND arborenv-sources gpu_uuid.cpp)
+endif()
+
+add_library(arborenv ${arborenv-sources})
+
+add_library(arborenv-public-headers INTERFACE)
+target_include_directories(arborenv-public-headers INTERFACE
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+    $<INSTALL_INTERFACE:include>
+)
+
+# A public arbor dependency is required for the use of arb::mpi_error,
+# which in turn requires the arbor libary's singleton for the mpi_error_category
+# singleton.
+
+target_link_libraries(arborenv PUBLIC arbor arborenv-public-headers)
+target_link_libraries(arborenv PRIVATE arbor-config-defs arborenv-private-deps)
+
+install(DIRECTORY include/arborenv
+    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+    FILES_MATCHING PATTERN "*.hpp")
+
+install(TARGETS arborenv-public-headers EXPORT arbor-targets)
+install(TARGETS arborenv EXPORT arbor-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/sup/affinity.cpp b/arborenv/affinity.cpp
similarity index 89%
rename from sup/affinity.cpp
rename to arborenv/affinity.cpp
index 7454b30d9c3f03bdcf3105d013377918c62291ac..61502cc621d89f3f129f450eab53dea358f37070 100644
--- a/sup/affinity.cpp
+++ b/arborenv/affinity.cpp
@@ -12,7 +12,7 @@ extern "C" {
 #include <sched.h>
 }
 
-namespace sup {
+namespace arbenv {
 
 std::vector<int> get_affinity() {
     std::vector<int> cores;
@@ -32,17 +32,17 @@ std::vector<int> get_affinity() {
     return cores;
 }
 
-} // namespace sup
+} // namespace arbenv
 
 #else // def __linux__
 
 // No support for non-linux systems.
-namespace sup {
+namespace arbenv {
 
 std::vector<int> get_affinity() {
     return {};
 }
 
-} // namespace sup
+} // namespace arbenv
 
 #endif // def __linux__
diff --git a/sup/concurrency.cpp b/arborenv/concurrency.cpp
similarity index 87%
rename from sup/concurrency.cpp
rename to arborenv/concurrency.cpp
index 6635ae376923c53aecb18452d605c3daa1a5743f..fd0e59198238ff41d68b309c7fb9be968d12dd75 100644
--- a/sup/concurrency.cpp
+++ b/arborenv/concurrency.cpp
@@ -3,20 +3,18 @@
 #include <string>
 #include <thread>
 
-#include <arbor/arbexcept.hpp>
-
-#include <sup/affinity.hpp>
-#include <sup/concurrency.hpp>
+#include <arborenv/concurrency.hpp>
 
 // TODO: C++17 use __has_include(<unistd.h>)
 #if defined(__unix__) || defined(__APPLE__) && defined(__MACH__)
 #include <unistd.h>
 #endif
 
-namespace sup {
+namespace arbenv {
 
 // Test environment variables for user-specified count of threads.
 unsigned get_env_num_threads() {
+    using namespace std::literals;
     const char* str;
 
     // select variable to use:
@@ -43,8 +41,7 @@ unsigned get_env_num_threads() {
         !std::regex_match(str, std::regex("\\s*\\d*[0-9]\\d*\\s*")))
     {
         errno = 0;
-        throw arb::arbor_exception(
-            std::string("Requested number of threads \"") + str + "\" is not a valid value");
+        throw std::runtime_error("Requested number of threads \""s + str + "\" is not a valid value"s);
     }
     errno = 0;
 
@@ -78,4 +75,4 @@ unsigned thread_concurrency() {
     return n;
 }
 
-} // namespace sup
+} // namespace arbenv
diff --git a/sup/default_gpu.cpp b/arborenv/default_gpu.cpp
similarity index 85%
rename from sup/default_gpu.cpp
rename to arborenv/default_gpu.cpp
index 80f3f4b9653e93aa50f662d9e6a66978ac5e45d5..d13e8dd035015b5ac58bad5961ee50dc44d8c6ff 100644
--- a/sup/default_gpu.cpp
+++ b/arborenv/default_gpu.cpp
@@ -2,7 +2,7 @@
 
 #include <cuda_runtime.h>
 
-namespace sup {
+namespace arbenv {
 
 // When arbor does not have CUDA support, return -1, which always
 // indicates that no GPU is available.
@@ -16,17 +16,17 @@ int default_gpu() {
     return -1;
 }
 
-} // namespace sup
+} // namespace arbenv
 
 #else // ifdef ARB_HAVE_GPU
 
-namespace sup {
+namespace arbenv {
 
 int default_gpu() {
     return -1;
 }
 
-} // namespace sup
+} // namespace arbenv
 
 #endif // ifdef ARB_HAVE_GPU
 
diff --git a/sup/gpu_uuid.cpp b/arborenv/gpu_uuid.cpp
similarity index 97%
rename from sup/gpu_uuid.cpp
rename to arborenv/gpu_uuid.cpp
index 08c87be5b310a1a836b16f34cb289eec36db8d02..b31c1ae42052f0848bb7206f0d0dda2d4bdfbd8f 100644
--- a/sup/gpu_uuid.cpp
+++ b/arborenv/gpu_uuid.cpp
@@ -9,20 +9,25 @@
 #include <stdexcept>
 #include <vector>
 
-#include <sup/scope_exit.hpp>
+#include <cuda_runtime.h>
 
+#include <arbor/util/scope_exit.hpp>
 #include "gpu_uuid.hpp"
 
-#include <cuda_runtime.h>
 
 // CUDA 10 allows GPU uuid to be queried via cudaGetDeviceProperties.
 // Previous versions require the CUDA NVML library to get uuid.
-#if CUDART_VERSION < 10000
+//
+// ARBENV_USE_NVML will be defined at configuration time if using
+// CUDA version 9.
+
+#ifdef ARBENV_USE_NVML
     #include <nvml.h>
-    #define ARB_USE_NVML
 #endif
 
-namespace sup {
+using arb::util::on_scope_exit;
+
+namespace arbenv {
 
 // Test GPU uids for equality
 bool operator==(const uuid& lhs, const uuid& rhs) {
@@ -65,7 +70,7 @@ std::runtime_error make_runtime_error(cudaError_t error_code) {
         + cudaGetErrorName(error_code) + ": " + cudaGetErrorString(error_code));
 }
 
-#ifndef ARB_USE_NVML
+#ifndef ARBENV_USE_NVML
 
 // For CUDA 10 and later the uuid of all available GPUs is straightforward
 // to obtain by querying cudaGetDeviceProperties for each visible device.
@@ -237,7 +242,7 @@ std::vector<uuid> get_gpu_uuids() {
 
     return uuids;
 }
-#endif
+#endif // ndef ARBENV_USE_NVML
 
 // Compare two sets of uuids
 //   1: both sets are identical
@@ -308,4 +313,4 @@ gpu_rank assign_gpu(const std::vector<uuid>& uids,
     return pos_in_group<ngpu_in_group? gpu_rank(pos_in_group): gpu_rank(-1);
 }
 
-} // namespace sup
+} // namespace arbenv
diff --git a/sup/gpu_uuid.hpp b/arborenv/gpu_uuid.hpp
similarity index 96%
rename from sup/gpu_uuid.hpp
rename to arborenv/gpu_uuid.hpp
index f6f34a17196e59f36c637168fc58de9af9035ea9..3226d694b53440cac0090c19cef4b63ca5df21e5 100644
--- a/sup/gpu_uuid.hpp
+++ b/arborenv/gpu_uuid.hpp
@@ -4,7 +4,7 @@
 #include <ostream>
 #include <vector>
 
-namespace sup {
+namespace arbenv {
 
 // Store cudaUUID_t in a byte array for easy type punning and comparison.
 // 128 bit uuids are not just for GPUs: they are used in many applications,
@@ -38,4 +38,4 @@ struct gpu_rank {
 
 gpu_rank assign_gpu(const std::vector<uuid>& uids, const std::vector<int>&  uid_part, int rank);
 
-} // namespace sup
+} // namespace arbenv
diff --git a/sup/include/sup/concurrency.hpp b/arborenv/include/arborenv/concurrency.hpp
similarity index 61%
rename from sup/include/sup/concurrency.hpp
rename to arborenv/include/arborenv/concurrency.hpp
index c7a082f38fc58e84032096141f045399078bb04c..2aa0b7709306f567a1a6c30ba51d93c00a4418de 100644
--- a/sup/include/sup/concurrency.hpp
+++ b/arborenv/include/arborenv/concurrency.hpp
@@ -1,8 +1,8 @@
 #pragma once
 
-#include <arbor/util/optional.hpp>
+#include <vector>
 
-namespace sup {
+namespace arbenv {
 
 // Test environment variables for user-specified count of threads.
 // Potential environment variables are tested in this order:
@@ -26,4 +26,15 @@ unsigned get_env_num_threads();
 // Will return at least 1.
 unsigned thread_concurrency();
 
-} // namespace sup
+// The list of logical processors for which the calling thread has affinity.
+// If calling from the main thread at application start up, before
+// attempting to change thread affinity, may produce unreliable
+// results.
+//  - beware thread pinning or custom job scheduler affinity
+//    flags that assign threads to specific cores.
+//
+// Returns an empty vector if unable to determine the number of
+// available cores.
+std::vector<int> get_affinity();
+
+} // namespace arbenv
diff --git a/sup/include/sup/gpu.hpp b/arborenv/include/arborenv/gpu_env.hpp
similarity index 59%
rename from sup/include/sup/gpu.hpp
rename to arborenv/include/arborenv/gpu_env.hpp
index 21b95a897afa6e37afd289a22d20f230428abf37..9db867af75d5da3d574123aba91d0f887b95e66f 100644
--- a/sup/include/sup/gpu.hpp
+++ b/arborenv/include/arborenv/gpu_env.hpp
@@ -1,13 +1,11 @@
 #pragma once
 
-#include <arbor/version.hpp>
-
-namespace sup {
+namespace arbenv {
 
 int default_gpu();
 
 template <typename Comm>
 int find_private_gpu(Comm comm);
 
-} // namespace sup
+} // namespace arbenv
 
diff --git a/sup/include/sup/with_mpi.hpp b/arborenv/include/arborenv/with_mpi.hpp
similarity index 94%
rename from sup/include/sup/with_mpi.hpp
rename to arborenv/include/arborenv/with_mpi.hpp
index 027b45df3b989fbe0b4da2d3120f93f98262a827..ee9e5f15dfc514ed001ff6496598b0b1c65df174 100644
--- a/sup/include/sup/with_mpi.hpp
+++ b/arborenv/include/arborenv/with_mpi.hpp
@@ -6,14 +6,14 @@
 
 #include <arbor/communication/mpi_error.hpp>
 
-namespace sup {
+namespace arbenv {
 
 struct with_mpi {
     with_mpi(int& argc, char**& argv, bool fatal_errors = true) {
         init(&argc, &argv, fatal_errors);
     }
 
-    with_mpi(bool fatal_errors = true) {
+    explicit with_mpi(bool fatal_errors = true) {
         init(nullptr, nullptr, fatal_errors);
     }
 
@@ -48,4 +48,4 @@ private:
     }
 };
 
-} // namespace sup
+} // namespace arbenv
diff --git a/sup/private_gpu.cpp b/arborenv/private_gpu.cpp
similarity index 93%
rename from sup/private_gpu.cpp
rename to arborenv/private_gpu.cpp
index 88c36043a9823bcf25c29c2ecde2e367cfc2c1ad..f029157112bfa0c06ec827aabb2c3a9a6bcea29f 100644
--- a/sup/private_gpu.cpp
+++ b/arborenv/private_gpu.cpp
@@ -1,24 +1,16 @@
-#ifndef ARB_HAVE_GPU
-
-#include <sup/gpu.hpp>
-#include <mpi.h>
-
-namespace sup {
-// return -1 -> "no gpu" when compiled without GPU support.
-template <>
-int find_private_gpu(MPI_Comm comm) {
-    return -1;
-}
-}
-
-#else
+#ifdef ARB_HAVE_MPI
 
+#include <stdexcept>
 #include <numeric>
+
 #include <mpi.h>
-#include <sup/gpu.hpp>
+
+#include <arborenv/gpu_env.hpp>
 #include "gpu_uuid.hpp"
 
-namespace sup {
+namespace arbenv {
+
+#ifdef ARB_HAVE_GPU
 
 template <>
 int find_private_gpu(MPI_Comm comm) {
@@ -99,7 +91,18 @@ int find_private_gpu(MPI_Comm comm) {
     return gpu.id;
 }
 
-} // namespace sup
+#else
+
+// return -1 -> "no gpu" when compiled without GPU support.
+template <>
+int find_private_gpu(MPI_Comm comm) {
+    return -1;
+}
+
+#endif // def ARB_HAVE_GPU
+
+} // namespace arbenv
+
+#endif // def ARB_HAVE_MPI
 
-#endif
 
diff --git a/cmake/arbor-config.cmake.in b/cmake/arbor-config.cmake.in
index 2d8a3cb6e6d5de804f3125af5d5c3f6599dd2e92..e5bc0caf6b77481c15206fcf917de9b00b5f49a2 100644
--- a/cmake/arbor-config.cmake.in
+++ b/cmake/arbor-config.cmake.in
@@ -19,16 +19,25 @@ endforeach()
 
 set(_override_lang @arbor_override_import_lang@)
 if(_override_lang)
-    set_target_properties(arbor::arbor PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_@arbor_build_config@ "${_override_lang}")
+    foreach(target arbor::arbor arbor::arborenv)
+        set_target_properties(${target} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_@arbor_build_config@ "${_override_lang}")
+    endforeach()
 endif()
 
-set(_add_libs @arbor_add_import_libs@)
-if(_add_libs)
-    get_target_property(_arbor_interface_libs arbor::arbor INTERFACE_LINK_LIBRARIES)
-    if(_arbor_interface_libs)
-        list(APPEND _arbor_interface_libs ${_add_libs})
+# Explicitly add extra link libraries not covered by dependencies above.
+# (See though arbor-sim/arbor issue #678).
+
+function(_append_property target property)
+    set(p_append ${ARGN})
+    get_target_property(p ${target} ${property})
+    if(p)
+        list(APPEND p ${p_append})
     else()
-        set(_arbor_interface_libs ${_add_libs})
+        set(interface_libs ${p_append})
     endif()
-    set_target_properties(arbor::arbor PROPERTIES INTERFACE_LINK_LIBRARIES "${_arbor_interface_libs}")
-endif()
+    set_target_properties(${target} PROPERTIES ${property} "${p}")
+endfunction()
+
+_append_property(arbor::arbor INTERFACE_LINK_LIBRARIES @arbor_add_import_libs@)
+_append_property(arbor::arborenv INTERFACE_LINK_LIBRARIES @arborenv_add_import_libs@)
+
diff --git a/example/bench/CMakeLists.txt b/example/bench/CMakeLists.txt
index 5f07e0c7474363eac10f3ba55179da6de34e50d9..aa23ea36ba754921507722414907282a1f8f5408 100644
--- a/example/bench/CMakeLists.txt
+++ b/example/bench/CMakeLists.txt
@@ -1,4 +1,4 @@
 add_executable(bench EXCLUDE_FROM_ALL bench.cpp recipe.cpp parameters.cpp)
 add_dependencies(examples bench)
 
-target_link_libraries(bench PRIVATE arbor arbor-sup ext-tclap ext-json)
+target_link_libraries(bench PRIVATE arbor arborenv arbor-sup ext-tclap ext-json)
diff --git a/example/bench/bench.cpp b/example/bench/bench.cpp
index 4ab37a5e8fbfcaf684748763779aba1da097a6ab..af985c66a07af9756778dbca7f10bfee6608d6a4 100644
--- a/example/bench/bench.cpp
+++ b/example/bench/bench.cpp
@@ -18,12 +18,15 @@
 #include <arbor/version.hpp>
 
 
-#include <sup/concurrency.hpp>
-#include <sup/gpu.hpp>
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
+
 #ifdef ARB_MPI_ENABLED
-#include <sup/with_mpi.hpp>
+#include <mpi.h>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 #include "parameters.hpp"
@@ -36,20 +39,20 @@ int main(int argc, char** argv) {
 
     try {
         arb::proc_allocation resources;
-        if (auto nt = sup::get_env_num_threads()) {
+        if (auto nt = arbenv::get_env_num_threads()) {
             resources.num_threads = nt;
         }
         else {
-            resources.num_threads = sup::thread_concurrency();
+            resources.num_threads = arbenv::thread_concurrency();
         }
 
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
-        resources.gpu_id = sup::find_private_gpu(MPI_COMM_WORLD);
+        arbenv::with_mpi guard(argc, argv, false);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
         auto context = arb::make_context(resources, MPI_COMM_WORLD);
         is_root = arb::rank(context) == 0;
 #else
-        resources.gpu_id = sup::default_gpu();
+        resources.gpu_id = arbenv::default_gpu();
         auto context = arb::make_context(resources);
 #endif
 #ifdef ARB_PROFILE_ENABLED
diff --git a/example/brunel/CMakeLists.txt b/example/brunel/CMakeLists.txt
index d94f5f3e18faed8b231a97233ddbbf793c823ae1..99b267435b2cb7b044d032eb3dcb413cade03f31 100644
--- a/example/brunel/CMakeLists.txt
+++ b/example/brunel/CMakeLists.txt
@@ -3,4 +3,4 @@ add_executable(brunel-miniapp EXCLUDE_FROM_ALL
     io.cpp)
 add_dependencies(examples brunel-miniapp)
 
-target_link_libraries(brunel-miniapp PRIVATE arbor arbor-sup ext-tclap)
+target_link_libraries(brunel-miniapp PRIVATE arbor arborenv arbor-sup ext-tclap)
diff --git a/example/brunel/brunel_miniapp.cpp b/example/brunel/brunel_miniapp.cpp
index b988c6dfff00e4c9ff5ae257dff657a97efd50de..2041b7bf13b5c752c6e925b4e895417c0e98a6c6 100644
--- a/example/brunel/brunel_miniapp.cpp
+++ b/example/brunel/brunel_miniapp.cpp
@@ -18,16 +18,18 @@
 #include <arbor/simulation.hpp>
 #include <arbor/version.hpp>
 
-#include <sup/concurrency.hpp>
-#include <sup/gpu.hpp>
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
 #include <sup/path.hpp>
 #include <sup/spike_emitter.hpp>
 #include <sup/strsub.hpp>
+
 #ifdef ARB_MPI_ENABLED
-#include <sup/with_mpi.hpp>
 #include <mpi.h>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 #include "io.hpp"
@@ -189,21 +191,21 @@ int main(int argc, char** argv) {
 
     try {
         arb::proc_allocation resources;
-        if (auto nt = sup::get_env_num_threads()) {
+        if (auto nt = arbenv::get_env_num_threads()) {
             resources.num_threads = nt;
         }
         else {
-            resources.num_threads = sup::thread_concurrency();
+            resources.num_threads = arbenv::thread_concurrency();
         }
 
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
-        resources.gpu_id = sup::find_private_gpu(MPI_COMM_WORLD);
+        arbenv::with_mpi guard(argc, argv, false);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
         auto context = arb::make_context(resources, MPI_COMM_WORLD);
         rank = arb::rank(context);
         root = rank==0;
 #else
-        resources.gpu_id = sup::default_gpu();
+        resources.gpu_id = arbenv::default_gpu();
         auto context = arb::make_context(resources);
 #endif
 
diff --git a/example/dryrun/CMakeLists.txt b/example/dryrun/CMakeLists.txt
index 99ed83f0c7d8837acc5aee926ed68e93e19d95e4..9d9cb559efec7a4f506881e13df8fbb3b9f64126 100644
--- a/example/dryrun/CMakeLists.txt
+++ b/example/dryrun/CMakeLists.txt
@@ -1,4 +1,4 @@
 add_executable(dryrun EXCLUDE_FROM_ALL dryrun.cpp)
 add_dependencies(examples dryrun)
 
-target_link_libraries(dryrun PRIVATE arbor arbor-sup ext-json)
+target_link_libraries(dryrun PRIVATE arbor arborenv arbor-sup ext-json)
diff --git a/example/dryrun/dryrun.cpp b/example/dryrun/dryrun.cpp
index 0077a91b3a7b53ba91d14dc6828e4ef7d49c24f4..92203545756391163c1dc8295724381235c24a06 100644
--- a/example/dryrun/dryrun.cpp
+++ b/example/dryrun/dryrun.cpp
@@ -29,7 +29,7 @@
 
 #ifdef ARB_MPI_ENABLED
 #include <mpi.h>
-#include <sup/with_mpi.hpp>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 using arb::cell_gid_type;
@@ -189,7 +189,7 @@ struct cell_stats {
 int main(int argc, char** argv) {
     try {
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
+        arbenv::with_mpi guard(argc, argv, false);
 #endif
         bool root = true;
         auto params = read_options(argc, argv);
diff --git a/example/miniapp/CMakeLists.txt b/example/miniapp/CMakeLists.txt
index d642cabf3df099709b453c2ce335044067b36002..ad78e8259a5e9d0dfd0976471e23fbdde6940465 100644
--- a/example/miniapp/CMakeLists.txt
+++ b/example/miniapp/CMakeLists.txt
@@ -7,4 +7,4 @@ add_executable(miniapp EXCLUDE_FROM_ALL
 )
 add_dependencies(examples miniapp)
 
-target_link_libraries(miniapp PRIVATE arbor arbor-sup ext-tclap ext-json)
+target_link_libraries(miniapp PRIVATE arbor arborenv arbor-sup ext-tclap ext-json)
diff --git a/example/miniapp/miniapp.cpp b/example/miniapp/miniapp.cpp
index a85a408ccc21a46979af6b233b95c694f74ed735..5fe1cc5fc5934904e050beffa9dba5c8d1d8db2d 100644
--- a/example/miniapp/miniapp.cpp
+++ b/example/miniapp/miniapp.cpp
@@ -17,16 +17,18 @@
 #include <arbor/version.hpp>
 
 
-#include <sup/concurrency.hpp>
-#include <sup/gpu.hpp>
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
 #include <sup/path.hpp>
 #include <sup/spike_emitter.hpp>
 #include <sup/strsub.hpp>
+
 #ifdef ARB_MPI_ENABLED
-#include <sup/with_mpi.hpp>
 #include <mpi.h>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 #include "io.hpp"
@@ -49,20 +51,20 @@ int main(int argc, char** argv) {
 
     try {
         arb::proc_allocation resources;
-        if (auto nt = sup::get_env_num_threads()) {
+        if (auto nt = arbenv::get_env_num_threads()) {
             resources.num_threads = nt;
         }
         else {
-            resources.num_threads = sup::thread_concurrency();
+            resources.num_threads = arbenv::thread_concurrency();
         }
 
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
-        resources.gpu_id = sup::find_private_gpu(MPI_COMM_WORLD);
+        arbenv::with_mpi guard(argc, argv, false);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
         auto context = arb::make_context(resources, MPI_COMM_WORLD);
         root = arb::rank(context) == 0;
 #else
-        resources.gpu_id = sup::default_gpu();
+        resources.gpu_id = arbenv::default_gpu();
         auto context = arb::make_context(resources);
 #endif
 
diff --git a/example/ring/CMakeLists.txt b/example/ring/CMakeLists.txt
index 48978586327338ac78beecec04c04a2abb0b1cfb..f35cba800266ac5236b732e82a24da1f6de24de4 100644
--- a/example/ring/CMakeLists.txt
+++ b/example/ring/CMakeLists.txt
@@ -1,4 +1,4 @@
 add_executable(ring EXCLUDE_FROM_ALL ring.cpp)
 add_dependencies(examples ring)
 
-target_link_libraries(ring PRIVATE arbor arbor-sup ext-tclap ext-json)
+target_link_libraries(ring PRIVATE arbor arborenv arbor-sup ext-tclap ext-json)
diff --git a/example/ring/ring.cpp b/example/ring/ring.cpp
index 9df416d83703aef320f761f4d4732ddf9b30017f..09d6e78b9666a3faeee8e8da4f67827e6c0c9cbd 100644
--- a/example/ring/ring.cpp
+++ b/example/ring/ring.cpp
@@ -21,8 +21,9 @@
 #include <arbor/recipe.hpp>
 #include <arbor/version.hpp>
 
-#include <sup/concurrency.hpp>
-#include <sup/gpu.hpp>
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
 
@@ -30,7 +31,7 @@
 
 #ifdef ARB_MPI_ENABLED
 #include <mpi.h>
-#include <sup/with_mpi.hpp>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 using arb::cell_gid_type;
@@ -161,20 +162,20 @@ int main(int argc, char** argv) {
         bool root = true;
 
         arb::proc_allocation resources;
-        if (auto nt = sup::get_env_num_threads()) {
+        if (auto nt = arbenv::get_env_num_threads()) {
             resources.num_threads = nt;
         }
         else {
-            resources.num_threads = sup::thread_concurrency();
+            resources.num_threads = arbenv::thread_concurrency();
         }
 
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
-        resources.gpu_id = sup::find_private_gpu(MPI_COMM_WORLD);
+        arbenv::with_mpi guard(argc, argv, false);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
         auto context = arb::make_context(resources, MPI_COMM_WORLD);
         root = arb::rank(context) == 0;
 #else
-        resources.gpu_id = sup::default_gpu();
+        resources.gpu_id = arbenv::default_gpu();
         auto context = arb::make_context(resources);
 #endif
 
diff --git a/sup/CMakeLists.txt b/sup/CMakeLists.txt
index 6ea4decf7621ae5809c9f86d26ef51b9ac2aaac2..9e014f07681e8d693cb83a8a4b7b0b0c1760f281 100644
--- a/sup/CMakeLists.txt
+++ b/sup/CMakeLists.txt
@@ -1,22 +1,11 @@
 set(sup-sources
-    affinity.cpp
-    concurrency.cpp
     glob.cpp
-    default_gpu.cpp
     ioutil.cpp
     json_meter.cpp
     path.cpp
     spike_emitter.cpp
 )
 
-if(ARB_WITH_GPU)
-    list(APPEND sup-sources gpu_uuid.cpp)
-endif()
-
-if(ARB_WITH_MPI)
-    list(APPEND sup-sources private_gpu.cpp)
-endif()
-
 add_library(arbor-sup ${sup-sources})
 
 # Compile sup library with the same optimization flags as libarbor.
@@ -27,26 +16,5 @@ target_link_libraries(arbor-sup PUBLIC ext-json arbor)
 
 target_include_directories(arbor-sup PUBLIC include)
 
-if(ARB_WITH_MPI)
-    target_compile_definitions(arbor-sup PRIVATE ARB_HAVE_MPI)
-endif()
-if(ARB_WITH_GPU)
-    target_compile_definitions(arbor-sup PRIVATE ARB_HAVE_GPU)
-    # So that cpp and hpp files can use cuda.h and cuda_runtime.h
-    target_include_directories(arbor-sup PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
-
-    # The sup lib needs to use the CUDA NVML library for CUDA 9
-    if (${CUDA_VERSION_MAJOR} LESS 10)
-        find_library(nvml_lib_path
-                     NAMES nvidia-ml
-                     PATHS ${CUDA_TOOLKIT_ROOT_DIR}
-                     PATH_SUFFIXES lib64/stubs)
-        if (NOT nvml_lib_path)
-            message(FATAL_ERROR "Unable to find CUDA NVML library: libnvida-ml.so")
-        endif()
-        target_link_libraries(arbor-sup PUBLIC ${nvml_lib_path})
-    endif()
-endif()
-
 set_target_properties(arbor-sup PROPERTIES OUTPUT_NAME arborsup)
 
diff --git a/sup/glob.cpp b/sup/glob.cpp
index 185cfc926b2c9ff204a79d5925373540cf0664a0..ecec061e17fc20226fd6516b0dade576af0e82d3 100644
--- a/sup/glob.cpp
+++ b/sup/glob.cpp
@@ -16,8 +16,10 @@ extern "C" {
 
 #include <cerrno>
 
+#include <arbor/util/scope_exit.hpp>
 #include <sup/path.hpp>
-#include <sup/scope_exit.hpp>
+
+using arb::util::on_scope_exit;
 
 namespace sup {
 
diff --git a/sup/include/sup/affinity.hpp b/sup/include/sup/affinity.hpp
deleted file mode 100644
index 49707c86e5401090f1a1ffc0bf1979d85d1e97f6..0000000000000000000000000000000000000000
--- a/sup/include/sup/affinity.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-namespace sup {
-
-// The list of logical processors for which the calling thread has affinity.
-// If calling from the main thread at application start up, before
-// attempting to change thread affinity, may produce unreliable
-// results.
-//  - beware thread pinning or custom job scheduler affinity
-//    flags that assign threads to specific cores.
-//
-// Returns an empty vector if unable to determine the number of
-// available cores.
-std::vector<int> get_affinity();
-
-} // namespace sup
diff --git a/test/unit-distributed/CMakeLists.txt b/test/unit-distributed/CMakeLists.txt
index 6b84f561ef4b990c104c561d88bce19dbf3ed0a2..d93748c9ef2cab84544e5ce54213596bae0ed24e 100644
--- a/test/unit-distributed/CMakeLists.txt
+++ b/test/unit-distributed/CMakeLists.txt
@@ -13,7 +13,7 @@ add_dependencies(tests unit-local)
 
 target_compile_options(unit-local PRIVATE ${ARB_CXXOPT_ARCH})
 target_compile_definitions(unit-local PRIVATE TEST_LOCAL)
-target_link_libraries(unit-local PRIVATE gtest arbor arbor-sup arbor-private-headers)
+target_link_libraries(unit-local PRIVATE gtest arbor arborenv arbor-sup arbor-private-headers)
 
 if(ARB_WITH_MPI)
     add_executable(unit-mpi EXCLUDE_FROM_ALL ${unit-distributed_sources})
@@ -21,6 +21,6 @@ if(ARB_WITH_MPI)
 
     target_compile_options(unit-mpi PRIVATE ${ARB_CXXOPT_ARCH})
     target_compile_definitions(unit-mpi PRIVATE TEST_MPI)
-    target_link_libraries(unit-mpi PRIVATE gtest arbor arbor-sup arbor-private-headers)
+    target_link_libraries(unit-mpi PRIVATE gtest arbor arborenv arbor-sup arbor-private-headers)
 endif()
 
diff --git a/test/unit-distributed/test.cpp b/test/unit-distributed/test.cpp
index 6dbbd01840dbf95ef6de021f05b0aa9f0d75f708..cc5a52d4cb4af2e0a611dbc134e39ee33c3e6cdb 100644
--- a/test/unit-distributed/test.cpp
+++ b/test/unit-distributed/test.cpp
@@ -9,8 +9,9 @@
 
 #include <sup/ioutil.hpp>
 #include <sup/tinyopt.hpp>
+
 #ifdef TEST_MPI
-#include <sup/with_mpi.hpp>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 #include "distributed_context.hpp"
@@ -35,7 +36,7 @@ int main(int argc, char **argv) {
     alloc.gpu_id = -1;
 
 #ifdef TEST_MPI
-    sup::with_mpi guard(argc, argv, false);
+    arbenv::with_mpi guard(argc, argv, false);
     g_context = arb::make_context(alloc, MPI_COMM_WORLD);
 #elif defined(TEST_LOCAL)
     g_context = arb::make_context(alloc);
diff --git a/test/unit/test_scope_exit.cpp b/test/unit/test_scope_exit.cpp
index 12b8c140aed5dbb1f32f58ec202e2c9ed97157ad..7f1890f09b31a4a11c7a8aa16069fce2ba07dd78 100644
--- a/test/unit/test_scope_exit.cpp
+++ b/test/unit/test_scope_exit.cpp
@@ -2,9 +2,9 @@
 
 #include "../gtest.h"
 
-#include <sup/scope_exit.hpp>
+#include <arbor/util/scope_exit.hpp>
 
-using sup::on_scope_exit;
+using arb::util::on_scope_exit;
 
 TEST(scope_exit, basic) {
     bool a = false;