Repository: pyswmm/Stormwater-Management-Model (language: python)

Issue:
Add thread count control to API
... And allow this value to be flexible during simulation time. 


Discussion / hints:
do you want to be a able to control the number of threads dynamically during the simulation @bemcdonnell 
@dickinsonre, I’d like this to be available before the simulation starts. It will be a easy function to add. Essentially, it will override the number in the input file. It will be useful in optimization mostly. Moreover, it will be especially useful in the new threaded version of SWMM 

Fix patch:
diff --git a/src/solver/include/toolkit.h b/src/solver/include/toolkit.h
index 339dbd09a..79ade19dc 100644
--- a/src/solver/include/toolkit.h
+++ b/src/solver/include/toolkit.h
@@ -99,6 +99,13 @@ EXPORT_TOOLKIT int swmm_getSimulationAnalysisSetting(SM_SimOption type, int *val
  */
 EXPORT_TOOLKIT int swmm_getSimulationParam(SM_SimSetting type, double *value);
 
+/**
+ @brief Set Simulation Analysis Setting
+ @param type Option code (see @ref SM_SimSetting)
+ @param value Option value
+ @return Error code
+ */
+EXPORT_TOOLKIT int  swmm_setSimulationParam(SM_SimSetting type, double value);
 /**
  @brief Gets Object Count
  @param type Option code (see @ref SM_ObjectType)
diff --git a/src/solver/include/toolkit_enums.h b/src/solver/include/toolkit_enums.h
index 651743b37..5d7c9e1dc 100644
--- a/src/solver/include/toolkit_enums.h
+++ b/src/solver/include/toolkit_enums.h
@@ -89,7 +89,8 @@ typedef enum {
     SM_QUALERROR     = 10, /**< Quality routing error */
     SM_HEADTOL       = 11, /**< DW routing head tolerance (ft) */
     SM_SYSFLOWTOL    = 12, /**< Tolerance for steady system flow */
-    SM_LATFLOWTOL    = 13  /**< Tolerance for steady nodal inflow */
+    SM_LATFLOWTOL    = 13, /**< Tolerance for steady nodal inflow */
+    SM_THREADS       = 14  /**< Number of Threads for this process */
 } SM_SimSetting;
 
 /// Node property codes
diff --git a/src/solver/toolkit.c b/src/solver/toolkit.c
index 83571a97e..d61006f15 100644
--- a/src/solver/toolkit.c
+++ b/src/solver/toolkit.c
@@ -24,6 +24,16 @@
 #include "swmm5.h"
 #include "toolkit.h"
 
+// Protect against lack of compiler support for OpenMP
+#if defined(_OPENMP)
+#include <omp.h>
+int alt_omp_get_max_threads(void)
+{
+    return omp_get_max_threads();
+}
+#else
+  int alt_omp_get_max_threads(void) { return 1;}
+#endif
 
 // Function Declarations for API
 int  massbal_getRoutingTotal(SM_RoutingTotals **routingTot);
@@ -405,6 +415,8 @@ EXPORT_TOOLKIT int  swmm_getSimulationParam(SM_SimSetting type, double *value)
             case SM_SYSFLOWTOL: *value = SysFlowTol; break;
             // Tolerance for steady nodal inflow
             case SM_LATFLOWTOL: *value = LatFlowTol; break;
+            // Number of Threads (if OpenMP enabled)
+            case SM_THREADS: *value = NumThreads; break;
             // Type not available
             default: error_code = ERR_TKAPI_OUTBOUNDS; break;
         }
@@ -412,6 +424,43 @@ EXPORT_TOOLKIT int  swmm_getSimulationParam(SM_SimSetting type, double *value)
     return error_code;
 }
 
+EXPORT_TOOLKIT int  swmm_setSimulationParam(SM_SimSetting type, double value)
+///
+/// Input:   type = SM_SimSetting
+///          Simulation Parameter
+/// Returns: error code
+/// Purpose: Set simulation analysis parameter (limited support)
+{
+    int error_code = 0;
+
+    // Check if Open
+    if(swmm_IsOpenFlag() == FALSE)
+    {
+        error_code = ERR_TKAPI_INPUTNOTOPEN;
+    }
+    // Check if Simulation is Running
+    else if(swmm_IsStartedFlag() == TRUE)
+    {
+        error_code = ERR_TKAPI_SIM_NRUNNING;
+    }
+    else
+    {
+        switch(type)
+        {
+            case SM_THREADS:
+            {
+                // --- adjust number of parallel threads to be used
+                if ( (int)value <= 0 ) NumThreads = 1;
+                else NumThreads = MIN((int)value, alt_omp_get_max_threads());
+                if ( Nobjects[LINK] < 4 * NumThreads ) NumThreads = 1;
+                break;
+            }
+            default: error_code = ERR_TKAPI_OUTBOUNDS; break;
+        }
+    }
+    return error_code;
+}
+
 EXPORT_TOOLKIT int  swmm_countObjects(SM_ObjectType type, int *count)
 ///
 /// Input:   type = object type (Based on SM_ObjectType enum)


Test patch:
diff --git a/tests/solver/test_toolkit.cpp b/tests/solver/test_toolkit.cpp
index 5138c666d..5033ca54d 100644
--- a/tests/solver/test_toolkit.cpp
+++ b/tests/solver/test_toolkit.cpp
@@ -419,6 +419,17 @@ BOOST_FIXTURE_TEST_CASE(project_info_metric, FixtureMetric) {
     error = swmm_getSimulationParam(SM_HEADTOL, &value);
     BOOST_REQUIRE(error == ERR_NONE);
     BOOST_CHECK_EQUAL(value, 0.015);
+
+    error = swmm_getSimulationParam(SM_THREADS, &value);
+    BOOST_REQUIRE(error == ERR_NONE);
+    BOOST_CHECK_EQUAL(value, 1);
+
+    #if defined(_OPENMP)
+      error = swmm_setSimulationParam(SM_THREADS, 2);
+      error = swmm_getSimulationParam(SM_THREADS, &value);
+      BOOST_REQUIRE(error == ERR_NONE);
+      BOOST_CHECK_EQUAL(value, 2);
+    #endif
 }