Repository: computationalgeography/lue (language: python)

Issue:
Split `lue_py_framework` library
Build of Conda package by CondaForge infrastructure fails on Windows due to this error while linking `lue_py_framework`:

```
fatal error C1002: compiler is out of heap space in pass 2
```

Split the library into parts. E.g.:

- `lue_py_framework_local_operation`
- `lue_py_framework_focal_operation`
- etc


Discussion / hints:

Fix patch:
diff --git a/source/framework/python/CMakeLists.txt b/source/framework/python/CMakeLists.txt
index 55a618ec6..cd434f6e2 100644
--- a/source/framework/python/CMakeLists.txt
+++ b/source/framework/python/CMakeLists.txt
@@ -25,54 +25,37 @@ file(GENERATE
 
 # A pybind11 SHARED library is a CMake runtime output artifact. Target properties to set/get are
 # RUNTIME_OUTPUT_DIRECTORY and RUNTIME_OUTPUT_NAME.
-add_library(lue_py_framework SHARED
-    src/algorithm/accu.cpp
-    src/algorithm/accu3.cpp
-    src/algorithm/accu_fraction.cpp
-    src/algorithm/accu_info.cpp
-    src/algorithm/accu_info3.cpp
-    src/algorithm/accu_threshold.cpp
-    src/algorithm/accu_threshold3.cpp
-    src/algorithm/all.cpp
-    src/algorithm/array_partition_id.cpp
-    src/algorithm/aspect.cpp
-    src/algorithm/atan2.cpp
-    src/algorithm/d8_flow_direction.cpp
-    src/algorithm/downstream.cpp
-    src/algorithm/downstream_distance.cpp
-    src/algorithm/inflow_count.cpp
-    src/algorithm/inflow_count3.cpp
-    src/algorithm/inter_partition_stream.cpp
-    src/algorithm/iterate_per_element.cpp
-    src/algorithm/locality_id.cpp
-    src/algorithm/normal.cpp
-    src/algorithm/slope.cpp
-    src/algorithm/timestamp.cpp
-    src/algorithm/uniform.cpp
-    src/algorithm/upstream.cpp
-    src/algorithm/where.cpp
 
+add_library(lue_py_framework_focal_operation SHARED
     src/algorithm/focal_operation.cpp
+    src/algorithm/focal_operation/aspect.cpp
     src/algorithm/focal_operation/focal_diversity.cpp
     src/algorithm/focal_operation/focal_high_pass.cpp
     src/algorithm/focal_operation/focal_majority.cpp
     src/algorithm/focal_operation/focal_maximum.cpp
     src/algorithm/focal_operation/focal_mean.cpp
     src/algorithm/focal_operation/focal_minimum.cpp
+    src/algorithm/focal_operation/slope.cpp
     src/algorithm/focal_operation/focal_sum.cpp
+)
 
+add_library(lue_py_framework_global_operation SHARED
     src/algorithm/global_operation.cpp
     src/algorithm/global_operation/reclassify.cpp
     src/algorithm/global_operation/maximum.cpp
     src/algorithm/global_operation/minimum.cpp
     src/algorithm/global_operation/sum.cpp
+)
 
+add_library(lue_py_framework_local_operation SHARED
     src/algorithm/local_operation.cpp
     src/algorithm/local_operation/abs.cpp
     src/algorithm/local_operation/acos.cpp
     src/algorithm/local_operation/add.cpp
+    src/algorithm/local_operation/all.cpp
     src/algorithm/local_operation/asin.cpp
     src/algorithm/local_operation/atan.cpp
+    src/algorithm/local_operation/atan2.cpp
     src/algorithm/local_operation/cast.cpp
     src/algorithm/local_operation/cell_index.cpp
     src/algorithm/local_operation/cos.cpp
@@ -83,6 +66,7 @@ add_library(lue_py_framework SHARED
     src/algorithm/local_operation/floor.cpp
     src/algorithm/local_operation/greater_than.cpp
     src/algorithm/local_operation/greater_than_equal_to.cpp
+    src/algorithm/local_operation/iterate_per_element.cpp
     src/algorithm/local_operation/less_than.cpp
     src/algorithm/local_operation/less_than_equal_to.cpp
     src/algorithm/local_operation/log.cpp
@@ -102,12 +86,32 @@ add_library(lue_py_framework SHARED
     src/algorithm/local_operation/tan.cpp
     src/algorithm/local_operation/unique_id.cpp
     src/algorithm/local_operation/valid.cpp
+    src/algorithm/local_operation/where.cpp
+)
 
+add_library(lue_py_framework_routing_operation SHARED
     src/algorithm/routing_operation.cpp
+    src/algorithm/routing_operation/accu.cpp
+    src/algorithm/routing_operation/accu3.cpp
+    src/algorithm/routing_operation/accu_fraction.cpp
+    src/algorithm/routing_operation/accu_info.cpp
+    src/algorithm/routing_operation/accu_info3.cpp
+    src/algorithm/routing_operation/accu_threshold.cpp
+    src/algorithm/routing_operation/accu_threshold3.cpp
+    src/algorithm/routing_operation/d8_flow_direction.cpp
     src/algorithm/routing_operation/decreasing_order.cpp
+    src/algorithm/routing_operation/downstream.cpp
+    src/algorithm/routing_operation/downstream_distance.cpp
     src/algorithm/routing_operation/first_n.cpp
+    src/algorithm/routing_operation/inflow_count.cpp
+    src/algorithm/routing_operation/inflow_count3.cpp
+    src/algorithm/routing_operation/inter_partition_stream.cpp
     src/algorithm/routing_operation/kinematic_wave.cpp
+    src/algorithm/routing_operation/upstream.cpp
 
+)
+
+add_library(lue_py_framework_zonal_operation SHARED
     src/algorithm/zonal_operation.cpp
     src/algorithm/zonal_operation/clump.cpp
     src/algorithm/zonal_operation/zonal_area.cpp
@@ -119,6 +123,33 @@ add_library(lue_py_framework SHARED
     src/algorithm/zonal_operation/zonal_normal.cpp
     src/algorithm/zonal_operation/zonal_sum.cpp
     src/algorithm/zonal_operation/zonal_uniform.cpp
+)
+
+foreach(name focal global local routing zonal)
+    add_library(lue::py_framework_${name}_operation ALIAS lue_py_framework_${name}_operation)
+
+    target_compile_definitions(lue_py_framework_${name}_operation
+        PRIVATE
+            NPY_NO_DEPRECATED_API=${LUE_NPY_NO_DEPRECATED_API}
+    )
+
+    target_link_libraries(lue_py_framework_${name}_operation
+        PRIVATE
+            lue::framework_${name}_operation
+            pybind11::module
+            pybind11::thin_lto
+            pybind11::opt_size
+            pybind11::windows_extras
+            Python::Python
+    )
+endforeach()
+
+add_library(lue_py_framework SHARED
+    src/algorithm/array_partition_id.cpp
+    src/algorithm/locality_id.cpp
+    src/algorithm/timestamp.cpp
+    src/algorithm/normal.cpp
+    src/algorithm/uniform.cpp
 
     src/core/create_partitioned_array.cpp
     src/core/partitioned_array.cpp
@@ -164,12 +195,6 @@ target_compile_definitions(lue_py_framework
         NPY_NO_DEPRECATED_API=${LUE_NPY_NO_DEPRECATED_API}
 )
 
-set_target_properties(lue_py_framework
-    PROPERTIES
-        VERSION ${LUE_VERSION}
-        SOVERSION ${LUE_VERSION_MAJOR}
-)
-
 target_include_directories(lue_py_framework
     PRIVATE
         ${CMAKE_CURRENT_SOURCE_DIR}/../../data_model/python/include
@@ -181,11 +206,12 @@ target_include_directories(lue_py_framework
 
 target_link_libraries(lue_py_framework
     PRIVATE
-        lue::framework_focal_operation
-        lue::framework_global_operation
-        lue::framework_local_operation
-        lue::framework_routing_operation
-        lue::framework_zonal_operation
+        lue::py_framework_focal_operation
+        lue::py_framework_global_operation
+        lue::py_framework_local_operation
+        lue::py_framework_routing_operation
+        lue::py_framework_zonal_operation
+        lue::framework_local_operation  # array and scalar use local operations in operators
         lue::framework_io
         lue::framework_model
         pybind11::module
@@ -195,14 +221,37 @@ target_link_libraries(lue_py_framework
         Python::Python
 )
 
+set_target_properties(
+    lue_py_framework_focal_operation
+    lue_py_framework_global_operation
+    lue_py_framework_local_operation
+    lue_py_framework_routing_operation
+    lue_py_framework_zonal_operation
+    lue_py_framework
+    PROPERTIES
+        VERSION ${LUE_VERSION}
+        SOVERSION ${LUE_VERSION_MAJOR}
+)
+
 # TODO pybind11_strip does not work on macOS, when building using Conda packages
 #      The resulting binary can't be imported. Results in "killed: 9".
 if(NOT MSVC AND NOT APPLE AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo)
     pybind11_strip(lue_py_framework)
+    pybind11_strip(lue_py_framework_focal_operation)
+    pybind11_strip(lue_py_framework_global_operation)
+    pybind11_strip(lue_py_framework_local_operation)
+    pybind11_strip(lue_py_framework_zonal_operation)
+    pybind11_strip(lue_py_framework_routing_operation)
 endif()
 
 lue_install_runtime_libraries(
-    TARGETS lue_py_framework
+    TARGETS
+        lue_py_framework
+        lue_py_framework_focal_operation
+        lue_py_framework_global_operation
+        lue_py_framework_local_operation
+        lue_py_framework_zonal_operation
+        lue_py_framework_routing_operation
 )
 
 if(LUE_PYTHON_FROM_CONDA AND NOT DEFINED ENV{CONDA_BUILD})
@@ -210,7 +259,13 @@ if(LUE_PYTHON_FROM_CONDA AND NOT DEFINED ENV{CONDA_BUILD})
     # to include the location of the Python libraries. But if we are building a Conda package,
     # we assume Conda takes care of RPATH handling.
     set_property(
-        TARGET lue_py_framework
+        TARGET
+            lue_py_framework
+            lue_py_framework_focal_operation
+            lue_py_framework_global_operation
+            lue_py_framework_local_operation
+            lue_py_framework_zonal_operation
+            lue_py_framework_routing_operation
         APPEND
         PROPERTY INSTALL_RPATH
             ${Python_LIBRARY_DIRS}
diff --git a/source/framework/python/src/algorithm/focal_operation.cpp b/source/framework/python/src/algorithm/focal_operation.cpp
index eb702f5f5..32a0a4ac0 100644
--- a/source/framework/python/src/algorithm/focal_operation.cpp
+++ b/source/framework/python/src/algorithm/focal_operation.cpp
@@ -3,23 +3,27 @@
 
 namespace lue::framework {
 
+    void bind_aspect(pybind11::module& module);
     void bind_focal_diversity(pybind11::module& module);
     void bind_focal_high_pass(pybind11::module& module);
     void bind_focal_majority(pybind11::module& module);
     void bind_focal_maximum(pybind11::module& module);
     void bind_focal_mean(pybind11::module& module);
     void bind_focal_minimum(pybind11::module& module);
+    void bind_slope(pybind11::module& module);
     void bind_focal_sum(pybind11::module& module);
 
 
-    void bind_focal_operations(pybind11::module& module)
+    PYBIND11_EXPORT void bind_focal_operations(pybind11::module& module)
     {
+        bind_aspect(module);
         bind_focal_diversity(module);
         bind_focal_high_pass(module);
         bind_focal_majority(module);
         bind_focal_maximum(module);
         bind_focal_mean(module);
         bind_focal_minimum(module);
+        bind_slope(module);
         bind_focal_sum(module);
     }
 
diff --git a/source/framework/python/src/algorithm/aspect.cpp b/source/framework/python/src/algorithm/focal_operation/aspect.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/aspect.cpp
rename to source/framework/python/src/algorithm/focal_operation/aspect.cpp
diff --git a/source/framework/python/src/algorithm/slope.cpp b/source/framework/python/src/algorithm/focal_operation/slope.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/slope.cpp
rename to source/framework/python/src/algorithm/focal_operation/slope.cpp
diff --git a/source/framework/python/src/algorithm/global_operation.cpp b/source/framework/python/src/algorithm/global_operation.cpp
index 3775dd522..66384b4a1 100644
--- a/source/framework/python/src/algorithm/global_operation.cpp
+++ b/source/framework/python/src/algorithm/global_operation.cpp
@@ -9,7 +9,7 @@ namespace lue::framework {
     void bind_sum(pybind11::module& module);
 
 
-    void bind_global_operations(pybind11::module& module)
+    PYBIND11_EXPORT void bind_global_operations(pybind11::module& module)
     {
         bind_reclassify(module);
         bind_maximum(module);
diff --git a/source/framework/python/src/algorithm/local_operation.cpp b/source/framework/python/src/algorithm/local_operation.cpp
index de284810d..d650abdc2 100644
--- a/source/framework/python/src/algorithm/local_operation.cpp
+++ b/source/framework/python/src/algorithm/local_operation.cpp
@@ -1,23 +1,12 @@
 #include <pybind11/pybind11.h>
 
 
-// TODO These are not all local operations. Split them over multiple
-//     modules, or rename this module.
 namespace lue::framework {
 
     void bind_abs(pybind11::module& module);
-    void bind_accu(pybind11::module& module);
-    void bind_accu3(pybind11::module& module);
-    void bind_accu_fraction(pybind11::module& module);
-    void bind_accu_info(pybind11::module& module);
-    void bind_accu_info3(pybind11::module& module);
-    void bind_accu_threshold(pybind11::module& module);
-    void bind_accu_threshold3(pybind11::module& module);
     void bind_acos(pybind11::module& module);
     void bind_add(pybind11::module& module);
     void bind_all(pybind11::module& module);
-    void bind_array_partition_id(pybind11::module& module);
-    void bind_aspect(pybind11::module& module);
     void bind_asin(pybind11::module& module);
     void bind_atan(pybind11::module& module);
     void bind_atan2(pybind11::module& module);
@@ -25,22 +14,15 @@ namespace lue::framework {
     void bind_cos(pybind11::module& module);
     void bind_ceil(pybind11::module& module);
     void bind_cell_index(pybind11::module& module);
-    void bind_d8_flow_direction(pybind11::module& module);
     void bind_divide(pybind11::module& module);
-    void bind_downstream(pybind11::module& module);
-    void bind_downstream_distance(pybind11::module& module);
     void bind_equal_to(pybind11::module& module);
     void bind_exp(pybind11::module& module);
     void bind_floor(pybind11::module& module);
     void bind_greater_than(pybind11::module& module);
     void bind_greater_than_equal_to(pybind11::module& module);
-    void bind_inflow_count(pybind11::module& module);
-    void bind_inflow_count3(pybind11::module& module);
-    void bind_inter_partition_stream(pybind11::module& module);
     void bind_iterate_per_element(pybind11::module& module);
     void bind_less_than(pybind11::module& module);
     void bind_less_than_equal_to(pybind11::module& module);
-    void bind_locality_id(pybind11::module& module);
     void bind_log(pybind11::module& module);
     void bind_log10(pybind11::module& module);
     void bind_logical_and(pybind11::module& module);
@@ -49,38 +31,24 @@ namespace lue::framework {
     void bind_logical_not(pybind11::module& module);
     void bind_multiply(pybind11::module& module);
     void bind_negate(pybind11::module& module);
-    void bind_normal(pybind11::module& module);
     void bind_not_equal_to(pybind11::module& module);
     void bind_pow(pybind11::module& module);
     void bind_round(pybind11::module& module);
     void bind_sin(pybind11::module& module);
-    void bind_slope(pybind11::module& module);
     void bind_sqrt(pybind11::module& module);
     void bind_subtract(pybind11::module& module);
     void bind_tan(pybind11::module& module);
-    void bind_timestamp(pybind11::module& module);
-    void bind_uniform(pybind11::module& module);
     void bind_unique_id(pybind11::module& module);
-    void bind_upstream(pybind11::module& module);
     void bind_valid(pybind11::module& module);
     void bind_where(pybind11::module& module);
 
 
-    void bind_local_operations(pybind11::module& module)
+    PYBIND11_EXPORT void bind_local_operations(pybind11::module& module)
     {
         bind_abs(module);
-        bind_accu(module);
-        bind_accu3(module);
-        bind_accu_fraction(module);
-        bind_accu_info(module);
-        bind_accu_info3(module);
-        bind_accu_threshold(module);
-        bind_accu_threshold3(module);
         bind_acos(module);
         bind_add(module);
         bind_all(module);
-        bind_array_partition_id(module);
-        bind_aspect(module);
         bind_asin(module);
         bind_atan(module);
         bind_atan2(module);
@@ -88,22 +56,15 @@ namespace lue::framework {
         bind_ceil(module);
         bind_cell_index(module);
         bind_cos(module);
-        bind_d8_flow_direction(module);
         bind_divide(module);
-        bind_downstream(module);
-        bind_downstream_distance(module);
         bind_equal_to(module);
         bind_exp(module);
         bind_floor(module);
         bind_greater_than(module);
         bind_greater_than_equal_to(module);
-        bind_inflow_count(module);
-        bind_inflow_count3(module);
-        bind_inter_partition_stream(module);
         bind_iterate_per_element(module);
         bind_less_than(module);
         bind_less_than_equal_to(module);
-        bind_locality_id(module);
         bind_log(module);
         bind_log10(module);
         bind_logical_and(module);
@@ -112,19 +73,14 @@ namespace lue::framework {
         bind_logical_not(module);
         bind_multiply(module);
         bind_negate(module);
-        bind_normal(module);
         bind_not_equal_to(module);
         bind_pow(module);
         bind_round(module);
         bind_sin(module);
-        bind_slope(module);
         bind_sqrt(module);
         bind_subtract(module);
         bind_tan(module);
-        bind_timestamp(module);
-        bind_uniform(module);
         bind_unique_id(module);
-        bind_upstream(module);
         bind_valid(module);
         bind_where(module);
     }
diff --git a/source/framework/python/src/algorithm/all.cpp b/source/framework/python/src/algorithm/local_operation/all.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/all.cpp
rename to source/framework/python/src/algorithm/local_operation/all.cpp
diff --git a/source/framework/python/src/algorithm/atan2.cpp b/source/framework/python/src/algorithm/local_operation/atan2.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/atan2.cpp
rename to source/framework/python/src/algorithm/local_operation/atan2.cpp
diff --git a/source/framework/python/src/algorithm/iterate_per_element.cpp b/source/framework/python/src/algorithm/local_operation/iterate_per_element.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/iterate_per_element.cpp
rename to source/framework/python/src/algorithm/local_operation/iterate_per_element.cpp
diff --git a/source/framework/python/src/algorithm/where.cpp b/source/framework/python/src/algorithm/local_operation/where.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/where.cpp
rename to source/framework/python/src/algorithm/local_operation/where.cpp
diff --git a/source/framework/python/src/algorithm/routing_operation.cpp b/source/framework/python/src/algorithm/routing_operation.cpp
index 3bf4a6983..8b56185f0 100644
--- a/source/framework/python/src/algorithm/routing_operation.cpp
+++ b/source/framework/python/src/algorithm/routing_operation.cpp
@@ -3,16 +3,44 @@
 
 namespace lue::framework {
 
+    void bind_accu(pybind11::module& module);
+    void bind_accu3(pybind11::module& module);
+    void bind_accu_fraction(pybind11::module& module);
+    void bind_accu_info(pybind11::module& module);
+    void bind_accu_info3(pybind11::module& module);
+    void bind_accu_threshold(pybind11::module& module);
+    void bind_accu_threshold3(pybind11::module& module);
+    void bind_d8_flow_direction(pybind11::module& module);
     void bind_decreasing_order(pybind11::module& module);
+    void bind_downstream(pybind11::module& module);
+    void bind_downstream_distance(pybind11::module& module);
     void bind_first_n(pybind11::module& module);
+    void bind_inflow_count(pybind11::module& module);
+    void bind_inflow_count3(pybind11::module& module);
+    void bind_inter_partition_stream(pybind11::module& module);
     void bind_kinematic_wave(pybind11::module& module);
+    void bind_upstream(pybind11::module& module);
 
 
-    void bind_routing_operations(pybind11::module& module)
+    PYBIND11_EXPORT void bind_routing_operations(pybind11::module& module)
     {
+        bind_accu(module);
+        bind_accu3(module);
+        bind_accu_fraction(module);
+        bind_accu_info(module);
+        bind_accu_info3(module);
+        bind_accu_threshold(module);
+        bind_accu_threshold3(module);
+        bind_d8_flow_direction(module);
         bind_decreasing_order(module);
+        bind_downstream(module);
+        bind_downstream_distance(module);
         bind_first_n(module);
+        bind_inflow_count(module);
+        bind_inflow_count3(module);
+        bind_inter_partition_stream(module);
         bind_kinematic_wave(module);
+        bind_upstream(module);
     }
 
 }  // namespace lue::framework
diff --git a/source/framework/python/src/algorithm/accu.cpp b/source/framework/python/src/algorithm/routing_operation/accu.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/accu.cpp
rename to source/framework/python/src/algorithm/routing_operation/accu.cpp
diff --git a/source/framework/python/src/algorithm/accu3.cpp b/source/framework/python/src/algorithm/routing_operation/accu3.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/accu3.cpp
rename to source/framework/python/src/algorithm/routing_operation/accu3.cpp
diff --git a/source/framework/python/src/algorithm/accu_fraction.cpp b/source/framework/python/src/algorithm/routing_operation/accu_fraction.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/accu_fraction.cpp
rename to source/framework/python/src/algorithm/routing_operation/accu_fraction.cpp
diff --git a/source/framework/python/src/algorithm/accu_info.cpp b/source/framework/python/src/algorithm/routing_operation/accu_info.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/accu_info.cpp
rename to source/framework/python/src/algorithm/routing_operation/accu_info.cpp
diff --git a/source/framework/python/src/algorithm/accu_info3.cpp b/source/framework/python/src/algorithm/routing_operation/accu_info3.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/accu_info3.cpp
rename to source/framework/python/src/algorithm/routing_operation/accu_info3.cpp
diff --git a/source/framework/python/src/algorithm/accu_threshold.cpp b/source/framework/python/src/algorithm/routing_operation/accu_threshold.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/accu_threshold.cpp
rename to source/framework/python/src/algorithm/routing_operation/accu_threshold.cpp
diff --git a/source/framework/python/src/algorithm/accu_threshold3.cpp b/source/framework/python/src/algorithm/routing_operation/accu_threshold3.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/accu_threshold3.cpp
rename to source/framework/python/src/algorithm/routing_operation/accu_threshold3.cpp
diff --git a/source/framework/python/src/algorithm/d8_flow_direction.cpp b/source/framework/python/src/algorithm/routing_operation/d8_flow_direction.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/d8_flow_direction.cpp
rename to source/framework/python/src/algorithm/routing_operation/d8_flow_direction.cpp
diff --git a/source/framework/python/src/algorithm/downstream.cpp b/source/framework/python/src/algorithm/routing_operation/downstream.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/downstream.cpp
rename to source/framework/python/src/algorithm/routing_operation/downstream.cpp
diff --git a/source/framework/python/src/algorithm/downstream_distance.cpp b/source/framework/python/src/algorithm/routing_operation/downstream_distance.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/downstream_distance.cpp
rename to source/framework/python/src/algorithm/routing_operation/downstream_distance.cpp
diff --git a/source/framework/python/src/algorithm/inflow_count.cpp b/source/framework/python/src/algorithm/routing_operation/inflow_count.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/inflow_count.cpp
rename to source/framework/python/src/algorithm/routing_operation/inflow_count.cpp
diff --git a/source/framework/python/src/algorithm/inflow_count3.cpp b/source/framework/python/src/algorithm/routing_operation/inflow_count3.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/inflow_count3.cpp
rename to source/framework/python/src/algorithm/routing_operation/inflow_count3.cpp
diff --git a/source/framework/python/src/algorithm/inter_partition_stream.cpp b/source/framework/python/src/algorithm/routing_operation/inter_partition_stream.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/inter_partition_stream.cpp
rename to source/framework/python/src/algorithm/routing_operation/inter_partition_stream.cpp
diff --git a/source/framework/python/src/algorithm/upstream.cpp b/source/framework/python/src/algorithm/routing_operation/upstream.cpp
similarity index 100%
rename from source/framework/python/src/algorithm/upstream.cpp
rename to source/framework/python/src/algorithm/routing_operation/upstream.cpp
diff --git a/source/framework/python/src/algorithm/zonal_operation.cpp b/source/framework/python/src/algorithm/zonal_operation.cpp
index 5a0e3389b..791bc1eb3 100644
--- a/source/framework/python/src/algorithm/zonal_operation.cpp
+++ b/source/framework/python/src/algorithm/zonal_operation.cpp
@@ -15,7 +15,7 @@ namespace lue::framework {
     void bind_zonal_uniform(pybind11::module& module);
 
 
-    void bind_zonal_operations(pybind11::module& module)
+    PYBIND11_EXPORT void bind_zonal_operations(pybind11::module& module)
     {
         bind_clump(module);
         bind_zonal_area(module);
diff --git a/source/framework/python/src/submodule.cpp b/source/framework/python/src/submodule.cpp
index 3f47fe6ec..ca9a170bb 100644
--- a/source/framework/python/src/submodule.cpp
+++ b/source/framework/python/src/submodule.cpp
@@ -57,6 +57,12 @@ namespace lue::framework {
     void bind_serial_route(pybind11::module& module);
     void bind_wait_serial_route(pybind11::module& module);
 
+    void bind_array_partition_id(pybind11::module& module);
+    void bind_locality_id(pybind11::module& module);
+    void bind_normal(pybind11::module& module);
+    void bind_timestamp(pybind11::module& module);
+    void bind_uniform(pybind11::module& module);
+
     void bind_read_array(pybind11::module& module);
     void bind_write_array(pybind11::module& module);
 
@@ -108,6 +114,12 @@ namespace lue::framework {
         bind_read_array(submodule);
         bind_write_array(submodule);
 
+        bind_array_partition_id(submodule);
+        bind_locality_id(submodule);
+        bind_normal(submodule);
+        bind_timestamp(submodule);
+        bind_uniform(submodule);
+
         bind_model(submodule);
         bind_progressor(submodule);
         bind_simulate(submodule);


Test patch:
diff --git a/source/framework/api/c/test/CMakeLists.txt b/source/framework/api/c/test/CMakeLists.txt
index 965e28bbf..c0834976d 100644
--- a/source/framework/api/c/test/CMakeLists.txt
+++ b/source/framework/api/c/test/CMakeLists.txt
@@ -13,11 +13,10 @@ foreach(name ${names})
     add_executable(${test_name} ${module_name}.cpp)
 
     target_link_libraries(${test_name}
-        PUBLIC
-            lue::framework_api_c
         PRIVATE
+            lue::framework_api_c
             lue::framework_test
-            Boost::unit_test_framework
+            Boost::headers
     )
 
     add_test(NAME ${test_name}
@@ -46,6 +45,8 @@ foreach(name ${names})
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::framework_focal_operation>
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::framework_routing_operation>
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::framework_zonal_operation>
+                    PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:$<TARGET_FILE_DIR:hdf5::hdf5>/../bin>
+                    PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:${HWLOC_ROOT}/bin>
         )
     endif()
 endforeach()
diff --git a/source/framework/api/cxx/test/CMakeLists.txt b/source/framework/api/cxx/test/CMakeLists.txt
index 80ff75e82..33023f638 100644
--- a/source/framework/api/cxx/test/CMakeLists.txt
+++ b/source/framework/api/cxx/test/CMakeLists.txt
@@ -14,20 +14,18 @@ set(names
 
 foreach(name ${names})
     set(module_name ${name}_test)
-    string(REPLACE "/" "_" test_name ${module_name})
-    set(test_name ${scope}_${test_name})
+    string(REPLACE "/" "_" test_name ${scope}_${module_name})
 
     add_executable(${test_name} ${module_name}.cpp)
 
     target_link_libraries(${test_name}
-        PUBLIC
-            lue::framework_api_cxx
         PRIVATE
+            lue::framework_api_cxx
             lue_framework_algorithm_test
             # lue::framework_algorithm
             # lue::framework_global_operation  # unique
             lue::framework_test
-            Boost::unit_test_framework
+            Boost::headers
     )
 
     add_test(NAME ${test_name}
@@ -56,6 +54,8 @@ foreach(name ${names})
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::framework_focal_operation>
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::framework_routing_operation>
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::framework_zonal_operation>
+                    PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:$<TARGET_FILE_DIR:hdf5::hdf5>/../bin>
+                    PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:${HWLOC_ROOT}/bin>
         )
     endif()
 endforeach()
diff --git a/source/framework/api/python/test/CMakeLists.txt b/source/framework/api/python/test/CMakeLists.txt
index 84617b20d..5177cca79 100644
--- a/source/framework/api/python/test/CMakeLists.txt
+++ b/source/framework/api/python/test/CMakeLists.txt
@@ -22,6 +22,7 @@ set_property(
         ENVIRONMENT
             $<$<AND:$<NOT:$<PLATFORM_ID:Windows>>,$<STREQUAL:${HPX_WITH_MALLOC},tcmalloc>>:$<IF:$<PLATFORM_ID:Darwin>,DYLD_INSERT_LIBRARIES,LD_PRELOAD>=${TCMALLOC_LIBRARY}>
 )
+
 set_property(
     TEST
         lue_framework_api_python_test
@@ -32,7 +33,10 @@ set_property(
             PYTHONPATH=path_list_prepend:$<TARGET_FILE_DIR:lue::py>/..
             PYTHONPATH=path_list_prepend:$<TARGET_PROPERTY:lue::py_framework,SOURCE_DIR>/test
             PYTHONPATH=path_list_prepend:${CMAKE_CURRENT_SOURCE_DIR}
+            PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:$<TARGET_FILE_DIR:hdf5::hdf5>/../bin>
+            PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:${HWLOC_ROOT}/bin>
 )
+
 set_property(
     TEST
         lue_framework_api_python_test
diff --git a/source/framework/image_land/python/test/CMakeLists.txt b/source/framework/image_land/python/test/CMakeLists.txt
index 75f0b19a8..b5993eaf6 100644
--- a/source/framework/image_land/python/test/CMakeLists.txt
+++ b/source/framework/image_land/python/test/CMakeLists.txt
@@ -32,6 +32,8 @@ set_property(
             PYTHONPATH=path_list_prepend:$<TARGET_FILE_DIR:lue::py>/..
             PYTHONPATH=path_list_prepend:$<TARGET_PROPERTY:lue::py_framework,SOURCE_DIR>/test  # lue_unit_test.py
             PYTHONPATH=path_list_prepend:${CMAKE_CURRENT_SOURCE_DIR}
+            PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:$<TARGET_FILE_DIR:hdf5::hdf5>/../bin>
+            PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:${HWLOC_ROOT}/bin>
 )
 set_property(
     TEST
diff --git a/source/framework/image_land/test/CMakeLists.txt b/source/framework/image_land/test/CMakeLists.txt
index 57037e660..22df9d0a3 100644
--- a/source/framework/image_land/test/CMakeLists.txt
+++ b/source/framework/image_land/test/CMakeLists.txt
@@ -45,6 +45,8 @@ foreach(name ${names})
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::framework_routing_operation>
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::framework_zonal_operation>
                     PATH=path_list_prepend:$<TARGET_FILE_DIR:lue::image_land>
+                    PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:$<TARGET_FILE_DIR:hdf5::hdf5>/../bin>
+                    PATH=path_list_prepend:$<$<PLATFORM_ID:Windows>:${HWLOC_ROOT}/bin>
         )
     endif()
 endforeach()