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

Issue:
Merged in USEPA-SWMM5.1.15
Pair Programming with @karosc 


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index fec94365b..54fd8cba7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-
 # Eclipse Stuff
 .metadata/
 .settings/
@@ -7,6 +6,17 @@
 .pydevproject
 
 
+.DS_Store
+
+build*/
+nrtest*/
+
+*_export.h
+
+src/outfile/include/*_export.h
+src/solver/include/*_export.h
+
+
 # Mac Stuff
 .DS_Store
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 087455567..a2409aa80 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,7 +22,7 @@ endif()
 
 
 project(swmm-solver
-    VERSION 5.1.14
+    VERSION 5.1.15
     LANGUAGES C CXX
 )
 
diff --git a/extern/boost.cmake b/extern/boost.cmake
index 839df22ec..4f9eb1792 100644
--- a/extern/boost.cmake
+++ b/extern/boost.cmake
@@ -7,10 +7,10 @@
 # Author: Michael E. Tryby
 #         US EPA - ORD/CESER
 #
-# Usage: 
-#  Create environment variable with the following pattern -- "BOOST_ROOT_X_XX_X"  
+# Usage:
+#  Create environment variable with the following pattern -- "BOOST_ROOT_X_XX_X"
 #  where Xs are the Boost version -- pointing to install location.
-# 
+#
 
 
 if(WIN32)
diff --git a/src/outfile/CMakeLists.txt b/src/outfile/CMakeLists.txt
index 11b7e5ce8..6732b9027 100644
--- a/src/outfile/CMakeLists.txt
+++ b/src/outfile/CMakeLists.txt
@@ -6,7 +6,7 @@
 #
 # Author: Michael E. Tryby
 #         US EPA ORD/CESER
-#         
+#
 #
 
 # configure file groups
@@ -18,7 +18,7 @@ set(SWMM_OUT_PUBLIC_HEADERS
 
 
 # the binary output file API
-add_library(swmm-output    
+add_library(swmm-output
         swmm_output.c
         errormanager.c
 )
diff --git a/src/outfile/swmm_output.c b/src/outfile/swmm_output.c
index 57fa847d6..11a0c57b4 100644
--- a/src/outfile/swmm_output.c
+++ b/src/outfile/swmm_output.c
@@ -10,6 +10,8 @@
  */
 
 
+#define _CRT_SECURE_NO_DEPRECATE
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/src/run/main.c b/src/run/main.c
index dee6418d2..ac1d2a018 100644
--- a/src/run/main.c
+++ b/src/run/main.c
@@ -8,6 +8,7 @@
   */
 
 #include <stdio.h>
+#include <string.h>
 #include <time.h>
 #include <string.h>
 #include <math.h>
@@ -124,7 +125,7 @@ int  main(int argc, char *argv[])
 
         else if (strcmp(arg1, "--version") == 0 || strcmp(arg1, "-v") == 0) {
             printf("\nVersion:\n");
-            printf("\tOWA-SWMM v%s (Build %.10s)\n\n", swmm_getSemVersion(), 
+            printf("\tOWA-SWMM v%s (Build %.10s)\n\n", swmm_getSemVersion(),
                 swmm_getBuildId());
         }
 
@@ -139,4 +140,4 @@ int  main(int argc, char *argv[])
     }
 
     return 0;
-}
\ No newline at end of file
+}
diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt
index e10fa4a9a..0741ffe3d 100644
--- a/src/solver/CMakeLists.txt
+++ b/src/solver/CMakeLists.txt
@@ -38,7 +38,7 @@ if(BUILD_DEF)
         PROPERTIES_HEADER_FILE_ONLY TRUE
     )
 
-    add_library(swmm5        
+    add_library(swmm5
             ${SWMM_SOURCES}
             ${PROJECT_SOURCE_DIR/bindings/swmm5.def}
             $<TARGET_OBJECTS:shared_objs>
@@ -46,7 +46,7 @@ if(BUILD_DEF)
 
 else()
     # Performs standard library build
-    add_library(swmm5        
+    add_library(swmm5
             ${SWMM_SOURCES}
             $<TARGET_OBJECTS:shared_objs>
     )
@@ -90,7 +90,7 @@ target_include_directories(swmm5
 
 include(GenerateExportHeader)
 generate_export_header(swmm5
-    BASE_NAME toolkit  
+    BASE_NAME toolkit
     EXPORT_MACRO_NAME EXPORT_TOOLKIT
     EXPORT_FILE_NAME toolkit_export.h
     STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC
@@ -118,6 +118,7 @@ install(TARGETS swmm5 EXPORT swmm5Targets
     FRAMEWORK  DESTINATION "${TOOL_DIST}"
 )
 
+# Create target import scripts so other cmake projects can use swmm libraries
 install(
     EXPORT
         swmm5Targets
@@ -125,14 +126,9 @@ install(
         "${CONFIG_DIST}"
     FILE
         swmm5-config.cmake
-)
+    )
 
-install(
-    FILES
-        ${SWMM_PUBLIC_HEADERS}
-    DESTINATION
-        "${INCLUDE_DIST}"
-)
+install(FILES ${SWMM_PUBLIC_HEADERS} DESTINATION "${INCLUDE_DIST}")
 
 
 # copy swmm5 to build tree for testing
diff --git a/src/solver/consts.h b/src/solver/consts.h
index 62bf52747..b1697c368 100644
--- a/src/solver/consts.h
+++ b/src/solver/consts.h
@@ -7,11 +7,16 @@
 //            08/01/16  (Build 5.1.011)
 //            05/10/18  (Build 5.1.013)
 //            03/01/20  (Build 5.1.014)
+//            04/01/20  (Build 5.1.015)
 //   Author:  L. Rossman
 //
 //   Various Constants
 //-----------------------------------------------------------------------------
 
+#ifndef CONSTS_H
+#define CONSTS_H
+
+
 //------------------
 // General Constants
 //------------------
@@ -93,3 +98,6 @@
 // Token separator characters
 //---------------------------
 #define   SEPSTR    " \t\n\r"
+
+
+#endif //CONSTS_H
diff --git a/src/solver/datetime.h b/src/solver/datetime.h
index 397f3180c..8c84b3b79 100644
--- a/src/solver/datetime.h
+++ b/src/solver/datetime.h
@@ -18,6 +18,10 @@
 //   - New getTimeStamp function added.
 //-----------------------------------------------------------------------------
 
+#ifndef DATETIME_H
+#define DATETIME_H
+
+
 typedef double DateTime;
 
 #define Y_M_D 0
@@ -61,3 +65,6 @@ void datetime_setDateFormat(int fmt);
 DateTime datetime_addSeconds(DateTime date1, double seconds);
 DateTime datetime_addDays(DateTime date1, DateTime date2);
 long     datetime_timeDiff(DateTime date1, DateTime date2);
+
+
+#endif //DATETIME_H
diff --git a/src/solver/dynwave.c b/src/solver/dynwave.c
index 294adc873..8c901057e 100644
--- a/src/solver/dynwave.c
+++ b/src/solver/dynwave.c
@@ -9,6 +9,8 @@
 //             03/19/15   (5.1.008)
 //             08/01/16   (5.1.011)
 //             05/10/18   (5.1.013)
+//             03/01/20   (5.1.014)
+//             07/10/20   (5.1.015)
 //   Author:   L. Rossman (EPA)
 //             M. Tryby (EPA)
 //             R. Dickinson (CDM)
@@ -49,6 +51,9 @@
 //   - updateNodeFlows() modified to subtract conduit evap. and seepage losses
 //     from downstream node inflow instead of upstream node outflow.
 //
+//   Build 5.1.015:
+//   - Roll back the 5.1.014 change for conduit losses in updateNodeFlows().
+//
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -403,7 +408,7 @@ void findLinkFlows(double dt)
     for ( i = 0; i < Nobjects[LINK]; i++)
     {
         if ( !isTrueConduit(i) )
-        {	
+        {
             if ( !Link[i].bypassed ) findNonConduitFlow(i, dt);
             updateNodeFlows(i);
         }
@@ -551,13 +556,13 @@ void updateNodeFlows(int i)
     // --- update total inflow & outflow at upstream/downstream nodes
     if ( q >= 0.0 )
     {
-        Node[n1].outflow += q;                                                 //(5.1.014)
-        Node[n2].inflow  += q - uniformLossRate;                               //(5.1.014)
+        Node[n1].outflow += q + uniformLossRate;                               //(5.1.015)
+        Node[n2].inflow  += q;                                                 //(5.1.015)
     }
     else
     {
-        Node[n1].inflow   -= q + uniformLossRate;                              //(5.1.014)
-        Node[n2].outflow  -= q;                                                //(5.1.014)
+        Node[n1].inflow   -= q;                                                //(5.1.015)
+        Node[n2].outflow  -= q - uniformLossRate;                              //(5.1.015)
     }
 
     // --- add surf. area contributions to upstream/downstream nodes
diff --git a/src/solver/enums.h b/src/solver/enums.h
index 7d2ab42e4..1ee7b0bea 100644
--- a/src/solver/enums.h
+++ b/src/solver/enums.h
@@ -35,10 +35,14 @@
 //
 //   Build 5.1.013:
 //   - SURCHARGE_METHOD and RULE_STEP options added.
-//   - WEIR_CURVE added as a curve type. 
+//   - WEIR_CURVE added as a curve type.
 //
 //-----------------------------------------------------------------------------
 
+#ifndef ENUMS_H
+#define ENUMS_H
+
+
 //-------------------------------------
 // Names of major object types
 //-------------------------------------
@@ -114,18 +118,18 @@
 // Cross section shape types
 //-------------------------------------
  enum XsectType {
-      DUMMY,                           // 0      
+      DUMMY,                           // 0
       CIRCULAR,                        // 1      closed
       FILLED_CIRCULAR,                 // 2      closed
       RECT_CLOSED,                     // 3      closed
-      RECT_OPEN,                       // 4       
-      TRAPEZOIDAL,                     // 5       
-      TRIANGULAR,                      // 6       
+      RECT_OPEN,                       // 4
+      TRAPEZOIDAL,                     // 5
+      TRIANGULAR,                      // 6
       PARABOLIC,                       // 7
-      POWERFUNC,                       // 8      
-      RECT_TRIANG,                     // 9       
+      POWERFUNC,                       // 8
+      RECT_TRIANG,                     // 9
       RECT_ROUND,                      // 10
-      MOD_BASKET,                      // 11      
+      MOD_BASKET,                      // 11
       HORIZ_ELLIPSE,                   // 12     closed
       VERT_ELLIPSE,                    // 13     closed
       ARCH,                            // 14     closed
@@ -194,7 +198,7 @@
 //-------------------------------------
 // Computed node quantities
 //-------------------------------------
- #define MAX_NODE_RESULTS 7 
+ #define MAX_NODE_RESULTS 7
  enum NodeResultType {
       NODE_DEPTH,                      // water depth above invert
       NODE_HEAD,                       // hydraulic head
@@ -411,7 +415,7 @@ enum  CompatibilityType {
 
  enum PumpCurveType {
       TYPE1_PUMP,                      // flow varies stepwise with wet well volume
-      TYPE2_PUMP,                      // flow varies stepwise with inlet depth 
+      TYPE2_PUMP,                      // flow varies stepwise with inlet depth
       TYPE3_PUMP,                      // flow varies with head delivered
       TYPE4_PUMP,                      // flow varies with inlet depth
       IDEAL_PUMP};                     // outflow equals inflow
@@ -480,3 +484,6 @@ enum  NoneAllType {
       NONE,
       ALL,
       SOME};
+
+
+#endif //ENUMS_H
diff --git a/src/solver/error.c b/src/solver/error.c
index dfd0998ae..f0d40f62b 100644
--- a/src/solver/error.c
+++ b/src/solver/error.c
@@ -6,6 +6,7 @@
 //   Date:    03/20/14  (Build 5.1.001)
 //            03/19/15  (Build 5.1.008)
 //            08/05/15  (Build 5.1.010)
+//            04/14/20  (Build 5.1.015)
 //   Author:  L. Rossman
 //
 //   Error messages
@@ -16,6 +17,8 @@
 //   Build 5.1.010:
 //   - Text of Error 318 for rainfall data files modified.
 //
+//   Build 5.1.015:
+//   - Added new Error 140 for storage nodes.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -55,6 +58,8 @@
 #define ERR138 \
 "\n  ERROR 138: Node %s has initial depth greater than maximum depth."
 #define ERR139 "\n  ERROR 139: Regulator %s is the outlet of a non-storage node."
+#define ERR140 \
+"\n  ERROR 140: Storage node %s has negative volume at full depth."            //(5.1.015)
 #define ERR141 \
 "\n  ERROR 141: Outfall %s has more than 1 inlet link or an outlet link."
 #define ERR143 "\n  ERROR 143: Regulator %s has invalid cross-section shape."
@@ -200,7 +205,7 @@ char* ErrorMsgs[] =
       ERR327, ERR329, ERR330, ERR331, ERR333, ERR335, ERR336, ERR337, ERR338,
       ERR339, ERR341, ERR343, ERR345, ERR351, ERR353, ERR355, ERR357, ERR361,
       ERR363, ERR401, ERR402, ERR403, ERR405, ERR501, ERR502, ERR503, ERR504,
-      ERR505, ERR506, ERR507, ERR508, ERR509, ERR510, ERR511, ERR512};
+      ERR505, ERR506, ERR507, ERR508, ERR509, ERR510, ERR511, ERR512, ERR140};
 
 int ErrorCodes[] =
     { 0,      101,    103,    105,    107,    108,    109,    110,    111,
@@ -215,7 +220,7 @@ int ErrorCodes[] =
       327,    329,    330,    331,    333,    335,    336,    337,    338,
       339,    341,    343,    345,    351,    353,    355,    357,    361,
       363,    401,    402,    403,    405,    501,    502,    503,    504,
-      505,    506,    507,    508,    509,    510,    511,    512};
+      505,    506,    507,    508,    509,    510,    511,    512,    140};
 
 char  ErrString[256];
 
diff --git a/src/solver/error.h b/src/solver/error.h
index 80e7c0d83..3697cc129 100644
--- a/src/solver/error.h
+++ b/src/solver/error.h
@@ -4,12 +4,17 @@
 //   Project: EPA SWMM5
 //   Version: 5.1
 //   Date:    03/20/14  (Build 5.1.001)
+//            04/14/20  (Build 5.1.015)
 //   Author:  L. Rossman
 //
 //   Error codes
 //
 //-----------------------------------------------------------------------------
 
+#ifndef ERROR_H
+#define ERROR_H
+
+
 enum  ErrorType {
 
   //... Runtime Errors
@@ -173,9 +178,14 @@ enum  ErrorType {
       ERR_API_LIDUNIT_INDEX,    //510  113
       ERR_API_UNDEFINED_LID,    //511  114
       ERR_API_MEMORY,           //512  115
+  //... Additional Errors
+      ERR_STORAGE_VOLUME,       //140  116                                     //(5.1.015)
       MAXERRMSG};
 
 char* error_getMsg(int i);
 int   error_getCode(int i);
 int   error_getErrorIndex(int ErrorCode);
 int   error_setInpError(int errcode, char* s);
+
+
+#endif //ERROR_H
diff --git a/src/solver/exfil.h b/src/solver/exfil.h
index 6958735cc..8315c08c4 100644
--- a/src/solver/exfil.h
+++ b/src/solver/exfil.h
@@ -12,6 +12,7 @@
 #ifndef EXFIL_H
 #define EXFIL_H
 
+
 //----------------------------
 // EXFILTRATION OBJECT
 //----------------------------
@@ -32,4 +33,5 @@ int    exfil_readStorageParams(int k, char* tok[], int ntoks, int n);
 void   exfil_initState(int k);
 double exfil_getLoss(TExfil* exfil, double tStep, double depth, double area);
 
-#endif
+
+#endif //EXFIL_H
diff --git a/src/solver/findroot.h b/src/solver/findroot.h
index 253a4ba4e..c0ae10dff 100644
--- a/src/solver/findroot.h
+++ b/src/solver/findroot.h
@@ -5,8 +5,16 @@
 //
 //   Last modified on 11/19/13.
 //-----------------------------------------------------------------------------
+
+#ifndef FINDROOT_H
+#define FINDROOT_H
+
+
 int findroot_Newton(double x1, double x2, double* rts, double xacc,
                     void (*func) (double x, double* f, double* df, void* p),
 					void* p);
 double findroot_Ridder(double x1, double x2, double xacc,
 	                   double (*func)(double, void* p), void* p);
+
+
+#endif //FINDROOT_H
diff --git a/src/solver/flowrout.c b/src/solver/flowrout.c
index b50b12787..675e39300 100644
--- a/src/solver/flowrout.c
+++ b/src/solver/flowrout.c
@@ -640,9 +640,9 @@ void setNewNodeState(int j, double dt)
 
     // --- update terminal storage nodes
     if ( Node[j].type == STORAGE )
-    {	
-	if ( Node[j].updated == FALSE )
-	    updateStorageState(j, Nobjects[LINK], NULL, dt);
+    {
+    if ( Node[j].updated == FALSE )
+        updateStorageState(j, Nobjects[LINK], NULL, dt);
         return; 
     }
 
diff --git a/src/solver/funcs.h b/src/solver/funcs.h
index 4199e54c3..09a2db4fc 100644
--- a/src/solver/funcs.h
+++ b/src/solver/funcs.h
@@ -33,6 +33,10 @@
 //
 //-----------------------------------------------------------------------------
 
+#ifndef FUNCS_H
+#define FUNCS_H
+
+
 void     project_open(char *f1, char *f2, char *f3);
 void     project_close(void);
 
@@ -360,10 +364,10 @@ int     inflow_readExtInflow(char* tok[], int ntoks);
 int     inflow_readDwfInflow(char* tok[], int ntoks);
 int     inflow_readDwfPattern(char* tok[], int ntoks);
 int     inflow_setExtInflow(int j, int param, int type,
-						int tSeries, int basePat, double cf,
-						double baseline, double sf);
+        int tSeries, int basePat, double cf,
+        double baseline, double sf);
 int     inflow_validate(int param, int type, int tSeries,
-						int basePat, double *cf);
+        int basePat, double *cf);
 
 void    inflow_initDwfInflow(TDwfInflow* inflow);
 void    inflow_initDwfPattern(int pattern);
@@ -521,3 +525,6 @@ void     writecon(char *s);                   // writes string to console
 DateTime getDateTime(double elapsedMsec);     // convert elapsed time to date
 void     getElapsedTime(DateTime aDate,       // convert elapsed date
          int* days, int* hrs, int* mins);
+
+
+#endif //FUNCS_H
diff --git a/src/solver/globals.h b/src/solver/globals.h
index b3d2ca403..93c11f2d7 100644
--- a/src/solver/globals.h
+++ b/src/solver/globals.h
@@ -10,6 +10,7 @@
 //            08/01/16  (Build 5.1.011)
 //            03/14/17  (Build 5.1.012)
 //            05/10/18  (Build 5.1.013)
+//            04/01/20  (Build 5.1.015)
 //   Author:  L. Rossman
 //
 //   Global Variables
@@ -26,7 +27,7 @@
 //
 //   Build 5.1.011:
 //   - Changed WarningCode to Warnings (# warnings issued)
-//   - Added error message text as a variable. 
+//   - Added error message text as a variable.
 //   - Added elapsed simulation time (in decimal days) variable.
 //   - Added variables associated with detailed routing events.
 //
@@ -35,8 +36,15 @@
 //
 //   Build 5.1.013:
 //   - CrownCutoff and RuleStep added as analysis option variables.
+//
+//   Build 5.1.015:
+//   - Fixes bug in summary statistics when Report Start date > Start Date.
 //-----------------------------------------------------------------------------
 
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+
 EXTERN TFile
                   Finp,                     // Input file
                   Fout,                     // Output file
@@ -52,7 +60,8 @@ EXTERN TFile
 
 EXTERN long
                   Nperiods,                 // Number of reporting periods
-                  StepCount,                // Number of routing steps used
+                  TotalStepCount,           // Total routing steps used        //(5.1.015)
+                  ReportStepCount,          // Reporting routing steps used    //(5.1.015)
                   NonConvergeCount;         // Number of non-converging steps
 
 EXTERN char
@@ -167,3 +176,6 @@ EXTERN TTable*    Tseries;                  // Array of time series tables
 EXTERN TTransect* Transect;                 // Array of transect data
 EXTERN TShape*    Shape;                    // Array of custom conduit shapes
 EXTERN TEvent*    Event;                    // Array of routing events
+
+
+#endif //GLOBALS_H
diff --git a/src/solver/hash.h b/src/solver/hash.h
index 26f1684fa..90ac1ca22 100644
--- a/src/solver/hash.h
+++ b/src/solver/hash.h
@@ -4,6 +4,10 @@
 //   Header file for Hash Table module hash.c.
 //-----------------------------------------------------------------------------
 
+#ifndef HASH_H
+#define HASH_H
+
+
 #define HTMAXSIZE 1999
 #define NOTFOUND  -1
 
@@ -21,3 +25,6 @@ int     HTinsert(HTtable *, char *, int);
 int     HTfind(HTtable *, char *);
 char    *HTfindKey(HTtable *, char *);
 void    HTfree(HTtable *);
+
+
+#endif //HASH_H
diff --git a/src/solver/headers.h b/src/solver/headers.h
index 88cd95e4b..016f5445c 100644
--- a/src/solver/headers.h
+++ b/src/solver/headers.h
@@ -10,6 +10,7 @@
 //
 //   DO NOT CHANGE THE ORDER OF THE #INCLUDE STATEMENTS
 //-----------------------------------------------------------------------------
+
 #include <stdio.h>
 #include "consts.h"
 #include "macros.h"
diff --git a/src/solver/hotstart.c b/src/solver/hotstart.c
index be81670bd..33c005e45 100644
--- a/src/solver/hotstart.c
+++ b/src/solver/hotstart.c
@@ -8,6 +8,7 @@
 //             04/23/14  (Build 5.1.005)
 //             03/19/15  (Build 5.1.008)
 //             08/01/16  (Build 5.1.011)
+//             04/01/20  (Build 5.1.015)
 //   Author:   L. Rossman (EPA)
 //
 //   Hot Start file functions.
@@ -37,6 +38,8 @@
 //   Build 5.1.011:
 //   - Link control setting bug when reading a hot start file fixed.    
 //
+//   Build 5.1.015:
+//   - Support added for multiple infiltration methods within a project.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -378,7 +381,7 @@ void  saveRunoff(void)
 
         // Infiltration state (max. of 6 elements)
         for (j=0; j<sizeX; j++) x[j] = 0.0;
-        infil_getState(i, InfilModel, x);
+        infil_getState(i, x);                                                  //(5.1.015)
         fwrite(x, sizeof(double), 6, f);
 
         // Groundwater state (4 elements)
@@ -447,7 +450,7 @@ void  readRunoff()
 
         // Infiltration state (max. of 6 elements)
         for (j=0; j<6; j++) if ( !readDouble(&x[j], f) ) return;
-        infil_setState(i, InfilModel, x);
+        infil_setState(i, x);                                                  //(5.1.015)
 
         // Groundwater state (4 elements)
         if ( Subcatch[i].groundwater != NULL )
diff --git a/src/solver/include/swmm5.h b/src/solver/include/swmm5.h
index 7753a0553..15bdd08be 100644
--- a/src/solver/include/swmm5.h
+++ b/src/solver/include/swmm5.h
@@ -14,6 +14,7 @@
 #ifndef SWMM5_H
 #define SWMM5_H
 
+
 // --- define WINDOWS
 
 #undef WINDOWS
@@ -54,8 +55,8 @@ int swmm_IsStartedFlag(void);
 // --- use "C" linkage for C++ programs
 
 #ifdef __cplusplus
-extern "C" { 
-#endif 
+extern "C" {
+#endif
 
 /**
  @brief Opens SWMM input file, reads in network data, runs, and closes
@@ -127,8 +128,9 @@ int DLLEXPORT swmm_getError(char* errMsg, int msgLen);
 
 int DLLEXPORT swmm_getWarnings(void);
 
-#ifdef __cplusplus 
-}   // matches the linkage specification from above */ 
+#ifdef __cplusplus
+}   // matches the linkage specification from above */
 #endif
 
-#endif
+
+#endif //SWMM5_H
diff --git a/src/solver/infil.c b/src/solver/infil.c
index 19bc2a253..68808a33d 100644
--- a/src/solver/infil.c
+++ b/src/solver/infil.c
@@ -9,6 +9,7 @@
 //             08/05/15  (Build 5.1.010)
 //             08/01/16  (Build 5.1.011)
 //             05/10/17  (Build 5.1.013)
+//             04/01/20  (Build 5.1.015)
 //   Author:   L. Rossman
 //
 //   Infiltration functions.
@@ -33,6 +34,9 @@
 //   Build 5.1.013:
 //   - Support added for subcatchment-specific time patterns that adjust
 //     hydraulic conductivity.
+//
+//   Build 5.1.015:
+//   - Support added for multiple infiltration methods within a project.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -44,9 +48,12 @@
 //-----------------------------------------------------------------------------
 //  Local Variables
 //-----------------------------------------------------------------------------
-THorton*   HortInfil = NULL;
-TGrnAmpt*  GAInfil   = NULL;
-TCurveNum* CNInfil   = NULL;
+typedef union TInfil {
+    THorton   horton;
+    TGrnAmpt  grnAmpt;
+    TCurveNum curveNum;
+} TInfil;
+TInfil *Infil;
 
 static double Fumax;   // saturated water volume in upper soil zone (ft)
 static double InfilFactor;                                                     //(5.1.013)
@@ -96,33 +103,17 @@ static double curvenum_getInfil(TCurveNum *infil, double tstep, double irate,
 
 //=============================================================================
 
-void infil_create(int subcatchCount, int model)
+void infil_create(int n)
 //
 //  Purpose: creates an array of infiltration objects.
 //  Input:   n = number of subcatchments
-//           m = infiltration method code
 //  Output:  none
 //
 {
-    switch (model)
-    {
-    case HORTON:
-    case MOD_HORTON:
-        HortInfil = (THorton *) calloc(subcatchCount, sizeof(THorton));
-        if ( HortInfil == NULL ) ErrorCode = ERR_MEMORY;
-        break;
-    case GREEN_AMPT:
-    case MOD_GREEN_AMPT:
-        GAInfil = (TGrnAmpt *) calloc(subcatchCount, sizeof(TGrnAmpt));
-        if ( GAInfil == NULL ) ErrorCode = ERR_MEMORY;
-        break;
-    case CURVE_NUMBER:
-        CNInfil = (TCurveNum *) calloc(subcatchCount, sizeof(TCurveNum));
-        if ( CNInfil == NULL ) ErrorCode = ERR_MEMORY;
-        break;
-    default: ErrorCode = ERR_MEMORY;
-    }
-    InfilFactor = 1.0;                                                         //(5.1.013)
+    Infil = (TInfil *) calloc(n, sizeof(TInfil));
+    if (Infil == NULL) ErrorCode = ERR_MEMORY;
+    InfilFactor = 1.0;
+    return;
 }
 
 //=============================================================================
@@ -134,23 +125,21 @@ void infil_delete()
 //  Output:  none
 //
 {
-    FREE(HortInfil);
-    FREE(GAInfil);
-    FREE(CNInfil);
+    FREE(Infil);
 }
 
 //=============================================================================
 
 int infil_readParams(int m, char* tok[], int ntoks)
 //
-//  Input:   m = infiltration method code
+//  Input:   m = default infiltration model
 //           tok[] = array of string tokens
 //           ntoks = number of tokens
 //  Output:  returns an error code
 //  Purpose: sets infiltration parameters from a line of input data.
 //
 //  Format of data line is:
-//     subcatch  p1  p2 ...
+//     subcatch  p1  p2 ... (infilMethod)
 {
     int   i, j, n, status;
     double x[5];
@@ -159,20 +148,29 @@ int infil_readParams(int m, char* tok[], int ntoks)
     j = project_findObject(SUBCATCH, tok[0]);
     if ( j < 0 ) return error_setInpError(ERR_NAME, tok[0]);
 
+    // --- check for infiltration method keyword is last token 
+    i = findmatch(tok[ntoks-1], InfilModelWords);
+    if ( i >= 0 )
+    {
+        m = i; 
+        --ntoks;
+    }
+
     // --- number of input tokens depends on infiltration model m
-    if      ( m == HORTON )         n = 5;
+    if      ( m == HORTON )         n = 5; 
     else if ( m == MOD_HORTON )     n = 5;
     else if ( m == GREEN_AMPT )     n = 4;
     else if ( m == MOD_GREEN_AMPT ) n = 4;
     else if ( m == CURVE_NUMBER )   n = 4;
-    else return 0;
-    if ( ntoks < n ) return error_setInpError(ERR_ITEMS, "");
+    else return 0; 
 
+    if ( ntoks < n ) return error_setInpError(ERR_ITEMS, "");
+   
     // --- parse numerical values from tokens
     for (i = 0; i < 5; i++) x[i] = 0.0;
     for (i = 1; i < n; i++)
     {
-        if ( ! getDouble(tok[i], &x[i-1]) )
+        if (!getDouble(tok[i], &x[i - 1]))
             return error_setInpError(ERR_NUMBER, tok[i]);
     }
 
@@ -183,18 +181,19 @@ int infil_readParams(int m, char* tok[], int ntoks)
             return error_setInpError(ERR_NUMBER, tok[n]);
     }
 
-    // --- assign parameter values to infil. object
+    // --- assign parameter values to infil, infilModel object
     Subcatch[j].infil = j;
+    Subcatch[j].infilModel = m;
     switch (m)
     {
       case HORTON:
-      case MOD_HORTON:   status = horton_setParams(&HortInfil[j], x);
+      case MOD_HORTON:   status = horton_setParams(&Infil[j].horton, x);
                          break;
       case GREEN_AMPT:
       case MOD_GREEN_AMPT:
-                         status = grnampt_setParams(&GAInfil[j], x);
+                         status = grnampt_setParams(&Infil[j].grnAmpt, x);
                          break;
-      case CURVE_NUMBER: status = curvenum_setParams(&CNInfil[j], x);
+      case CURVE_NUMBER: status = curvenum_setParams(&Infil[j].curveNum, x);
                          break;
       default:           status = TRUE;
     }
@@ -204,49 +203,47 @@ int infil_readParams(int m, char* tok[], int ntoks)
 
 //=============================================================================
 
-void infil_initState(int j, int m)
+void infil_initState(int j)
 //
 //  Input:   j = subcatchment index
-//           m = infiltration method code
 //  Output:  none
 //  Purpose: initializes state of infiltration for a subcatchment.
 //
 {
-    switch (m)
+    switch (Subcatch[j].infilModel)
     {
       case HORTON:
-      case MOD_HORTON:   horton_initState(&HortInfil[j]);   break;
+      case MOD_HORTON:   horton_initState(&Infil[j].horton);   break;
       case GREEN_AMPT:
       case MOD_GREEN_AMPT:
-                         grnampt_initState(&GAInfil[j]);    break;
-      case CURVE_NUMBER: curvenum_initState(&CNInfil[j]);   break;
+                         grnampt_initState(&Infil[j].grnAmpt);    break;
+      case CURVE_NUMBER: curvenum_initState(&Infil[j].curveNum);   break;
     }
 }
 
 //=============================================================================
 
-void infil_getState(int j, int m, double x[])
+void infil_getState(int j, double x[])
 //
 //  Input:   j = subcatchment index
-//           m = infiltration method code
-//  Output:  none
+//  Output:  x = subcatchment's infiltration state
 //  Purpose: retrieves the current infiltration state for a subcatchment.
 //
 {
-    switch (m)
+    switch (Subcatch[j].infilModel)
     {
       case HORTON:
-      case MOD_HORTON:   horton_getState(&HortInfil[j], x); break;
+      case MOD_HORTON:   horton_getState(&Infil[j].horton, x); break;
       case GREEN_AMPT:
       case MOD_GREEN_AMPT:
-                         grnampt_getState(&GAInfil[j],x);   break;
-      case CURVE_NUMBER: curvenum_getState(&CNInfil[j], x); break;
+                         grnampt_getState(&Infil[j].grnAmpt, x);   break;
+      case CURVE_NUMBER: curvenum_getState(&Infil[j].curveNum, x); break;
     }
 }
 
 //=============================================================================
 
-void infil_setState(int j, int m, double x[])
+void infil_setState(int j, double x[])
 //
 //  Input:   j = subcatchment index
 //           m = infiltration method code
@@ -254,14 +251,14 @@ void infil_setState(int j, int m, double x[])
 //  Purpose: sets the current infiltration state for a subcatchment.
 //
 {
-    switch (m)
+    switch (Subcatch[j].infilModel)
     {
       case HORTON:
-      case MOD_HORTON:   horton_setState(&HortInfil[j], x); break;
+      case MOD_HORTON:   horton_setState(&Infil[j].horton, x); break;
       case GREEN_AMPT:
       case MOD_GREEN_AMPT:
-                         grnampt_setState(&GAInfil[j],x);   break;
-      case CURVE_NUMBER: curvenum_setState(&CNInfil[j], x); break;
+                         grnampt_setState(&Infil[j].grnAmpt, x);   break;
+      case CURVE_NUMBER: curvenum_setState(&Infil[j].curveNum, x); break;
     }
 }
 
@@ -295,11 +292,10 @@ void infil_setInfilFactor(int j)
 
 //=============================================================================
 
-double infil_getInfil(int j, int m, double tstep, double rainfall,
+double infil_getInfil(int j, double tstep, double rainfall,
                       double runon, double depth)
 //
 //  Input:   j = subcatchment index
-//           m = infiltration method code
 //           tstep = runoff time step (sec)
 //           rainfall = rainfall rate (ft/sec)
 //           runon = runon rate from other sub-areas or subcatchments (ft/sec)
@@ -308,22 +304,23 @@ double infil_getInfil(int j, int m, double tstep, double rainfall,
 //  Purpose: computes infiltration rate depending on infiltration method.
 //
 {
-    switch (m)
+    switch (Subcatch[j].infilModel)
     {
       case HORTON:
-          return horton_getInfil(&HortInfil[j], tstep, rainfall+runon, depth);
+          return horton_getInfil(&Infil[j].horton, tstep, rainfall+runon, depth);
 
       case MOD_HORTON:
-          return modHorton_getInfil(&HortInfil[j], tstep, rainfall+runon,
+          return modHorton_getInfil(&Infil[j].horton, tstep, rainfall+runon,
                                     depth);
 
       case GREEN_AMPT:
       case MOD_GREEN_AMPT:
-        return grnampt_getInfil(&GAInfil[j], tstep, rainfall+runon, depth, m);
+        return grnampt_getInfil(&Infil[j].grnAmpt, tstep, rainfall+runon, depth,
+            Subcatch[j].infilModel);
 
       case CURVE_NUMBER:
         depth += runon / tstep;
-        return curvenum_getInfil(&CNInfil[j], tstep, rainfall, depth);
+        return curvenum_getInfil(&Infil[j].curveNum, tstep, rainfall, depth);
 
       default:
         return 0.0;
@@ -341,7 +338,7 @@ int horton_setParams(THorton *infil, double p[])
 //
 {
     int k;
-    for (k=0; k<5; k++) if ( p[k] < 0.0 ) return FALSE;
+    for (k = 0; k < 5; k++) if ( p[k] < 0.0 ) return FALSE;
 
     // --- max. & min. infil rates (ft/sec)
     infil->f0   = p[0] / UCF(RAINFALL);
@@ -566,6 +563,21 @@ double modHorton_getInfil(THorton *infil, double tstep, double irate,
 
 //=============================================================================
 
+void grnampt_getParams(int j, double p[])
+//
+//  Input:   j = subcatchment index
+//           p[] = array of parameter values
+//  Output:  none
+//  Purpose: retrieves Green-Ampt infiltration parameters for a subcatchment.
+//
+{
+    p[0] = Infil[j].grnAmpt.S * UCF(RAINDEPTH);   // Capillary suction head (ft)
+    p[1] = Infil[j].grnAmpt.Ks * UCF(RAINFALL);   // Sat. hyd. conductivity (ft/sec)
+    p[2] = Infil[j].grnAmpt.IMDmax;               // Max. init. moisture deficit
+}
+
+//=============================================================================
+
 int grnampt_setParams(TGrnAmpt *infil, double p[])
 //
 //  Input:   infil = ptr. to Green-Ampt infiltration object
diff --git a/src/solver/infil.h b/src/solver/infil.h
index cf208e234..c4923e88d 100644
--- a/src/solver/infil.h
+++ b/src/solver/infil.h
@@ -7,6 +7,7 @@
 //            09/15/14   (Build 5.1.007)
 //            08/05/15   (Build 5.1.010)
 //            05/10/18   (Build 5.1.013)
+//            04/01/20   (Build 5.1.015)
 //   Author:  L. Rossman (US EPA)
 //
 //   Public interface for infiltration functions.
@@ -16,11 +17,15 @@
 //
 //   Build 5.1.013:
 //   - New function infil_setInfilFactor() added.
+//
+//   Build 5.1.015:
+//   - Support added for multiple infiltration methods within a project.
 //-----------------------------------------------------------------------------
 
 #ifndef INFIL_H
 #define INFIL_H
 
+
 //---------------------
 // Enumerated Constants
 //---------------------
@@ -93,19 +98,21 @@ extern TCurveNum* CNInfil;
 //-----------------------------------------------------------------------------
 //   Infiltration Methods
 //-----------------------------------------------------------------------------
-void    infil_create(int subcatchCount, int model);
+void    infil_create(int n);
 void    infil_delete(void);
-int     infil_readParams(int model, char* tok[], int ntoks);
-void    infil_initState(int area, int model);
-void    infil_getState(int j, int m, double x[]);
-void    infil_setState(int j, int m, double x[]);
-void    infil_setInfilFactor(int j);                                           //(5.1.013)
-double  infil_getInfil(int area, int model, double tstep, double rainfall,
-        double runon, double depth);
+int     infil_readParams(int m, char* tok[], int ntoks);
+void    infil_initState(int j);
+void    infil_getState(int j, double x[]);
+void    infil_setState(int j, double x[]);
+void    infil_setInfilFactor(int j);
+double  infil_getInfil(int area, double tstep, double rainfall, double runon,
+        double depth);
 
+void    grnampt_getParams(int j, double p[]);
 int     grnampt_setParams(TGrnAmpt *infil, double p[]);
 void    grnampt_initState(TGrnAmpt *infil);
 double  grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate,
         double depth, int modelType);
 
-#endif
+
+#endif //INFIL_H
diff --git a/src/solver/inflow.c b/src/solver/inflow.c
index e11e15086..42976c13e 100644
--- a/src/solver/inflow.c
+++ b/src/solver/inflow.c
@@ -71,11 +71,11 @@ int inflow_readExtInflow(char* tok[], int ntoks)
         Tseries[tseries].refersTo = EXTERNAL_INFLOW;
     }
 
-	// --- assign type & cf values for a FLOW inflow
-	if (param == -1)
-	{
-		type = FLOW_INFLOW;
-	}
+    // --- assign type & cf values for a FLOW inflow
+    if (param == -1)
+    {
+        type = FLOW_INFLOW;
+    }
 
     // --- do the same for a pollutant inflow
     if ( ntoks >= 4 && param > -1)
@@ -115,9 +115,9 @@ int inflow_readExtInflow(char* tok[], int ntoks)
         basePat = project_findObject(TIMEPATTERN, tok[7]);
         if ( basePat < 0 ) return error_setInpError(ERR_NAME, tok[7]);
     }
-	
-	return(inflow_setExtInflow(j, param, type, tseries, basePat,
-		cf, baseline, sf));
+
+    return(inflow_setExtInflow(j, param, type, tseries, basePat,
+        cf, baseline, sf));
 }
 
 int inflow_validate(int param, int type, int tseries, int basePat, double *cf)
@@ -130,49 +130,49 @@ int inflow_validate(int param, int type, int tseries, int basePat, double *cf)
 // Output: cf = Unit Conversion
 // Return: returns Error Code
 {
-	int errcode = 0;
-	// Validate param
-	if (param >= Nobjects[POLLUT])
-	{
-		errcode = ERR_API_POLLUT_INDEX;
-	}
-	// Validate Type
-	else if (type != FLOW_INFLOW 
-	         && type != CONCEN_INFLOW 
-	         && type != MASS_INFLOW)
-	{
-		errcode = ERR_KEYWORD;
-	}
-	// Validate Timeseries Index
-	else if (tseries >= Nobjects[TSERIES])
-	{
-		errcode = ERR_API_TSERIES_INDEX;
-	}
-	// Validate Timepattern Index
-	else if (basePat >= Nobjects[TIMEPATTERN])
-	{
-		errcode = ERR_API_PATTERN_INDEX;
-	}
-	else
-	{
-		// --- assign type & cf values for a FLOW inflow
-		if ( type == FLOW_INFLOW )
-		{
-			*cf = 1.0/UCF(FLOW);
-		}
-		// --- include LperFT3 term in conversion factor for MASS_INFLOW
-		else if ( type == MASS_INFLOW ) 
-		{
-			*cf /= LperFT3;		
-		}
-	}
-	
-	return(errcode);
+    int errcode = 0;
+    // Validate param
+    if (param >= Nobjects[POLLUT])
+    {
+        errcode = ERR_API_POLLUT_INDEX;
+    }
+    // Validate Type
+    else if (type != FLOW_INFLOW 
+             && type != CONCEN_INFLOW 
+             && type != MASS_INFLOW)
+    {
+        errcode = ERR_KEYWORD;
+    }
+    // Validate Timeseries Index
+    else if (tseries >= Nobjects[TSERIES])
+    {
+        errcode = ERR_API_TSERIES_INDEX;
+    }
+    // Validate Timepattern Index
+    else if (basePat >= Nobjects[TIMEPATTERN])
+    {
+        errcode = ERR_API_PATTERN_INDEX;
+    }
+    else
+    {
+        // --- assign type & cf values for a FLOW inflow
+        if ( type == FLOW_INFLOW )
+        {
+            *cf = 1.0/UCF(FLOW);
+        }
+        // --- include LperFT3 term in conversion factor for MASS_INFLOW
+        else if ( type == MASS_INFLOW ) 
+        {
+            *cf /= LperFT3;		
+        }
+    }
+
+    return(errcode);
 }
 
 
 int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat,
-						double cf, double baseline, double sf)
+                        double cf, double baseline, double sf)
 // Purpose:  This function assigns property values to the inflow object 
 // Inputs:   j = Node index
 //           param = FLOW (-1) or pollutant index
@@ -185,45 +185,45 @@ int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat,
 // Return:   returns Error Code
 
 {
-	int errcode = 0;
-
-	// Validate Inflow
-	errcode = inflow_validate(param, type, tseries, basePat, &cf);
-	
-	if (errcode == 0)
-	{
-		TExtInflow* inflow;            // external inflow object
-
-		// --- check if an external inflow object for this constituent already exists
-		inflow = Node[j].extInflow;
-		while ( inflow )
-		{
-			if ( inflow->param == param ) break;
-			inflow = inflow->next;
-		}
-
-		// --- if it doesn't exist, then create it
-		if ( inflow == NULL )
-		{
-			inflow = (TExtInflow *) malloc(sizeof(TExtInflow));
-			if ( inflow == NULL ) 
-			{
-				return error_setInpError(ERR_MEMORY, "");
-			}
-			inflow->next = Node[j].extInflow;
-			Node[j].extInflow = inflow;
-		}
-		
-		// Assigning Values to the inflow object 
-		inflow->param    = param;
-		inflow->type     = type;
-		inflow->tSeries  = tseries;
-		inflow->cFactor  = cf;
-		inflow->sFactor  = sf;
-		inflow->baseline = baseline;
-		inflow->basePat  = basePat;
-		inflow->extIfaceInflow = 0.0;
-	}
+    int errcode = 0;
+    TExtInflow* inflow;            // external inflow object
+
+    // Validate Inflow
+    errcode = inflow_validate(param, type, tseries, basePat, &cf);
+
+    if (errcode == 0)
+    {
+
+        // --- check if an external inflow object for this constituent already exists
+        inflow = Node[j].extInflow;
+        while ( inflow )
+        {
+            if ( inflow->param == param ) break;
+            inflow = inflow->next;
+        }
+
+        // --- if it doesn't exist, then create it
+        if ( inflow == NULL )
+        {
+            inflow = (TExtInflow *) malloc(sizeof(TExtInflow));
+            if ( inflow == NULL ) 
+            {
+                return error_setInpError(ERR_MEMORY, "");
+            }
+            inflow->next = Node[j].extInflow;
+            Node[j].extInflow = inflow;
+        }
+
+        // Assigning Values to the inflow object 
+        inflow->param    = param;
+        inflow->type     = type;
+        inflow->tSeries  = tseries;
+        inflow->cFactor  = cf;
+        inflow->sFactor  = sf;
+        inflow->baseline = baseline;
+        inflow->basePat  = basePat;
+        inflow->extIfaceInflow = 0.0;
+    }
     return(errcode);
 }
 
@@ -265,7 +265,7 @@ double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate)
     double sf = inflow->sFactor;     // scaling factor
     double blv = inflow->baseline;   // baseline value
     double tsv = 0.0;                // time series value
-	double extIfaceInflow = inflow->extIfaceInflow;// external interfacing inflow
+    double extIfaceInflow = inflow->extIfaceInflow;// external interfacing inflow
 
     if ( p >= 0 )
     {
diff --git a/src/solver/input.c b/src/solver/input.c
index b36fd4289..390b6f088 100644
--- a/src/solver/input.c
+++ b/src/solver/input.c
@@ -6,6 +6,7 @@
 //   Date:     03/20/14  (Build 5.1.001)
 //             09/15/14  (Build 5.1.007)
 //             08/01/16  (Build 5.1.011)
+//             04/01/20  (Build 5.1.015)
 //   Author:   L. Rossman
 //
 //   Input data processing functions.
@@ -16,6 +17,8 @@
 //   Build 5.1.011:
 //   - Support added for reading hydraulic event dates.
 //
+//   Build 5.1.015:
+//   - Support added for multiple infiltration methods within a project.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -478,7 +481,7 @@ int  parseLine(int sect, char *line)
         return subcatch_readSubareaParams(Tok, Ntokens);
 
       case s_INFIL:
-        return infil_readParams(InfilModel, Tok, Ntokens);
+        return infil_readParams(InfilModel, Tok, Ntokens);                     //(5.1.015)
 
       case s_AQUIFER:
         j = Mobjects[AQUIFER];
diff --git a/src/solver/keywords.h b/src/solver/keywords.h
index 3724a0c89..af89e0d58 100644
--- a/src/solver/keywords.h
+++ b/src/solver/keywords.h
@@ -17,6 +17,10 @@
 //   - New keyword array defined for surcharge method.
 //-----------------------------------------------------------------------------
 
+#ifndef KEYWORDS_H
+#define KEYWORDS_H
+
+
 extern char* BuildupTypeWords[];
 extern char* CurveTypeWords[];
 extern char* DividerTypeWords[];
@@ -65,3 +69,6 @@ extern char* VolUnitsWords2[];
 extern char* WashoffTypeWords[];
 extern char* WeirTypeWords[];
 extern char* XsectTypeWords[];
+
+
+#endif //KEYWORDS_H
diff --git a/src/solver/lid.c b/src/solver/lid.c
index e7f6ef82d..45cb020f1 100644
--- a/src/solver/lid.c
+++ b/src/solver/lid.c
@@ -13,6 +13,7 @@
 //             03/14/17   (Build 5.1.012)
 //             05/10/18   (Build 5.1.013)
 //             03/01/20   (Build 5.1.014)
+//             04/01/20   (Build 5.1.015)
 //   Author:   L. Rossman (US EPA)
 //
 //   This module handles all data processing involving LID (Low Impact
@@ -69,7 +70,7 @@
 //
 //   Build 5.1.013:
 //   - Support added for LID units treating pervious area runoff.
-//   - Support added for open/closed head levels and multiplier v. head 
+//   - Support added for open/closed head levels and multiplier v. head
 //     control curve for underdrain flow.
 //   - Support added for unclogging permeable pavement at fixed intervals.
 //   - Support added for pollutant removal in underdrain flow.
@@ -77,6 +78,9 @@
 //   Build 5.1.014:
 //   - Fixed bug in creating LidProcs when there are no subcatchments.
 //   - Fixed bug in adding underdrain pollutant loads to mass balances.
+//
+//   Build 5.1.015:
+//   - Support added for mutiple infiltration methods within a project.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -106,7 +110,7 @@ enum LidLayerTypes {
     REMOVALS};               // pollutant removals                             //(5.1.013)
 
 //// Note: DRAINMAT must be placed before DRAIN so the two keywords can
-///        be distinguished from one another when parsing a line of input. 
+///        be distinguished from one another when parsing a line of input.
 
 char* LidLayerWords[] =
     {"SURFACE", "SOIL", "STORAGE", "PAVEMENT", "DRAINMAT", "DRAIN",
@@ -143,7 +147,7 @@ static double     MaxNativeInfil;      // native soil infil. rate limit (ft/s)
 //-----------------------------------------------------------------------------
 //  Imported Variables (from SUBCATCH.C)
 //-----------------------------------------------------------------------------
-// Volumes (ft3) for a subcatchment over a time step 
+// Volumes (ft3) for a subcatchment over a time step
 extern double     Vevap;               // evaporation
 extern double     Vpevap;              // pervious area evaporation
 extern double     Vinfil;              // non-LID infiltration
@@ -251,10 +255,10 @@ void lid_create(int lidCount, int subcatchCount)
             return;
         }
     }
-        
+
     //... initialize LID groups
     for (j = 0; j < GroupCount; j++) LidGroups[j] = NULL;
-    
+
     //... create LID objects
     if ( LidCount == 0 ) return;
     LidProcs = (TLidProc *) calloc(LidCount, sizeof(TLidProc));
@@ -287,7 +291,7 @@ void lid_create(int lidCount, int subcatchCount)
         {                                                                      //
             ErrorCode = ERR_MEMORY;                                            //
             return;                                                            //
-        }                                                                      // 
+        }                                                                      //
     }
 }
 
@@ -548,7 +552,7 @@ int addLidUnit(int j, int k, int n, double x[], char* fname,
     //... open report file if it was supplied
     if ( fname != NULL )
     {
-        if ( !createLidRptFile(lidUnit, fname) ) 
+        if ( !createLidRptFile(lidUnit, fname) )
             return error_setInpError(ERR_RPT_FILE, fname);
     }
     return 0;
@@ -559,7 +563,7 @@ int addLidUnit(int j, int k, int n, double x[], char* fname,
 int createLidRptFile(TLidUnit* lidUnit, char* fname)
 {
     TLidRptFile* rptFile;
-    
+
     rptFile = (TLidRptFile *) malloc(sizeof(TLidRptFile));
     if ( rptFile == NULL ) return 0;
     lidUnit->rptFile = rptFile;
@@ -574,7 +578,7 @@ int readSurfaceData(int j, char* toks[], int ntoks)
 //
 //  Purpose: reads surface layer data for a LID process from line of input
 //           data file
-//  Input:   j = LID process index 
+//  Input:   j = LID process index
 //           toks = array of string tokens
 //           ntoks = number of tokens
 //  Output:  returns error code
@@ -592,7 +596,7 @@ int readSurfaceData(int j, char* toks[], int ntoks)
         if ( ! getDouble(toks[i], &x[i-2]) || x[i-2] < 0.0 )
             return error_setInpError(ERR_NUMBER, toks[i]);
     }
-    if ( x[1] >= 1.0 ) return error_setInpError(ERR_NUMBER, toks[3]);           
+    if ( x[1] >= 1.0 ) return error_setInpError(ERR_NUMBER, toks[3]);
     if ( x[0] == 0.0 ) x[1] = 0.0;
 
     LidProcs[j].surface.thickness     = x[0] / UCF(RAINDEPTH);
@@ -609,7 +613,7 @@ int readPavementData(int j, char* toks[], int ntoks)
 //
 //  Purpose: reads pavement layer data for a LID process from line of input
 //           data file
-//  Input:   j = LID process index 
+//  Input:   j = LID process index
 //           toks = array of string tokens
 //           ntoks = number of tokens
 //  Output:  returns error code
@@ -654,7 +658,7 @@ int readPavementData(int j, char* toks[], int ntoks)
     if (LidProcs[j].pavement.thickness > 0.0)
     {
         LidProcs[j].pavement.clogFactor = x[4] *
-        LidProcs[j].pavement.thickness * 
+        LidProcs[j].pavement.thickness *
         LidProcs[j].pavement.voidFrac *
         (1.0 - LidProcs[j].pavement.impervFrac);
     }
@@ -673,7 +677,7 @@ int readSoilData(int j, char* toks[], int ntoks)
 //
 //  Purpose: reads soil layer data for a LID process from line of input
 //           data file
-//  Input:   j = LID process index 
+//  Input:   j = LID process index
 //           toks = array of string tokens
 //           ntoks = number of tokens
 //  Output:  returns error code
@@ -707,13 +711,13 @@ int readStorageData(int j, char* toks[], int ntoks)
 //
 //  Purpose: reads drainage layer data for a LID process from line of input
 //           data file
-//  Input:   j = LID process index 
+//  Input:   j = LID process index
 //           toks = array of string tokens
 //           ntoks = number of tokens
 //  Output:  returns error code
 //
 //  Format of data is:
-//    LID_ID STORAGE  Thickness  VoidRatio  Ksat  ClogFactor 
+//    LID_ID STORAGE  Thickness  VoidRatio  Ksat  ClogFactor
 //
 {
     int    i;
@@ -737,25 +741,25 @@ int readStorageData(int j, char* toks[], int ntoks)
 
     if (LidProcs[j].storage.thickness > 0.0)
     {
-        LidProcs[j].storage.clogFactor = x[3] * 
-        LidProcs[j].storage.thickness * 
+        LidProcs[j].storage.clogFactor = x[3] *
+        LidProcs[j].storage.thickness *
         LidProcs[j].storage.voidFrac;
     }
-    else 
+    else
     {
         LidProcs[j].storage.clogFactor = 0.0;
     }
 
     return 0;
 }
- 
+
 //=============================================================================
 
 int readDrainData(int j, char* toks[], int ntoks)
 //
 //  Purpose: reads underdrain data for a LID process from line of input
 //           data file
-//  Input:   j = LID process index 
+//  Input:   j = LID process index
 //           toks = array of string tokens
 //           ntoks = number of tokens
 //  Output:  returns error code
@@ -772,7 +776,7 @@ int readDrainData(int j, char* toks[], int ntoks)
     for (i = 0; i < 6; i++) x[i] = 0.0;                                        //(5.1.013)
     for (i = 2; i < 8; i++)                                                    //
     {
-        if ( ( ntoks > i && ! getDouble(toks[i], &x[i-2]) )  || x[i-2] < 0.0 ) //(5.1.013)
+        if ( ntoks > i && ! getDouble(toks[i], &x[i-2]) || x[i-2] < 0.0 )      //(5.1.013)
             return error_setInpError(ERR_NUMBER, toks[i]);
     }
 
@@ -793,14 +797,14 @@ int readDrainData(int j, char* toks[], int ntoks)
     LidProcs[j].drain.qCurve = i;                                              //
     return 0;
 }
- 
+
 //=============================================================================
 
 int readDrainMatData(int j, char* toks[], int ntoks)
 //
 //  Purpose: reads drainage mat data for a LID process from line of input
 //           data file
-//  Input:   j = LID process index 
+//  Input:   j = LID process index
 //           toks = array of string tokens
 //           ntoks = number of tokens
 //  Output:  returns error code
@@ -836,7 +840,7 @@ int readRemovalsData(int j, char* toks[], int ntoks)
 //
 //  Purpose: reads pollutant removal data for a LID process from line of input
 //           data file
-//  Input:   j = LID process index 
+//  Input:   j = LID process index
 //           toks = array of string tokens
 //           ntoks = number of tokens
 //  Output:  returns error code
@@ -885,7 +889,7 @@ void lid_writeSummary()
     TLidUnit*  lidUnit;
     TLidList*  lidList;
     TLidGroup  lidGroup;
- 
+
     fprintf(Frpt.file, "\n");
     fprintf(Frpt.file, "\n");
     fprintf(Frpt.file, "\n  *******************");
@@ -925,7 +929,7 @@ void lid_writeSummary()
 void lid_validate()
 //
 //  Purpose: validates LID process and group parameters.
-//  Input:   none 
+//  Input:   none
 //  Output:  none
 //
 {
@@ -939,7 +943,7 @@ void lid_validate()
 void validateLidProc(int j)
 //
 //  Purpose: validates LID process parameters.
-//  Input:   j = LID process index 
+//  Input:   j = LID process index
 //  Output:  none
 //
 {
@@ -960,7 +964,7 @@ void validateLidProc(int j)
         if ( LidProcs[j].soil.thickness <= 0.0 ) layerMissing = TRUE;
         break;
     case GREEN_ROOF:
-        if ( LidProcs[j].soil.thickness <= 0.0 ) layerMissing = TRUE; 
+        if ( LidProcs[j].soil.thickness <= 0.0 ) layerMissing = TRUE;
         if ( LidProcs[j].drainMat.thickness <= 0.0) layerMissing = TRUE;
         break;
     case POROUS_PAVEMENT:
@@ -979,8 +983,8 @@ void validateLidProc(int j)
     //... check pavement layer parameters
     if ( LidProcs[j].lidType == POROUS_PAVEMENT )
     {
-        if ( LidProcs[j].pavement.thickness  <= 0.0 
-        ||   LidProcs[j].pavement.kSat       <= 0.0 
+        if ( LidProcs[j].pavement.thickness  <= 0.0
+        ||   LidProcs[j].pavement.kSat       <= 0.0
         ||   LidProcs[j].pavement.voidFrac   <= 0.0
         ||   LidProcs[j].pavement.voidFrac   >  1.0
         ||   LidProcs[j].pavement.impervFrac >  1.0 )
@@ -995,7 +999,7 @@ void validateLidProc(int j)
     //... check soil layer parameters
     if ( LidProcs[j].soil.thickness > 0.0 )
     {
-        if ( LidProcs[j].soil.porosity      <= 0.0 
+        if ( LidProcs[j].soil.porosity      <= 0.0
         ||   LidProcs[j].soil.fieldCap      >= LidProcs[j].soil.porosity
         ||   LidProcs[j].soil.wiltPoint     >= LidProcs[j].soil.fieldCap
         ||   LidProcs[j].soil.kSat          <= 0.0
@@ -1019,9 +1023,9 @@ void validateLidProc(int j)
         }
     }
 
-    //... if no storage layer adjust void fraction and drain offset 
+    //... if no storage layer adjust void fraction and drain offset
     else
-    {    
+    {
         LidProcs[j].storage.voidFrac = 1.0;
         LidProcs[j].drain.offset = 0.0;
     }
@@ -1038,7 +1042,7 @@ void validateLidProc(int j)
     //... compute the surface layer's overland flow constant (alpha)
     if ( LidProcs[j].lidType == VEG_SWALE )
     {
-        if ( LidProcs[j].surface.roughness * 
+        if ( LidProcs[j].surface.roughness *
              LidProcs[j].surface.surfSlope <= 0.0 ||
              LidProcs[j].surface.thickness == 0.0
            )
@@ -1047,7 +1051,7 @@ void validateLidProc(int j)
             strcat(Msg, ERR_SWALE_SURF);
             report_writeErrorMsg(ERR_LID_PARAMS, Msg);
         }
-        else LidProcs[j].surface.alpha = 
+        else LidProcs[j].surface.alpha =
             1.49 * sqrt(LidProcs[j].surface.surfSlope) /
                 LidProcs[j].surface.roughness;
     }
@@ -1090,7 +1094,7 @@ void validateLidProc(int j)
         LidProcs[j].storage.kSat = 0.0;
     }
 
-    //... set storage layer parameters of a green roof 
+    //... set storage layer parameters of a green roof
     if ( LidProcs[j].lidType == GREEN_ROOF )
     {
         LidProcs[j].storage.thickness = LidProcs[j].drainMat.thickness;
@@ -1105,7 +1109,7 @@ void validateLidProc(int j)
 void validateLidGroup(int j)
 //
 //  Purpose: validates properties of LID units grouped in a subcatchment.
-//  Input:   j = subcatchment index 
+//  Input:   j = subcatchment index
 //  Output:  returns 1 if data are valid, 0 if not
 //
 {
@@ -1147,15 +1151,14 @@ void validateLidGroup(int j)
                 report_writeErrorMsg(ERR_LID_PARAMS, Msg);
             }
         }
-        
+
         //... assign vegetative swale infiltration parameters
         if ( LidProcs[k].lidType == VEG_SWALE )
         {
-            if ( InfilModel == GREEN_AMPT || InfilModel == MOD_GREEN_AMPT )
+            if ( Subcatch[j].infilModel == GREEN_AMPT ||                       //(5.1.015)
+                 Subcatch[j].infilModel == MOD_GREEN_AMPT )                    //(5.1.015)
             {
-                p[0] = GAInfil[j].S * UCF(RAINDEPTH);
-                p[1] = GAInfil[j].Ks * UCF(RAINFALL);
-                p[2] = GAInfil[j].IMDmax;
+                grnampt_getParams(j, p);                                       //(5.1.015)
                 if ( grnampt_setParams(&(lidUnit->soilInfil), p) == FALSE )
                 {
                     strcpy(Msg, LidProcs[k].ID);
@@ -1204,7 +1207,7 @@ void validateLidGroup(int j)
 void lid_initState()
 //
 //  Purpose: initializes the internal state of each LID in a subcatchment.
-//  Input:   none 
+//  Input:   none
 //  Output:  none
 //
 {
@@ -1245,7 +1248,7 @@ void lid_initState()
             initVol = 0.0;
             if ( LidProcs[k].soil.thickness > 0.0 )
             {
-                lidUnit->soilMoisture = LidProcs[k].soil.wiltPoint + 
+                lidUnit->soilMoisture = LidProcs[k].soil.wiltPoint +
                     lidUnit->initSat * (LidProcs[k].soil.porosity -
                     LidProcs[k].soil.wiltPoint);
                 initVol += lidUnit->soilMoisture * LidProcs[k].soil.thickness;
@@ -1266,7 +1269,7 @@ void lid_initState()
 
             //... initialize water balance totals
             lidproc_initWaterBalance(lidUnit, initVol);
-            //... initialize water rate 
+            //... initialize water rate
             lidproc_initWaterRate(lidUnit);
             lidUnit->volTreated = 0.0;
 
@@ -1282,7 +1285,7 @@ void lid_initState()
 
             //... set previous flux rates to 0
             for (i = 0; i < MAX_LAYERS; i++)
-            {    
+            {
                 lidUnit->oldFluxRates[i] = 0.0;
             }
 
@@ -1303,7 +1306,7 @@ void lid_initState()
 void  lid_setOldGroupState(int j)
 //
 //  Purpose: saves the current drain flow rate for the LIDs in a subcatchment.
-//  Input:   j = subcatchment index 
+//  Input:   j = subcatchment index
 //  Output:  none
 //
 {
@@ -1327,7 +1330,7 @@ void  lid_setOldGroupState(int j)
 int isLidPervious(int k)
 //
 //  Purpose: determines if a LID process allows infiltration or not.
-//  Input:   k = LID process index 
+//  Input:   k = LID process index
 //  Output:  returns 1 if process is pervious or 0 if not
 //
 {
@@ -1341,7 +1344,7 @@ double getSurfaceDepth(int j)
 //
 //  Purpose: computes the depth (volume per unit area) of ponded water on the
 //           surface of all LIDs within a subcatchment.
-//  Input:   j = subcatchment index 
+//  Input:   j = subcatchment index
 //  Output:  returns volumetric depth of ponded water (ft)
 //
 {
@@ -1397,9 +1400,9 @@ double   lid_getFlowToPerv(int j)
 
 double lid_getStoredVolume(int j)
 //
-//  Purpose: computes stored volume of water for all LIDs 
+//  Purpose: computes stored volume of water for all LIDs
 //           grouped within a subcatchment.
-//  Input:   j = subcatchment index 
+//  Input:   j = subcatchment index
 //  Output:  returns stored volume of water (ft3)
 //
 {
@@ -1426,7 +1429,7 @@ double  lid_getDrainFlow(int j, int timePeriod)
 //
 //  Purpose: returns flow from all of a subcatchment's LID drains for
 //           a designated time period
-//  Input:   j = subcatchment index 
+//  Input:   j = subcatchment index
 //           timePeriod = either PREVIOUS or CURRENT
 //  Output:  total drain flow (cfs) from the subcatchment.
 {
@@ -1454,11 +1457,11 @@ void  lid_addDrainLoads(int j, double c[], double tStep)
 {
     int    isRunoffLoad;     // true if drain becomes external runoff load
     int    p;                // pollutant index
-    double r;                // pollutant fractional removal 
+    double r;                // pollutant fractional removal
     double w;                // pollutant mass load (lb or kg)
     TLidUnit*  lidUnit;
-    TLidList*  lidList; 
-    TLidGroup  lidGroup; 
+    TLidList*  lidList;
+    TLidGroup  lidGroup;
 
     //... check if LID group exists
     lidGroup = LidGroups[j];
@@ -1469,11 +1472,11 @@ void  lid_addDrainLoads(int j, double c[], double tStep)
         while ( lidList )
         {
             lidUnit = lidList->lidUnit;
- 
+
             //... see if unit's drain flow becomes external runoff
             isRunoffLoad = (lidUnit->drainNode >= 0 ||
                             lidUnit->drainSubcatch == j);
-            
+
             //... for each pollutant not routed back on to subcatchment surface
             if (!lidUnit->toPerv) for (p = 0; p < Nobjects[POLLUT]; p++)
             {
@@ -1500,7 +1503,7 @@ void  lid_addDrainLoads(int j, double c[], double tStep)
 void lid_addDrainRunon(int j)
 //
 //  Purpose: adds drain flows from LIDs in a given subcatchment to the
-//           subcatchments that were designated to receive them 
+//           subcatchments that were designated to receive them
 //  Input:   j = index of subcatchment contributing underdrain flows
 //  Output:  none.
 //
@@ -1511,8 +1514,8 @@ void lid_addDrainRunon(int j)
     double q;                // drain flow rate (cfs)
     double w;                // mass of polllutant from drain flow             //(5.1.013)
     TLidUnit*  lidUnit;
-    TLidList*  lidList; 
-    TLidGroup  lidGroup; 
+    TLidList*  lidList;
+    TLidGroup  lidGroup;
 
     //... check if LID group exists
     lidGroup = LidGroups[j];
@@ -1551,7 +1554,7 @@ void lid_addDrainRunon(int j)
 
 void  lid_addDrainInflow(int j, double f)
 //
-//  Purpose: adds LID drain flow to conveyance system nodes 
+//  Purpose: adds LID drain flow to conveyance system nodes
 //  Input:   j = subcatchment index
 //           f = time interval weighting factor
 //  Output:  none.
@@ -1588,12 +1591,12 @@ void  lid_addDrainInflow(int j, double f)
                 Node[k].newLatFlow += q;
                 massbal_addInflowFlow(WET_WEATHER_INFLOW, q);
 
-                //... add pollutant load, based on parent subcatchment quality 
+                //... add pollutant load, based on parent subcatchment quality
                 for (p = 0; p < Nobjects[POLLUT]; p++)
                 {
                     //... get previous & current drain loads
                     w1 = lidUnit->oldDrainFlow * Subcatch[j].oldQual[p];
-                    w2 = lidUnit->newDrainFlow * Subcatch[j].newQual[p]; 
+                    w2 = lidUnit->newDrainFlow * Subcatch[j].newQual[p];
 
                     //... add interpolated load to node's wet weather loading
                     w = (1.0 - f) * w1 + f * w2;
@@ -1612,7 +1615,7 @@ void  lid_addDrainInflow(int j, double f)
 void lid_getRunoff(int j, double tStep)
 //
 //  Purpose: computes runoff and drain flows from the LIDs in a subcatchment.
-//  Input:   j     = subcatchment index 
+//  Input:   j     = subcatchment index
 //           tStep = time step (sec)
 //  Output:  updates following global quantities after LID treatment applied:
 //           Vevap, Vpevap, VlidInfil, VlidIn, VlidOut, VlidDrain.
@@ -1624,10 +1627,10 @@ void lid_getRunoff(int j, double tStep)
     double lidArea;               // area of an LID unit
     double qImperv = 0.0;         // runoff from impervious areas (cfs)
     double qPerv = 0.0;           // runoff from pervious areas (cfs)          //(5.1.013)
-    double lidInflow = 0.0;       // inflow to an LID unit (ft/s) 
+    double lidInflow = 0.0;       // inflow to an LID unit (ft/s)
     double qRunoff = 0.0;         // surface runoff from all LID units (cfs)
     double qDrain = 0.0;          // drain flow from all LID units (cfs)
-    double qReturn = 0.0;         // LID outflow returned to pervious area (cfs) 
+    double qReturn = 0.0;         // LID outflow returned to pervious area (cfs)
 
     //... return if there are no LID's
     theLidGroup = LidGroups[j];
@@ -1645,7 +1648,7 @@ void lid_getRunoff(int j, double tStep)
     //... get impervious and pervious area runoff from non-LID
     //    portion of subcatchment (cfs)
     if ( Subcatch[j].area > Subcatch[j].lidArea )
-    {    
+    {
         qImperv = getImpervAreaRunoff(j);
         qPerv = getPervAreaRunoff(j);                                          //(5.1.013)
     }
@@ -1678,7 +1681,7 @@ void lid_getRunoff(int j, double tStep)
 
             //... evaluate the LID unit's performance, updating the LID group's
             //    total surface runoff, drain flow, and flow returned to
-            //    pervious area 
+            //    pervious area
             evalLidUnit(j, lidUnit, lidArea, lidInflow, tStep,
                         &qRunoff, &qDrain, &qReturn);
         }
@@ -1690,7 +1693,7 @@ void lid_getRunoff(int j, double tStep)
     theLidGroup->flowToPerv = qReturn;
 
     //... save the LID group's total surface, drain and return flow volumes
-    VlidOut = qRunoff * tStep; 
+    VlidOut = qRunoff * tStep;
     VlidDrain = qDrain * tStep;
     VlidReturn = qReturn * tStep;
 }
@@ -1718,10 +1721,10 @@ void findNativeInfil(int j, double tStep)
     //... otherwise find infil. rate for the subcatchment's rainfall + runon
     else
     {
-        NativeInfil = infil_getInfil(j, InfilModel, tStep,
+        NativeInfil = infil_getInfil(j, tStep,
                                      Subcatch[j].rainfall,
                                      Subcatch[j].runon,
-                                     getSurfaceDepth(j));                      //(5.1.008)
+                                     getSurfaceDepth(j));                      //(5.1.015)
     }
 
     //... see if there is any groundwater-imposed limit on infil.
@@ -1823,7 +1826,7 @@ void evalLidUnit(int j, TLidUnit* lidUnit, double lidArea, double lidInflow,
     lidRunoff = lidproc_getOutflow(lidUnit, lidProc, lidInflow, EvapRate,
                                   NativeInfil, MaxNativeInfil, tStep,
                                   &lidEvap, &lidInfil, &lidDrain) * lidArea;
-    
+
     //... convert drain flow to CFS
     lidDrain *= lidArea;
 
@@ -1842,7 +1845,7 @@ void evalLidUnit(int j, TLidUnit* lidUnit, double lidArea, double lidInflow,
             lidDrain = 0.0;
         }
     }
- 
+
     //... update system flow balance if drain flow goes to a
     //    conveyance system node
     if ( lidUnit->drainNode >= 0 )
@@ -1910,7 +1913,7 @@ void lid_writeWaterBalance()
 "\n  --------------------------------------------------------------------------------------------------------------------"
 "\n                                         Total      Evap     Infil   Surface    Drain    Initial     Final  Continuity"
 "\n                                        Inflow      Loss      Loss   Outflow   Outflow   Storage   Storage       Error");
-    if ( UnitSystem == US ) fprintf(Frpt.file, 
+    if ( UnitSystem == US ) fprintf(Frpt.file,
 "\n  Subcatchment      LID Control             in        in        in        in        in        in        in           %%");
     else fprintf(Frpt.file,
 "\n  Subcatchment      LID Control             mm        mm        mm        mm        mm        mm        mm           %%");
@@ -1940,7 +1943,7 @@ void lid_writeWaterBalance()
                     lidUnit->waterBalance.finalVol*ucf);
 
             //... compute flow balance error
-            inflow = lidUnit->waterBalance.initVol + 
+            inflow = lidUnit->waterBalance.initVol +
                      lidUnit->waterBalance.inflow;
             outflow = lidUnit->waterBalance.finalVol +
                       lidUnit->waterBalance.evap +
@@ -2018,8 +2021,8 @@ void initLidRptFile(char* title, char* lidID, char* subcatchID, TLidUnit* lidUni
 
 
 int lid_getLidUnitCount(int index)
-// Input:   index = Index of desired subcatchment 
-// Output:  int = number of lid units for subcatchment 
+// Input:   index = Index of desired subcatchment
+// Output:  int = number of lid units for subcatchment
 // Return:  number of lid units for subcatchment
 // Purpose: count number of lid units for subcatchment
 {
@@ -2028,7 +2031,7 @@ int lid_getLidUnitCount(int index)
     TLidGroup lidGroup;
 
     lidGroup = LidGroups[index];
-    
+
     if (lidGroup)
     {
         lidList = lidGroup->lidList;
@@ -2045,13 +2048,13 @@ int lid_getLidUnitCount(int index)
 
 TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode)
 //
-// Input:   index = Index of desired subcatchment 
+// Input:   index = Index of desired subcatchment
 //          lidIndex = Index of desired lid control (subcatchment allow for multiple lids)
 //          errcode  = ptr to errcode
-// Output:  TLidUnit = TLidUnit ptr 
-// Return:  TLidUnit ptr 
+// Output:  TLidUnit = TLidUnit ptr
+// Return:  TLidUnit ptr
 // Purpose: Gets lid unit (TLidUnit) ptr
-{   
+{
     int currLidIndex = 0;
     int unitCount = 1;
     TLidUnit* lidUnit = NULL;
@@ -2063,16 +2066,16 @@ TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode)
     {
         *errcode = ERR_API_UNDEFINED_LID;
     }
-    else 
+    else
     {
         lidList = lidGroup->lidList;
 
         // Patch solution for now
         // Realized the lid units are stored in reverse order of
         // how they are defined in the [LID USAGE]
-        // For now, I will just count the number of Lid Units in Lid List 
+        // For now, I will just count the number of Lid Units in Lid List
         unitCount = lid_getLidUnitCount(index);
-    
+
         if (lidIndex > (unitCount - 1))
         {
             *errcode = ERR_API_LIDUNIT_INDEX;
@@ -2082,7 +2085,7 @@ TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode)
         {
             // update lidIndex due to reverse order
             lidIndex = unitCount - lidIndex - 1;
-    
+
             // Traverse through lid list to find lid unit
             while ((lidList) && (currLidIndex <= lidIndex))
             {
@@ -2090,7 +2093,7 @@ TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode)
                 currLidIndex += 1;
                 lidList = lidList->nextLidUnit;
             }
-    
+
             // Verify that the lid unit found matches the one specified by the user
             if (!((currLidIndex - 1) == lidIndex))
             {
@@ -2106,8 +2109,8 @@ TLidUnit* lid_getLidUnit(int index, int lidIndex, int* errcode)
 TLidProc* lid_getLidProc(int index)
 //
 // Input:   index = Index of desired lid control
-// Output:  ptr = TLidProc ptr 
-// Return:  TLidProc ptr 
+// Output:  ptr = TLidProc ptr
+// Return:  TLidProc ptr
 // Purpose: Gets lid process (TLidProc) ptr
 {
     TLidProc* ptr;
@@ -2118,8 +2121,8 @@ TLidProc* lid_getLidProc(int index)
 
 TLidGroup lid_getLidGroup(int index)
 //
-// Input:   index = index of desired subcatchment 
-// Output:  result = result data desired 
+// Input:   index = index of desired subcatchment
+// Output:  result = result data desired
 // Return:  TLidGroup ptr
 // Purpose: Gets lid group (TLidGroup) ptr
 {
@@ -2131,7 +2134,7 @@ TLidGroup lid_getLidGroup(int index)
 void lid_validateLidProc(int index)
 //
 //  Purpose: hook to lid internal function to validate LID process parameters.
-//  Input:   index = Index of desired subcatchment 
+//  Input:   index = Index of desired subcatchment
 //  Output:  none
 {
     validateLidProc(index);
@@ -2141,7 +2144,7 @@ void lid_validateLidProc(int index)
 void lid_validateLidGroup(int index)
 //
 //  Purpose: hook to lid internal function to validate LID process parameters.
-//  Input:   index = Index of desired subcatchment 
+//  Input:   index = Index of desired subcatchment
 //  Output:  none
 {
     validateLidGroup(index);
@@ -2160,14 +2163,14 @@ void lid_updateLidUnit(TLidUnit* lidUnit, int subIndex)
     lid_validateLidGroup(subIndex);
     if (LidProcs[lidIndex].soil.thickness > 0.0)
     {
-        lidUnit->soilMoisture = LidProcs[lidIndex].soil.wiltPoint + 
-            lidUnit->initSat * (LidProcs[lidIndex].soil.porosity - 
+        lidUnit->soilMoisture = LidProcs[lidIndex].soil.wiltPoint +
+            lidUnit->initSat * (LidProcs[lidIndex].soil.porosity -
                 LidProcs[lidIndex].soil.wiltPoint);
     }
 
     if (LidProcs[lidIndex].storage.thickness > 0.0)
     {
-        lidUnit->storageDepth = lidUnit->initSat * 
+        lidUnit->storageDepth = lidUnit->initSat *
             LidProcs[lidIndex].storage.thickness;
     }
 
@@ -2227,7 +2230,7 @@ void lid_updateLidGroup(int index)
 
         if (area > 0.0 && Subcatch[index].subArea[i].N > 0.0)
         {
-            Subcatch[index].subArea[i].alpha = 1.49 * Subcatch[index].width / area * 
+            Subcatch[index].subArea[i].alpha = 1.49 * Subcatch[index].width / area *
                 sqrt(Subcatch[index].slope) / Subcatch[index].subArea[i].N;
         }
     }
@@ -2248,7 +2251,7 @@ void lid_updateLidGroup(int index)
 
 void lid_updateAllLidUnit(int lidIndex)
 {
-// 
+//
 // Purpose: update all lid unit parameters due to change in lid control parameters
 // Input:   lidIndex = Index of desired lid control (subcatchment allow for multiple lids)
 // Output:  none
@@ -2279,4 +2282,4 @@ void lid_updateAllLidUnit(int lidIndex)
         }
 
     }
-}
\ No newline at end of file
+}
diff --git a/src/solver/lid.h b/src/solver/lid.h
index abd5bfb48..d36ecda72 100644
--- a/src/solver/lid.h
+++ b/src/solver/lid.h
@@ -18,7 +18,7 @@
 //   - Detailed LID reporting modified.
 //
 //   Build 5.1.011:
-//   - Water depth replaces moisture content for LID's pavement layer. 
+//   - Water depth replaces moisture content for LID's pavement layer.
 //   - Arguments for lidproc_saveResults() modified.
 //
 //   Build 5.1.012:
@@ -41,6 +41,7 @@
 #ifndef LID_H
 #define LID_H
 
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -51,8 +52,8 @@
 //-----------------------------------------------------------------------------
 enum LidTypes {
     BIO_CELL,                // bio-retention cell
-    RAIN_GARDEN,             // rain garden 
-    GREEN_ROOF,              // green roof 
+    RAIN_GARDEN,             // rain garden
+    GREEN_ROOF,              // green roof
     INFIL_TRENCH,            // infiltration trench
     POROUS_PAVEMENT,         // porous pavement
     RAIN_BARREL,             // rain barrel
@@ -73,7 +74,7 @@ typedef struct
 {
     double    thickness;          // depression storage or berm ht. (ft)
     double    voidFrac;           // available fraction of storage volume
-    double    roughness;          // surface Mannings n 
+    double    roughness;          // surface Mannings n
     double    surfSlope;          // land surface slope (fraction)
     double    sideSlope;          // swale side slope (run/rise)
     double    alpha;              // slope/roughness term in Manning eqn.
@@ -160,7 +161,7 @@ typedef struct
     double         finalVol;      // final stored volume (ft)
 }  TWaterBalance;
 
-// 
+//
 typedef struct
 {
     double         evap;           // evaporation rate (ft/s)
@@ -203,7 +204,7 @@ typedef struct
     int      drainNode;      // node receiving drain flow
     TLidRptFile* rptFile;    // pointer to detailed report file
 
-    TGrnAmpt soilInfil;      // infil. object for biocell soil layer 
+    TGrnAmpt soilInfil;      // infil. object for biocell soil layer
     double   surfaceDepth;   // depth of ponded water on surface layer (ft)
     double   paveDepth;      // depth of water in porous pavement layer
     double   soilMoisture;   // moisture content of biocell soil layer
@@ -211,7 +212,7 @@ typedef struct
 
     // net inflow - outflow from previous time step for each LID layer (ft/s)
     double   oldFluxRates[MAX_LAYERS];
-                                     
+
     double   dryTime;        // time since last rainfall (sec)
     double   oldDrainFlow;   // previous drain flow (cfs)
     double   newDrainFlow;   // current drain flow (cfs)
@@ -268,7 +269,7 @@ void     lid_writeWaterBalance(void);
 int         lid_getLidUnitCount(int index);
 TLidUnit*   lid_getLidUnit(int index, int lidIndex, int* errcode);
 TLidProc*   lid_getLidProc(int index);
-TLidGroup   lid_getLidGroup(int index);   
+TLidGroup   lid_getLidGroup(int index);
 void        lid_validateLidProc(int index);
 void        lid_validateLidGroup(int index);
 void        lid_updateLidUnit(TLidUnit* lidUnit, int subIndex);
@@ -284,4 +285,6 @@ double   lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc,
 
 void     lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall,
          double ucfRainDepth);
-#endif
+
+
+#endif //LID_H
diff --git a/src/solver/macros.h b/src/solver/macros.h
index 998e92ac0..6f10ae464 100644
--- a/src/solver/macros.h
+++ b/src/solver/macros.h
@@ -7,6 +7,10 @@
 //   Author:  L. Rossman
 //-----------------------------------------------------------------------------
 
+#ifndef MACROS_H
+#define MACROS_H
+
+
 //--------------------------------------------------
 // Macro to test for successful allocation of memory
 //--------------------------------------------------
@@ -36,3 +40,6 @@
 // Macro to evaluate function x with error checking
 //-------------------------------------------------
 #define CALL(x) (ErrorCode = ((ErrorCode>0) ? (ErrorCode) : (x)))
+
+
+#endif //MACROS_H
diff --git a/src/solver/mathexpr.h b/src/solver/mathexpr.h
index 34d81eafd..ab56b7fba 100644
--- a/src/solver/mathexpr.h
+++ b/src/solver/mathexpr.h
@@ -8,6 +8,10 @@
 **  LAST UPDATE:   03/20/14
 ******************************************************************************/
 
+#ifndef MATHEXPR_H
+#define MATHEXPR_H
+
+
 //  Node in a tokenized math expression list
 struct ExprNode
 {
@@ -27,3 +31,6 @@ double mathexpr_eval(MathExpr* expr, double (*getVal) (int));
 
 //  Deletes a tokenized math expression
 void  mathexpr_delete(MathExpr* expr);
+
+
+#endif //MATHEXPR_H
diff --git a/src/solver/mempool.h b/src/solver/mempool.h
index cc7b45cc1..864d2d6c3 100644
--- a/src/solver/mempool.h
+++ b/src/solver/mempool.h
@@ -7,6 +7,10 @@
 //  alloc pool - only the alloc routines know its structure.
 //-----------------------------------------------------------------------------
 
+#ifndef MEMPOOL_H
+#define MEMPOOL_H
+
+
 typedef struct
 {
    long  dummy;
@@ -17,3 +21,6 @@ char           *Alloc(long);
 alloc_handle_t *AllocSetPool(alloc_handle_t *);
 void            AllocReset(void);
 void            AllocFreePool(void);
+
+
+#endif //MEMPOOL_H
diff --git a/src/solver/node.c b/src/solver/node.c
index b74909d8c..f2445097d 100644
--- a/src/solver/node.c
+++ b/src/solver/node.c
@@ -9,6 +9,7 @@
 //             08/05/15   (Build 5.1.010)
 //             05/10/18   (Build 5.1.013)
 //             03/01/20   (Build 5.1.014)
+//             04/14/20   (Build 5.1.015)
 //   Author:   L. Rossman
 //
 //   Conveyance system node functions.
@@ -31,6 +32,10 @@
 //
 //   Build 5.1.014:
 //   - Fixed bug in storage_losses() that affected storage exfiltration.
+//
+//   Build 5.1.015:
+//   - Fatal error issued if a storage node's area curve produces a negative
+//     volume when extrapolated to the node's full depth.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -211,6 +216,11 @@ void  node_validate(int j)
     if ( Node[j].initDepth > Node[j].fullDepth + Node[j].surDepth )
         report_writeErrorMsg(ERR_NODE_DEPTH, Node[j].ID);
 
+    // --- check for negative volume for storage node at full depth            //(5.1.015)
+    if (Node[j].type == STORAGE)                                               //
+        if (node_getVolume(j, Node[j].fullDepth) < 0.0)                        //
+            report_writeErrorMsg(ERR_STORAGE_VOLUME, Node[j].ID);              //
+
     if ( Node[j].type == DIVIDER ) divider_validate(j);
 
     // --- initialize dry weather inflows
@@ -795,7 +805,7 @@ void  storage_getVolDiff(double y, double* f, double* df, void* p)
     int    k;
     double e, v;
     TStorageVol* storageVol;
-		
+
     // ... cast void pointer p to a TStorageVol object
     storageVol = (TStorageVol *)p;
     k = storageVol->k;
@@ -831,7 +841,7 @@ double storage_getVolume(int j, double d)
 
     // --- use table integration if area v. depth table exists
     if ( i >= 0 )
-        return table_getArea(&Curve[i], d*UCF(LENGTH)) / UCF(VOLUME);
+      return table_getArea(&Curve[i], d*UCF(LENGTH)) / UCF(VOLUME);
 
     // --- otherwise use functional area v. depth relation
     else
@@ -841,6 +851,7 @@ double storage_getVolume(int j, double d)
         v += Storage[k].aCoeff / (Storage[k].aExpon+1.0) *
              pow(d, Storage[k].aExpon+1.0);
         return v / UCF(VOLUME);
+
     }
 }
 
diff --git a/src/solver/objects.h b/src/solver/objects.h
index 96e09e158..265d0409f 100644
--- a/src/solver/objects.h
+++ b/src/solver/objects.h
@@ -9,6 +9,7 @@
 //            08/05/15  (Build 5.1.010)
 //            08/01/16  (Build 5.1.011)
 //            05/10/18  (Build 5.1.013)
+//            04/01/20  (Build 5.1.015)
 //
 //   Author:  L. Rossman (EPA)
 //            M. Tryby (EPA)
@@ -54,8 +55,16 @@
 //   - Adjustment patterns added to TSubcatch structure.
 //   - Members impervRunoff and pervRunoff added to TSubcatchStats structure.
 //   - Member cdCurve (weir coeff. curve) added to TWeir structure.
+//
+//   Build 5.1.015:
+//   - Support added for multiple infiltration methods within a project.
+//   - Support added for grouped freqency table of routing time steps.
 //-----------------------------------------------------------------------------
 
+#ifndef OBJECTS_H
+#define OBJECTS_H
+
+
 #include "mathexpr.h"
 #include "infil.h"
 #include "exfil.h"
@@ -109,8 +118,8 @@ typedef struct
 typedef struct
 {
    char*         ID;              // raingage name
-   double		 externalRain;    // rainfall-rate injected RAIN API
-   int           dataSource;      // data from time series or file 
+   double        externalRain;    // rainfall-rate injected RAIN API
+   int           dataSource;      // data from time series or file
    int           tSeries;         // rainfall data time series index
    char          fname[MAXFNAME+1]; // name of rainfall data file
    char          staID[MAXMSG+1]; // station number
@@ -134,7 +143,7 @@ typedef struct
    double        reportRainfall;  // rainfall value used for reported results
    int           coGage;          // index of gage with same rain timeseries
    int           isUsed;          // TRUE if gage used by any subcatchment
-   int           isCurrent;       // TRUE if gage's rainfall is current 
+   int           isCurrent;       // TRUE if gage's rainfall is current
 }  TGage;
 
 //-------------------
@@ -142,7 +151,7 @@ typedef struct
 //-------------------
 typedef struct
 {
-   int           dataSource;      // data from time series or file 
+   int           dataSource;      // data from time series or file
    int           tSeries;         // temperature data time series index
    DateTime      fileStartDate;   // starting date of data read from file
    double        elev;            // elev. of study area (ft)
@@ -190,11 +199,11 @@ typedef struct
     int          tSeries;         // time series index
     double       monthlyEvap[12]; // monthly evaporation values
     double       panCoeff[12];    // monthly pan coeff. values
-    int          recoveryPattern; // soil recovery factor pattern 
+    int          recoveryPattern; // soil recovery factor pattern
     int          dryOnly;         // true if evaporation only in dry periods
     //----------------------------
     double       rate;            // current evaporation rate (ft/sec)
-    double       recoveryFactor;  // current soil recovery factor 
+    double       recoveryFactor;  // current soil recovery factor
 }   TEvap;
 
 //-------------------
@@ -262,7 +271,7 @@ typedef struct
 //------------------------
 typedef struct
 {
-    int           aquifer;        // index of associated gw aquifer 
+    int           aquifer;        // index of associated gw aquifer
     int           node;           // index of node receiving gw flow
     double        surfElev;       // elevation of ground surface (ft)
     double        a1, b1;         // ground water outflow coeff. & exponent
@@ -370,6 +379,7 @@ typedef struct
    int           gage;            // raingage index
    int           outNode;         // outlet node index
    int           outSubcatch;     // outlet subcatchment index
+   int           infilModel;      // infiltration method index                 //(5.1.015)
    int           infil;           // infiltration object index
    TSubarea      subArea[3];      // sub-area data
    double        width;           // overland flow width (ft)
@@ -390,7 +400,7 @@ typedef struct
    double        lidArea;         // area devoted to LIDs (ft2)
    double        rainfall;        // current rainfall (ft/sec)
    double        evapLoss;        // current evap losses (ft/sec)
-   double        infilLoss;       // current infil losses (ft/sec) 
+   double        infilLoss;       // current infil losses (ft/sec)
    double        runon;           // runon from other subcatchments (cfs)
    double        oldRunoff;       // previous runoff (cfs)
    double        newRunoff;       // current runoff (cfs)
@@ -516,7 +526,7 @@ typedef struct
    double*       newQual;         // current quality state
    double*	 extQual;	  // external quality state
    double*	 inQual;          // inflow quality state
-   double*	 reactorQual;     // concentration in the mixed reactor   
+   double*	 reactorQual;     // concentration in the mixed reactor
    double        oldFlowInflow;   // previous flow inflow
    double        oldNetInflow;    // previous net inflow
 }  TNode;
@@ -549,7 +559,7 @@ typedef struct
    TExfil*     exfil;             // ptr. to exfiltration object
    //-----------------------------
    double      hrt;               // hydraulic residence time (sec)
-   double      evapLoss;          // evaporation loss (ft3) 
+   double      evapLoss;          // evaporation loss (ft3)
    double      exfilLoss;         // exfiltration loss (ft3)
 }  TStorage;
 
@@ -601,10 +611,10 @@ typedef struct
     double       aFull;                     // area when full (ft2)
     double       rFull;                     // hyd. radius when full (ft)
     double       wMax;                      // width at widest point (ft)
-    double       ywMax;                     // depth at max width (ft) 
+    double       ywMax;                     // depth at max width (ft)
     double       sMax;                      // section factor at max. flow (ft^4/3)
     double       aMax;                      // area at max. flow (ft2)
-    double       lengthFactor;              // floodplain / channel length 
+    double       lengthFactor;              // floodplain / channel length
     //--------------------------------------
     double       roughness;                 // Manning's n
     double       areaTbl[N_TRANSECT_TBL];   // table of area v. depth
@@ -621,7 +631,7 @@ typedef struct
 {
     int          curve;                     // index of shape curve
     int          nTbl;                      // size of geometry tables
-    double       aFull;                     // area when full 
+    double       aFull;                     // area when full
     double       rFull;                     // hyd. radius when full
     double       wMax;                      // max. width
     double       sMax;                      // max. section factor
@@ -670,8 +680,8 @@ typedef struct
    double*       oldQual;         // previous quality state
    double*       newQual;         // current quality state
    double*       totalLoad;       // total quality mass loading
-   double*       extQual;	  // external quality state 	
-   double*	 reactorQual;     // concentration in the mixed reactor   
+   double*       extQual;	  // external quality state
+   double*	 reactorQual;     // concentration in the mixed reactor
    int           flowClass;       // flow classification
    double        dqdh;            // change in flow w.r.t. head (ft2/sec)
    signed char   direction;       // flow direction flag
@@ -712,10 +722,10 @@ typedef struct
 {
    int           type;            // pump type
    int           pumpCurve;       // pump curve table index
-   double        initSetting;     // initial speed setting 
+   double        initSetting;     // initial speed setting
    double        yOn;             // startup depth (ft)
    double        yOff;            // shutoff depth (ft)
-   double        xMin;            // minimum pt. on pump curve 
+   double        xMin;            // minimum pt. on pump curve
    double        xMax;            // maximum pt. on pump curve
 }  TPump;
 
@@ -846,7 +856,7 @@ typedef struct
 //-------------------------------
 // typedef struct
 // {                                 // All volume totals are in ft3.
-//    double        rainfall;        // rainfall volume 
+//    double        rainfall;        // rainfall volume
 //    double        evap;            // evaporation loss
 //    double        infil;           // infiltration loss
 //    double        runoff;          // runoff volume
@@ -918,6 +928,7 @@ typedef SM_RoutingTotals TRoutingTotals;
 //-----------------------
 // SYSTEM-WIDE STATISTICS
 //-----------------------
+#define TIMELEVELS 6                                                           //(5.1.015)
 typedef struct
 {
    double        minTimeStep;
@@ -925,6 +936,8 @@ typedef struct
    double        avgTimeStep;
    double        avgStepCount;
    double        steadyStateCount;
+   double        timeStepIntervals[TIMELEVELS];                                //(5.1.015)
+   int           timeStepCounts[TIMELEVELS];                                   //(5.1.015)
 }  TSysStats;
 
 //--------------------
@@ -949,7 +962,7 @@ typedef struct
 //     double       evap;
 //     double       infil;
 //     double       runoff;
-//     double       maxFlow;         
+//     double       maxFlow;
 // 	double       impervRunoff;                                                 //(5.1.013)
 // 	double       pervRunoff;                                                   //
 // }  TSubcatchStats;
@@ -1003,13 +1016,13 @@ typedef SM_StorageStats TStorageStats;
 // {
 //    double       avgFlow;
 //    double       maxFlow;
-//    double*      totalLoad;   
+//    double*      totalLoad;
 //    int          totalPeriods;
 // }  TOutfallStats;
 
 typedef SM_OutfallStats TOutfallStats;
 
-//---------------- 
+//----------------
 // PUMP STATISTICS
 //----------------
 // typedef struct
@@ -1060,15 +1073,18 @@ typedef struct
    int           objType;         // either NODE or LINK
    int           index;           // node or link index
    double        value;           // value of node or link statistic
-}  TMaxStats; 
+}  TMaxStats;
 
 //------------------
 // REPORT FIELD INFO
 //------------------
-typedef struct 
+typedef struct
 {
-   char          Name[80];        // name of reported variable 
+   char          Name[80];        // name of reported variable
    char          Units[80];       // units of reported variable
    char          Enabled;         // TRUE if appears in report table
    int           Precision;       // number of decimal places when reported
 }  TRptField;
+
+
+#endif //OBJECTS_H
diff --git a/src/solver/odesolve.h b/src/solver/odesolve.h
index 86fc86e67..77316452f 100644
--- a/src/solver/odesolve.h
+++ b/src/solver/odesolve.h
@@ -5,8 +5,15 @@
 //
 //-----------------------------------------------------------------------------
 
+#ifndef ODESOLVE_H
+#define ODESOLVE_H
+
+
 // functions that open, close, and use the ODE solver
 int  odesolve_open(int n);
 void odesolve_close(void);
 int  odesolve_integrate(double ystart[], int n, double x1, double x2,
      double eps, double h1, void (*derivs)(double, double*, double*));
+
+
+#endif //ODESOLVE_H
diff --git a/src/solver/project.c b/src/solver/project.c
index 70b5a8c56..7f092ca2d 100644
--- a/src/solver/project.c
+++ b/src/solver/project.c
@@ -11,6 +11,7 @@
 //             08/01/16  (Build 5.1.011)
 //             03/14/17  (Build 5.1.012)
 //             05/10/18  (Build 5.1.013)
+//             04/01/20  (Build 5.1.015)
 //   Author:   L. Rossman
 //
 //   Project management functions.
@@ -55,6 +56,8 @@
 //   - More robust parsing of MinSurfarea option provided.
 //   - Support added for new RuleStep analysis option.
 //
+//   Build 5.1.015: 
+//   - Support added for multiple infiltration methods within a project.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -1019,7 +1022,7 @@ void createObjects()
     if ( ErrorCode ) return;
 
     // --- allocate memory for infiltration data
-    infil_create(Nobjects[SUBCATCH], InfilModel);
+    infil_create(Nobjects[SUBCATCH]);                                          //(5.1.015)
 
     // --- allocate memory for water quality state variables
     for (j = 0; j < Nobjects[SUBCATCH]; j++)
diff --git a/src/solver/qualrout.c b/src/solver/qualrout.c
index fd94ccd60..d8a933beb 100644
--- a/src/solver/qualrout.c
+++ b/src/solver/qualrout.c
@@ -7,6 +7,7 @@
 //             04/02/15   (Build 5.1.008)
 //             04/30/15   (Build 5.1.009)
 //             08/05/15   (Build 5.1.010)
+//             04/01/20   (Build 5.1.015)
 //   Author:   L. Rossman
 //
 //   Water quality routing functions.
@@ -22,6 +23,8 @@
 //   - Entire module re-written to be more compact and easier to follow.
 //   - Neglible depth limit replaced with a negligible volume limit.
 //
+//   Build 5.1.015:
+//   - Fixed mass balance issue for empty storage nodes that flood.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -466,8 +469,8 @@ void  findStorageQual(int j, double tStep)
         wIn = Node[j].newQual[p];
         c2 = getMixedQual(c1, v1, wIn, qIn, tStep);
 
-        // --- set concen. to zero if remaining volume is negligible
-        if ( Node[j].newVolume <= ZeroVolume )
+// --- set concen. to zero if remaining volume & inflow is negligible          //(5.1.015)
+        if (Node[j].newVolume <= ZeroVolume && qIn <= FLOW_TOL)                //(5.1.015)
         {
             massbal_addToFinalStorage(p, c2 * Node[j].newVolume);
             c2 = 0.0;
diff --git a/src/solver/report.c b/src/solver/report.c
index 07598b98a..07c6bd968 100644
--- a/src/solver/report.c
+++ b/src/solver/report.c
@@ -11,6 +11,7 @@
 //             03/14/17    (Build 5.1.012)
 //             05/10/18    (Build 5.1.013)
 //             03/01/20    (Build 5.1.014)
+//             05/18/20    (Build 5.1.015)
 //   Author:   L. Rossman (EPA)
 //
 //   Report writing functions.
@@ -43,6 +44,10 @@
 //
 //   Build 5.1.014:
 //   - Fixed bug in confusing keywords with ID names in report_readOptions().
+//
+//   Build 5.1.015:
+//   - Fixes bug in summary statistics when Report Start date > Start Date.
+//   - Support added for grouped freqency table of routing time steps.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -89,7 +94,7 @@ static void report_Nodes(void);
 static void report_NodeHeader(char *id);
 static void report_Links(void);
 static void report_LinkHeader(char *id);
-
+static void report_RouteStepFreq(TSysStats* sysStats);                         //(5.1.015)
 
 //=============================================================================
 
@@ -1038,10 +1043,11 @@ void report_writeSysStats(TSysStats* sysStats)
 //
 {
     double x;
-    double eventStepCount = (double)StepCount - sysStats->steadyStateCount;
+    double eventStepCount;  // Routing steps taken during reporting period   //(5.1.015)
 
-    if ( Nobjects[LINK] == 0 || StepCount == 0
-	                     || eventStepCount == 0.0 ) return; 
+    eventStepCount = ReportStepCount - sysStats->steadyStateCount;           //(5.1.015)
+    if ( Nobjects[LINK] == 0 || TotalStepCount == 0
+        || eventStepCount == 0.0 ) return; 
     WRITE("");
     WRITE("*************************");
     WRITE("Routing Time Step Summary");
@@ -1064,9 +1070,37 @@ void report_writeSysStats(TSysStats* sysStats)
     fprintf(Frpt.file,
         "\n  Percent Not Converging      :  %7.2f",
         100.0 * (double)NonConvergeCount / eventStepCount);
+
+    // --- write grouped frequency table of variable routing time steps        //(5.1.015)
+    if (RouteModel == DW && CourantFactor > 0.0)                               //
+        report_RouteStepFreq(sysStats);                                        //
     WRITE("");
 }
 
+//=============================================================================
+
+////  New function added to release 5.1.015.  ////                             //(5.1.015)
+void report_RouteStepFreq(TSysStats* sysStats)
+//
+//  Input:   sysStats = simulation statistics for overall system
+//  Output:  none
+//  Purpose: writes grouped frequency table of routing time steps to report file.
+//
+{
+    double totalSteps = 0.0;
+    int    i;
+
+    for (i = 1; i < TIMELEVELS; i++)
+        totalSteps += sysStats->timeStepCounts[i];
+    fprintf(Frpt.file,
+        "\n  Time Step Frequencies       :");
+    for (i = 1; i < TIMELEVELS; i++)
+        fprintf(Frpt.file,
+            "\n     %6.3f - %6.3f sec      :  %7.2f %%",
+            sysStats->timeStepIntervals[i-1], sysStats->timeStepIntervals[i],
+            100.0 * (double)(sysStats->timeStepCounts[i]) / totalSteps);
+}
+
 
 //=============================================================================
 //      SIMULATION RESULTS REPORTING
diff --git a/src/solver/stats.c b/src/solver/stats.c
index cd571c537..b23d015c1 100644
--- a/src/solver/stats.c
+++ b/src/solver/stats.c
@@ -9,6 +9,7 @@
 //             08/01/16   (Build 5.1.011)
 //             03/14/17   (Build 5.1.012)
 //             05/10/18   (Build 5.1.013)
+//             04/01/20   (Build 5.1.015)
 //   Author:   L. Rossman (EPA)
 //             R. Dickinson (CDM)
 //
@@ -36,6 +37,11 @@
 //   - Statistics on impervious and pervious runoff totals added.
 //   - Storage nodes with a non-zero surcharge depth (e.g. enclosed tanks)
 //     can now be classified as being surcharged.
+//
+//   Build 5.1.015:
+//   - Fixes bug in summary statistics when Report Start date > Start Date.
+//   - Fixes failure to initialize all subcatchment groundwater statistics.
+//   - Support added for grouped freqency table of routing time steps.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -106,6 +112,9 @@ int  stats_open()
 //
 {
     int j, k;
+    double timeStepDelta;                                                      //(5.1.015)
+    double logMaxTimeStep;                                                     //(5.1.015)
+    double logMinTimeStep;                                                     //(5.1.015)
 
     // --- set all pointers to NULL
     NodeStats = NULL;
@@ -147,6 +156,8 @@ int  stats_open()
             Subcatch[j].groundwater->stats.deepFlow = 0.0;
             Subcatch[j].groundwater->stats.evap = 0.0;
             Subcatch[j].groundwater->stats.maxFlow = 0.0;
+            Subcatch[j].groundwater->stats.finalUpperMoist = 0.0;              //(5.1.015)
+            Subcatch[j].groundwater->stats.finalWaterTable = 0.0;              //
         }
     }
 
@@ -286,6 +297,20 @@ int  stats_open()
     SysStats.avgTimeStep = 0.0;
     SysStats.avgStepCount = 0.0;
     SysStats.steadyStateCount = 0.0;
+
+    // --- divide range between min and max routing time steps into            //(5.1.015)
+    //     equal intervals using a logarithmic scale                           //
+    logMaxTimeStep = log10(RouteStep);                                         //
+    logMinTimeStep = log10(MinRouteStep);                                      //
+    timeStepDelta = (logMaxTimeStep - logMinTimeStep) / (TIMELEVELS-1);        //
+    SysStats.timeStepIntervals[0] = RouteStep;                                 //
+    for (j = 1; j < TIMELEVELS; j++)                                           //
+    {                                                                          //
+        SysStats.timeStepIntervals[j] =                                        //
+            pow(10., logMaxTimeStep - j * timeStepDelta);                      //
+        SysStats.timeStepCounts[j] = 0;                                        //
+    }                                                                          //
+    SysStats.timeStepIntervals[TIMELEVELS - 1] = MinRouteStep;                 //
     return 0;
 }
 
@@ -446,6 +471,7 @@ void   stats_updateFlowStats(double tStep, DateTime aDate, int stepCount,
 }
 
     // --- update count of times in steady state
+    ReportStepCount++;
     SysStats.steadyStateCount += steadyState;
 
     // --- update time step stats if not in steady state
@@ -455,6 +481,15 @@ void   stats_updateFlowStats(double tStep, DateTime aDate, int stepCount,
         if ( OldRoutingTime > 0 )
         {
             SysStats.minTimeStep = MIN(SysStats.minTimeStep, tStep);
+
+            // --- locate interval that logged time step falls in              //(5.1.015)
+            //     and update its count                                        //
+            for (j = 1; j < TIMELEVELS; j++)                                   //
+                if (tStep >= SysStats.timeStepIntervals[j])                    //
+                {                                                              //
+                    SysStats.timeStepCounts[j]++;                              //
+                    break;                                                     //
+                }                                                              //
         }
         SysStats.avgTimeStep += tStep;
         SysStats.maxTimeStep = MAX(SysStats.maxTimeStep, tStep);
@@ -703,6 +738,7 @@ void  stats_findMaxStats()
 {
     int    j;
     double x;
+    double stepCount = ReportStepCount - SysStats.steadyStateCount;            //(5.1.015)
 
     // --- initialize max. stats arrays
     for (j=0; j<MAX_STATS; j++)
@@ -717,11 +753,11 @@ void  stats_findMaxStats()
     }
 
     // --- find links with most flow turns
-    if ( StepCount > 2 )
+    if ( stepCount > 2 )                                                       //(5.1.015)
     {
         for (j=0; j<Nobjects[LINK]; j++)
         {
-            x = 100.0 * LinkStats[j].flowTurns / (2./3.*(StepCount-2));
+            x = 100.0 * LinkStats[j].flowTurns / (2./3.*(stepCount-2));        //(5.1.015)
             stats_updateMaxStats(MaxFlowTurns, LINK, j, x);
         }
     }
@@ -747,17 +783,17 @@ void  stats_findMaxStats()
     if ( RouteModel != DW || CourantFactor == 0.0 ) return;
 
     // --- find nodes most frequently Courant critical
-    if ( StepCount == 0 ) return;
+    if ( stepCount == 0 ) return;                                              //(5.1.015)
     for (j=0; j<Nobjects[NODE]; j++)
     {
-        x = NodeStats[j].timeCourantCritical / StepCount;
+        x = NodeStats[j].timeCourantCritical / stepCount;                      //(5.1.015)
         stats_updateMaxStats(MaxCourantCrit, NODE, j, 100.0*x);
     }
 
     // --- find links most frequently Courant critical
     for (j=0; j<Nobjects[LINK]; j++)
     {
-        x = LinkStats[j].timeCourantCritical / StepCount;
+        x = LinkStats[j].timeCourantCritical / stepCount;                      //(5.1.015)
         stats_updateMaxStats(MaxCourantCrit, LINK, j, 100.0*x);
     }
 }
@@ -802,9 +838,9 @@ int stats_getNodeStat(int index, TNodeStats **nodeStats)
     // Perform memcopy
     memcpy(*nodeStats, &NodeStats[index], sizeof(TNodeStats));
 
-    // Convert units 
+    // Convert units
     // Current Average Depth
-    (*nodeStats)->avgDepth *= (UCF(LENGTH) / (double)StepCount);
+    (*nodeStats)->avgDepth *= (UCF(LENGTH) / (double)ReportStepCount);
     // Current Maximum Depth
     (*nodeStats)->maxDepth *= UCF(LENGTH);
     // Current Maximum Lateral Inflow
@@ -841,12 +877,12 @@ int stats_getStorageStat(int index, TStorageStats **storageStats)
     int k = Node[index].subIndex;
     // Copy Structure
     memcpy(*storageStats, &StorageStats[k], sizeof(TStorageStats));
-    
+
     // Convert units
     // Initial Volume
     (*storageStats)->initVol *= UCF(VOLUME);
     // Current Average Volume
-    (*storageStats)->avgVol *= (UCF(VOLUME) / (double)StepCount);
+    (*storageStats)->avgVol *= (UCF(VOLUME) / (double)ReportStepCount);
     // Current Maximum Volume
     (*storageStats)->maxVol *= UCF(VOLUME);
     // Current Maximum Flow
@@ -872,7 +908,7 @@ int stats_getOutfallStat(int index, TOutfallStats **outfallStats)
 
     // fetch sub index
     k = Node[index].subIndex;
-    
+
     // Copy Structure
     temp = (*outfallStats)->totalLoad;
     memcpy(*outfallStats, &(OutfallStats[k]), sizeof(TOutfallStats));
@@ -937,7 +973,7 @@ int stats_getLinkStat(int index, TLinkStats **linkStats)
     (*linkStats)->timeCapacityLimited /= 3600.0;
     // Cumulative Time Courant Critical Flow
     (*linkStats)->timeCourantCritical /= 3600.0;
-    
+
 	return 0;
 }
 
diff --git a/src/solver/statsrpt.c b/src/solver/statsrpt.c
index 4c5d90b76..b08851abf 100644
--- a/src/solver/statsrpt.c
+++ b/src/solver/statsrpt.c
@@ -9,6 +9,7 @@
 //             04/30/15 (Build 5.1.009)
 //             08/01/16 (Build 5.1.011)
 //             05/10/18 (Build 5.1.013)
+//             04/01/20 (Build 5.1.015)
 //   Author:   L. Rossman
 //
 //   Report writing functions for summary statistics.
@@ -26,6 +27,9 @@
 //
 //   Build 5.1.013:
 //   - Pervious and impervious runoff added to Subcatchment Runoff Summary.
+//
+//   Build 5.1.015:
+//   - Fixes bug in summary statistics when Report Start date > Start Date.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -337,7 +341,7 @@ void writeNodeDepths()
         fprintf(Frpt.file, " %-9s ", NodeTypeWords[Node[j].type]);
         getElapsedTime(NodeStats[j].maxDepthDate, &days, &hrs, &mins);
         fprintf(Frpt.file, "%7.2f  %7.2f  %7.2f  %4d  %02d:%02d  %10.2f",
-            NodeStats[j].avgDepth / StepCount * UCF(LENGTH),
+            NodeStats[j].avgDepth / ReportStepCount * UCF(LENGTH),             //(5.1.015)
             NodeStats[j].maxDepth * UCF(LENGTH),
             (NodeStats[j].maxDepth + Node[j].invertElev) * UCF(LENGTH),
             days, hrs, mins, NodeStats[j].maxRptDepth);
@@ -538,7 +542,7 @@ void writeStorageVolumes()
             if ( Node[j].type != STORAGE ) continue;
             k = Node[j].subIndex;
             fprintf(Frpt.file, "\n  %-20s", Node[j].ID);
-            avgVol = StorageStats[k].avgVol / StepCount;
+            avgVol = StorageStats[k].avgVol / (double)ReportStepCount;         //(5.1.015)
             maxVol = StorageStats[k].maxVol;
             pctMaxVol = 0.0;
             pctAvgVol = 0.0;
@@ -634,7 +638,7 @@ void writeOutfallLoads()
 
             // --- print node ID, flow freq., avg. flow, max. flow & flow vol.
             fprintf(Frpt.file, "\n  %-20s", Node[j].ID);
-            x = 100.*flowCount/(double)StepCount;
+            x = 100.*flowCount/(double)ReportStepCount;                        //(5.1.015)
             fprintf(Frpt.file, "%7.2f", x);
             freqSum += x;
             if ( flowCount > 0 )
@@ -804,7 +808,7 @@ void writeFlowClass()
         for ( i=0; i<MAX_FLOW_CLASSES; i++ )
         {
             fprintf(Frpt.file, "  %4.2f",
-                LinkStats[j].timeInFlowClass[i] /= StepCount);
+                LinkStats[j].timeInFlowClass[i] /= (double)ReportStepCount);   //(5.1.015)
         }
         fprintf(Frpt.file, "  %4.2f", LinkStats[j].timeNormalFlow /
                                       (NewRoutingTime/1000.0));
diff --git a/src/solver/subcatch.c b/src/solver/subcatch.c
index d363f63f1..659d428fc 100644
--- a/src/solver/subcatch.c
+++ b/src/solver/subcatch.c
@@ -11,6 +11,7 @@
 //             08/01/16  (Build 5.1.011)
 //             03/14/17  (Build 5.1.012)
 //             05/10/18  (Build 5.1.013)
+//             05/18/20  (Build 5.1.015)
 //   Author:   L. Rossman
 //
 //   Subcatchment runoff functions.
@@ -45,6 +46,9 @@
 //   - Support added for monthly adjustment of subcatchment's depression
 //     storage, pervious N, and infiltration.
 //
+//   Build 5.1.015: 
+//   - Support added for multiple infiltration methods within a project.
+//   - Only pervious area depression storage receives monthly adjustment.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -447,7 +451,7 @@ void  subcatch_initState(int j)
     Subcatch[j].infilLoss = 0.0;
 
     // --- initialize state of infiltration, groundwater, & snow pack objects
-    if ( Subcatch[j].infil == j )  infil_initState(j, InfilModel);
+    if ( Subcatch[j].infil == j )  infil_initState(j);                         //(5.1.015)
     if ( Subcatch[j].groundwater ) gwater_initState(j);
     if ( Subcatch[j].snowpack )    snow_initSnowpack(j);
 
@@ -1027,7 +1031,7 @@ double getSubareaInfil(int j, TSubarea* subarea, double precip, double tStep)
     double infil = 0.0;                     // actual infiltration rate (ft/sec)
 
     // --- compute infiltration rate 
-    infil = infil_getInfil(j, InfilModel, tStep, precip,
+    infil = infil_getInfil(j, tStep, precip,                                   //(5.1.015)
                            subarea->inflow, subarea->depth);
 
     // --- limit infiltration rate by available void space in unsaturated
@@ -1161,7 +1165,7 @@ void adjustSubareaParams(int i, int j)
 //  Input:   i = type of subarea being analyzed
 //           j = index of current subcatchment being analyzed
 //  Output   adjusted values of module-level variables Dstore & Alpha
-//  Purpose: adjusts a subarea's depression storage and its pervious
+//  Purpose: adjusts a pervious subarea's depression storage and its           //(5.1.015)
 //           runoff coeff. by month of the year.
 //
 {
@@ -1169,22 +1173,25 @@ void adjustSubareaParams(int i, int j)
     int m;              // current month of the year
     double f;           // adjustment factor
 
-     // --- depression storage adjustment
-     p = Subcatch[j].dStorePattern;
-     if (p >= 0 && Pattern[p].type == MONTHLY_PATTERN)
-     {
-         m = datetime_monthOfYear(getDateTime(OldRunoffTime)) - 1;
-         f = Pattern[p].factor[m];
-         if (f >= 0.0) Dstore *= f;
-     }
-
-    // --- pervious area roughness
-    p = Subcatch[j].nPervPattern;
-    if (i == PERV && p >= 0 && Pattern[p].type == MONTHLY_PATTERN)
+    if (i == PERV)                                                             //(5.1.015)
     {
-         m = datetime_monthOfYear(getDateTime(OldRunoffTime)) - 1;
-         f = Pattern[p].factor[m];
-         if (f <= 0.0) Alpha = 0.0;
-         else          Alpha /= f;
-     }
+        // --- depression storage adjustment
+        p = Subcatch[j].dStorePattern;
+        if (p >= 0 && Pattern[p].type == MONTHLY_PATTERN)
+        {
+            m = datetime_monthOfYear(getDateTime(OldRunoffTime)) - 1;
+            f = Pattern[p].factor[m];
+            if (f >= 0.0) Dstore *= f;
+        }
+
+        // --- roughness adjustment to runoff coeff.                           //(5.1.015)
+        p = Subcatch[j].nPervPattern;
+        if (p >= 0 && Pattern[p].type == MONTHLY_PATTERN)                      //(5.1.015)
+        {
+            m = datetime_monthOfYear(getDateTime(OldRunoffTime)) - 1;
+            f = Pattern[p].factor[m];
+            if (f <= 0.0) Alpha = 0.0;
+            else          Alpha /= f;
+        }
+    }
 }
diff --git a/src/solver/swmm5.c b/src/solver/swmm5.c
index 517436d62..9051da5fc 100644
--- a/src/solver/swmm5.c
+++ b/src/solver/swmm5.c
@@ -8,6 +8,7 @@
 //             08/01/16  (Build 5.1.011)
 //             03/14/17  (Build 5.1.012)
 //             05/10/18  (Build 5.1.013)
+//             04/01/20  (Build 5.1.015)
 //   Author:   L. Rossman
 //
 //   This is the main module of the computational engine for Version 5 of
@@ -39,6 +40,8 @@
 //   - Support added for saving average results within a reporting period.
 //   - SWMM engine now always compiled to a shared object library.
 //
+//   Build 5.1.015:
+//   - Fixes bug in summary statistics when Report Start date > Start Date.
 //-----------------------------------------------------------------------------
 #define _CRT_SECURE_NO_DEPRECATE
 
@@ -287,7 +290,8 @@ int DLLEXPORT swmm_start(int saveResults)
         NewRunoffTime = 0.0;
         NewRoutingTime = 0.0;
         ReportTime =   (double)(1000 * ReportStep);
-        StepCount = 0;
+        TotalStepCount = 0;                                                    //(5.1.015)
+        ReportStepCount = 0;                                                   //(5.1.015)
         NonConvergeCount = 0;
         IsStartedFlag = TRUE;
 
@@ -424,7 +428,7 @@ int DLLEXPORT swmm_step(double* elapsedTime)
 
 #ifdef EXH
     // --- end of try loop; handle exception here
-    __except(xfilter(GetExceptionCode(), "swmm_step", ElapsedTime, StepCount))
+    __except(xfilter(GetExceptionCode(), "swmm_step", ElapsedTime, TotalStepCount)) //(5.1.015)
     {
         ErrorCode = ERR_SYSTEM;
     }
@@ -450,7 +454,7 @@ void execRouting()
 #endif
     {
         // --- determine when next routing time occurs
-        StepCount++;
+        TotalStepCount++;                                                      //(5.1.015)
         if ( !DoRouting ) routingStep = MIN(WetStep, ReportStep);
         else routingStep = routing_getRoutingStep(RouteModel, RouteStep);
         if ( routingStep <= 0.0 )
@@ -464,7 +468,7 @@ void execRouting()
         if ( nextRoutingTime > TotalDuration )
         {
             routingStep = (TotalDuration - NewRoutingTime) / 1000.0;
-            routingStep = MAX(routingStep, 1./1000.0);
+            routingStep = MAX(routingStep, 1. / 1000.0);
             nextRoutingTime = TotalDuration;
         }
 
@@ -488,7 +492,7 @@ void execRouting()
 #ifdef EXH
     // --- end of try loop; handle exception here
     __except(xfilter(GetExceptionCode(), "execRouting",
-                     ElapsedTime, StepCount))
+                     ElapsedTime, TotalStepCount))                             //(5.1.015)
     {
         ErrorCode = ERR_SYSTEM;
         return;
@@ -799,7 +803,7 @@ void  writecon(char *s)
 //  Purpose: writes string of characters to the console.
 //
 {
-    fprintf(stdout,"%s", s);
+    fprintf(stdout,"%s",s);
     fflush(stdout);
 }
 
diff --git a/src/solver/text.h b/src/solver/text.h
index f8224a77c..204f6d518 100644
--- a/src/solver/text.h
+++ b/src/solver/text.h
@@ -15,23 +15,28 @@
 //            08/01/16  (Build 5.1.011)
 //            03/14/17  (Build 5.1.012)
 //            05/10/18  (Build 5.1.013)
+//            03/01/20  (Build 5.1.014)
+//            04/01/20  (Build 5.1.015)
+//
 //   Author:  L. Rossman
 //
 //   Text strings
 //-----------------------------------------------------------------------------
 
-//#include "consts.h"
+#ifndef TEXT_H
+#define TEXT_H
+
 
-//#define FMT01 \
-// "\tswmm5 <input file> <report file> <output file>\n"
+#define FMT01 \
+ "\tswmm5 <input file> <report file> <output file>\n"
 
 #define FMT03 " There are errors.\n"
 #define FMT04 " There are warnings.\n"
 #define FMT05 "\n"
 #define FMT06 "\n o  Retrieving project data"
 #define FMT07 "\n o  Writing output report"
-//#define FMT08 \
-//  "\n  EPA STORM WATER MANAGEMENT MODEL - VERSION 5.1 (Build "SEMVERSION_MAJOR"."SEMVERSION_MINOR"."SEMVERSION_PATCH")"         //(5.1.014)
+#define FMT08 \
+  "\n  EPA STORM WATER MANAGEMENT MODEL - VERSION 5.1 (Build 5.1.015)"         //(5.1.015)
 #define FMT09 \
   "\n  --------------------------------------------------------------"
 #define FMT10 "\n"
@@ -445,3 +450,6 @@
 #define  ws_GWF              "[GWF"
 #define  ws_ADJUST           "[ADJUSTMENT"
 #define  ws_EVENT            "[EVENT"
+
+
+#endif //TEXT_H


Test patch:
diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index 41e7c5020..f48647079 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -24,7 +24,7 @@ jobs:
         requirements: [requirements-swmm.txt]
         include:
           - os: windows-2022
-            sys_pkgs: choco install boost-msvc-14.1
+            sys_pkgs: choco install boost-msvc-14.3
             build_unit_test: make.cmd /g "Visual Studio 17 2022" /A "x64" /t
             build_reg_test: make.cmd /g "Visual Studio 17 2022" /A "x64"
             before_reg_test: before-nrtest.cmd
@@ -83,17 +83,17 @@ jobs:
 
     steps:
       - name: Checkout repo
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Clone ci-tools repo
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
         with:
           repository: OpenWaterAnalytics/ci-tools
           ref: master
           path: ci-tools
 
       - name: Setup python
-        uses: actions/setup-python@v2
+        uses: actions/setup-python@v4
         with:
           python-version: "3.7"
 
@@ -114,7 +114,7 @@ jobs:
       - name: Before reg test
         env:
           NRTESTS_URL: https://github.com/OpenWaterAnalytics/swmm-nrtestsuite
-          BENCHMARK_TAG: v2.0.1
+          BENCHMARK_TAG: v2.0.2
         run: ./${{ matrix.before_reg_test }} ${{ env.BENCHMARK_TAG }}
 
       - name: Run reg test
@@ -122,7 +122,7 @@ jobs:
 
       - name: Upload artifacts
         if: ${{ always() }}
-        uses: actions/upload-artifact@v2
+        uses: actions/upload-artifact@v3
         with:
           name: build-test-artifacts
           path: upload/*.*
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 17f17d546..ebb0f397b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -9,7 +9,6 @@
 #
 
 
-
 #Prep ourselves for compiling with boost
 include(../extern/boost.cmake)