diff --git a/.travis.yml b/.travis.yml
index 3049e05..7dfa9f9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,6 +9,7 @@ env:
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y libboost-test-dev
+ - sudo apt-get install -y libboost-thread-dev
- sudo apt-get install -y swig
#install:
diff --git a/README.md b/README.md
index 4020cb0..2c84538 100755
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ EPANET {#epanet-readme}
## Build Status
[](https://ci.appveyor.com/project/OpenWaterAnalytics/epanet/branch/dev)
-[](https://travis-ci.org/OpenWaterAnalytics/EPANET)
+[](https://travis-ci.org/OpenWaterAnalytics/EPANET)
## For EPANET-related questions and discussion
For community discussion, FAQ, and roadmapping of the project, go to the [Community Forum](http://community.wateranalytics.org/category/epanet).
diff --git a/ReleaseNotes2_2.md b/ReleaseNotes2_2.md
index d09fbf8..65f1574 100644
--- a/ReleaseNotes2_2.md
+++ b/ReleaseNotes2_2.md
@@ -1,6 +1,4 @@
-
-
Release Notes for EPANET 2.2 (Draft)
============================
@@ -49,15 +47,18 @@ These new parameters augment the current `EN_ACCURACY` option which always remai
## Improved Linear Solver Routine
EPANET's hydraulic solver requires solving a system of linear equations over a series of iterations until a set of convergence criteria are met. The coefficient matrix of this linear system is square and symmetric. It has a row for each network node and a non-zero off-diagonal coefficient for each link. The numerical effort needed to solve the linear system can be reduced if the nodes are re-ordered so that the non-zero coefficients cluster more tightly around the diagonal.
-EPANET's original node re-ordering scheme has been replaced by the more powerful **Multiple Minimum Degree (MMD)** algorithm. On a series of eight networks ranging in size from 7,700 to 100,000 nodes **MMD** reduced the solution time for a single period (steady state) hydraulic analysis by an average of more than 50%.
+EPANET's original node re-ordering scheme has been replaced by the more powerful **Multiple Minimum Degree (MMD)** algorithm. On a series of eight networks ranging in size from 7,700 to 100,000 nodes **MMD** reduced the solution time for a single period (steady state) hydraulic analysis by an average of 58%.
## Pressure Dependent Demands
EPANET has always employed a Demand Driven Analysis (**DDA**) when modeling network hydraulics. Under this approach nodal demands at a given point in time are fixed values that must be delivered no matter what nodal heads and link flows are produced by a hydraulic solution. This can result in situations where required demands are satisfied at nodes that have negative pressures - a physical impossibility.
To address this issue EPANET has been extended to use a Pressure Driven Analysis (**PDA**) if so desired. Under **PDA**, the demand *D* delivered at a node depends on the node's available pressure *P* according to:
-$$D =D_f\left(\frac{P-P_{min}}{P_{req}-P_{min}}\right)^{P_{exp}} for P_{0}<=P<=P_{req}$$where *Df* is the full demand required, *Pmin* is the pressure below which demand is zero, *Preq* is the pressure required to deliver the full required demand and *Pexp* is an exponent. When *P < Pmin* demand is 0 and when *P > Preq* demand equals *Df*.
-To implement pressure dependent analysis four new parameters have been added to the [OPTIONS] section of the EPANET input file:
+*D = Df [ (P - Pmin) / (Preq - Pmin) ]Pexp*
+
+where *Df* is the full demand required, *Pmin* is the pressure below which demand is zero, *Preq* is the pressure required to deliver the full required demand and *Pexp* is an exponent. When *P < Pmin* demand is 0 and when *P > Preq* demand equals *Df*.
+
+To implement pressure driven analysis four new parameters have been added to the [OPTIONS] section of the EPANET input file:
| Parameter | Description | Default |
@@ -81,7 +82,8 @@ for the thread-safe API. Some additional points regarding the new **PDA** option
- If no DEMAND MODEL and its parameters are specified then the analysis defaults to being demand driven (**DDA**).
- This implementation of **PDA** assumes that the same parameters apply to all nodes in the network. Extending the framework to allow different parameters for specific nodes is straightforward to do but is left as a future feature to implement.
- - *P0* is allowed to equal to *Preq*. This condition can be used to find a solution that results in the smallest amount of demand reductions needed to insure that no node delivers positive demand at a pressure below *Pmin*.
+ - *Pmin* is allowed to equal to *Preq*. This condition can be used to find a solution that results in the smallest amount of demand reductions needed to insure that no node delivers positive demand at a pressure below *Pmin*.
+
## Code Changes
diff --git a/include/epanet2.h b/include/epanet2.h
index 3951cd7..e619792 100644
--- a/include/epanet2.h
+++ b/include/epanet2.h
@@ -1170,9 +1170,9 @@ extern "C" {
***************************************************/
int DLLEXPORT EN_createproject(EN_ProjectHandle *ph);
int DLLEXPORT EN_deleteproject(EN_ProjectHandle *ph);
-
- int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *inpFile,
- const char *rptFile, const char *binOutFile, void (*callback) (char *));
+
+ int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *f1,
+ const char *f2, const char *f3, void (*pviewprog)(char *));
void DLLEXPORT EN_clearError(EN_ProjectHandle ph);
int DLLEXPORT EN_checkError(EN_ProjectHandle ph, char** msg_buffer);
diff --git a/src/epanet.c b/src/epanet.c
index ca3495a..12dba99 100644
--- a/src/epanet.c
+++ b/src/epanet.c
@@ -613,6 +613,27 @@ int DLLEXPORT EN_deleteproject(EN_ProjectHandle *ph)
return 0;
}
+int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *f1, const char *f2,
+ const char *f3, void (*pviewprog)(char *))
+{
+ int errcode = 0;
+ EN_Project *p = NULL;
+
+ ERRCODE(EN_open(ph, f1, f2, f3));
+ p = (EN_Project*)(ph);
+ p->viewprog = pviewprog;
+
+ if (p->out_files.Hydflag != USE) {
+ ERRCODE(EN_solveH(ph));
+ }
+
+ ERRCODE(EN_solveQ(ph));
+ ERRCODE(EN_report(ph));
+
+ EN_close(ph);
+
+ return errcode;
+}
int DLLEXPORT EN_init(EN_ProjectHandle ph, char *f2, char *f3,
EN_FlowUnits UnitsType, EN_FormType HeadlossFormula)
diff --git a/src/mempool.c b/src/mempool.c
index ef7e645..f77d8ec 100755
--- a/src/mempool.c
+++ b/src/mempool.c
@@ -1,17 +1,9 @@
/* mempool.c
**
-** A simple fast memory allocation package.
+** A simple fast pooled memory allocation package.
**
-** By Steve Hill in Graphics Gems III, David Kirk (ed.),
-** Academic Press, Boston, MA, 1992
-**
-** Modified by Lew Rossman, 8/13/94.
-**
-** AllocInit() - create an alloc pool, returns the old pool handle
-** Alloc() - allocate memory
-** AllocReset() - reset the current pool
-** AllocSetPool() - set the current pool
-** AllocFree() - free the memory used by the current pool.
+** Based on code by Steve Hill in Graphics Gems III,
+** David Kirk (ed.), Academic Press, Boston, MA, 1992
**
*/
@@ -28,95 +20,80 @@
#define ALLOC_BLOCK_SIZE 64000 /*(62*1024)*/
-/*
-** alloc_hdr_t - Header for each block of memory.
-*/
-
-typedef struct alloc_hdr_s
+struct MemBlock
{
- struct alloc_hdr_s *next; /* Next Block */
- char *block, /* Start of block */
- *free, /* Next free in block */
- *end; /* block + block size */
-} alloc_hdr_t;
+ struct MemBlock *next; // Next block
+ char *block, // Start of block
+ *free, // Next free position in block
+ *end; // block + block size
+};
-/*
-** alloc_root_t - Header for the whole pool.
-*/
-
-typedef struct alloc_root_s
+struct Mempool
{
- alloc_hdr_t *first, /* First header in pool */
- *current; /* Current header */
-} alloc_root_t;
+ struct MemBlock *first;
+ struct MemBlock *current;
+};
-/*
-** root - Pointer to the current pool.
-*/
-
-static alloc_root_t *root;
-
-
-/*
-** AllocHdr()
-**
-** Private routine to allocate a header and memory block.
-*/
-
-static alloc_hdr_t *AllocHdr(void);
-
-static alloc_hdr_t * AllocHdr()
+static struct MemBlock* createMemBlock()
{
- alloc_hdr_t *hdr;
- char *block;
-
- block = (char *) malloc(ALLOC_BLOCK_SIZE);
- hdr = (alloc_hdr_t *) malloc(sizeof(alloc_hdr_t));
-
- if (hdr == NULL || block == NULL) return(NULL);
- hdr->block = block;
- hdr->free = block;
- hdr->next = NULL;
- hdr->end = block + ALLOC_BLOCK_SIZE;
-
- return(hdr);
+ struct MemBlock* memBlock = malloc(sizeof(struct MemBlock));
+ if (memBlock)
+ {
+ memBlock->block = malloc(ALLOC_BLOCK_SIZE * sizeof(char));
+ if (memBlock->block == NULL)
+ {
+ free(memBlock);
+ return NULL;
+ }
+ memBlock->free = memBlock->block;
+ memBlock->next = NULL;
+ memBlock->end = memBlock->block + ALLOC_BLOCK_SIZE;
+ }
+ return memBlock;
}
-/*
-** AllocInit()
-**
-** Create a new memory pool with one block.
-** Returns pointer to the new pool.
-*/
-
-DLLEXPORT alloc_handle_t * AllocInit()
+static void deleteMemBlock(struct MemBlock* memBlock)
{
- alloc_handle_t *newpool;
- root = (alloc_root_t *) malloc(sizeof(alloc_root_t));
- if (root == NULL) {
- return(NULL);
- }
- if ( (root->first = AllocHdr()) == NULL) {
- return(NULL);
- }
- root->current = root->first;
- newpool = (alloc_handle_t *) root;
- return(newpool);
+ free(memBlock->block);
+ free(memBlock);
}
-/*
-** Alloc()
-**
-** Use as a direct replacement for malloc(). Allocates
-** memory from the current pool.
-*/
-
-DLLEXPORT char *Alloc(long size)
+struct Mempool * mempool_create()
{
- alloc_hdr_t *hdr = root->current;
- char *ptr;
+ struct Mempool *mempool;
+ mempool = (struct Mempool *)malloc(sizeof(struct Mempool));
+ if (mempool == NULL) return NULL;
+ mempool->first = createMemBlock();
+ mempool->current = mempool->first;
+ if (mempool->first == NULL) return NULL;
+ return mempool;
+}
+
+void mempool_delete(struct Mempool *mempool)
+{
+ if (mempool == NULL) return;
+ while (mempool->first)
+ {
+ mempool->current = mempool->first->next;
+ deleteMemBlock(mempool->first);
+ mempool->first = mempool->current;
+ }
+ free(mempool);
+ mempool = NULL;
+}
+
+void mempool_reset(struct Mempool *mempool)
+{
+ mempool->current = mempool->first;
+ mempool->current->free = mempool->current->block;
+}
+
+
+char * mempool_alloc(struct Mempool *mempool, size_t size)
+{
+ char* ptr;
/*
** Align to 4 byte boundary - should be ok for most machines.
@@ -124,86 +101,37 @@ DLLEXPORT char *Alloc(long size)
*/
size = (size + 3) & 0xfffffffc;
- ptr = hdr->free;
- hdr->free += size;
+ if (!mempool->current) return NULL;
+ ptr = mempool->current->free;
+ mempool->current->free += size;
- /* Check if the current block is exhausted. */
+ // Check if the current block is exhausted
- if (hdr->free >= hdr->end)
+ if (mempool->current->free >= mempool->current->end)
{
- /* Is the next block already allocated? */
+ // Is the next block already allocated?
- if (hdr->next != NULL)
+ if (mempool->current->next)
{
- /* re-use block */
- hdr->next->free = hdr->next->block;
- root->current = hdr->next;
+ // re-use block
+ mempool->current->next->free = mempool->current->next->block;
+ mempool->current = mempool->current->next;
}
else
{
- /* extend the pool with a new block */
- if ( (hdr->next = AllocHdr()) == NULL) return(NULL);
- root->current = hdr->next;
+ // extend the pool with a new block
+ mempool->current->next = createMemBlock();
+ if (!mempool->current->next) return NULL;
+ mempool->current = mempool->current->next;
}
- /* set ptr to the first location in the next block */
- ptr = root->current->free;
- root->current->free += size;
+ // set ptr to the first location in the next block
+
+ ptr = mempool->current->free;
+ mempool->current->free += size;
}
- /* Return pointer to allocated memory. */
+ // Return pointer to allocated memory
- return(ptr);
-}
-
-
-/*
-** AllocSetPool()
-**
-** Change the current pool. Return the old pool.
-*/
-
-DLLEXPORT alloc_handle_t * AllocSetPool(alloc_handle_t *newpool)
-{
- alloc_handle_t *old = (alloc_handle_t *) root;
- root = (alloc_root_t *) newpool;
- return(old);
-}
-
-
-/*
-** AllocReset()
-**
-** Reset the current pool for re-use. No memory is freed,
-** so this is very fast.
-*/
-
-DLLEXPORT void AllocReset()
-{
- root->current = root->first;
- root->current->free = root->current->block;
-}
-
-
-/*
-** AllocFreePool()
-**
-** Free the memory used by the current pool.
-** Don't use where AllocReset() could be used.
-*/
-
-DLLEXPORT void AllocFreePool()
-{
- alloc_hdr_t *tmp,
- *hdr = root->first;
-
- while (hdr != NULL)
- {
- tmp = hdr->next;
- free((char *) hdr->block);
- free((char *) hdr);
- hdr = tmp;
- }
- free((char *) root);
- root = NULL;
+ return ptr;
}
diff --git a/src/mempool.h b/src/mempool.h
index 4fe3c0f..002272f 100755
--- a/src/mempool.h
+++ b/src/mempool.h
@@ -3,41 +3,17 @@
**
** Header for mempool.c
**
-** The type alloc_handle_t provides an opaque reference to the
-** alloc pool - only the alloc routines know its structure.
+** A simple pooled memory allocator
*/
#ifndef MEMPOOL_H
#define MEMPOOL_H
-#ifndef DLLEXPORT
- #ifdef DLL
- #ifdef __cplusplus
- #define DLLEXPORT extern "C" __declspec(dllexport)
- #else
- #define DLLEXPORT __declspec(dllexport) __stdcall
- #endif
- #elif defined(CYGWIN)
- #define DLLEXPORT __stdcall
- #else
- #ifdef __cplusplus
- #define DLLEXPORT
- #else
- #define DLLEXPORT
- #endif
- #endif
-#endif
+struct Mempool;
-
-typedef struct
-{
- long dummy;
-} alloc_handle_t;
-
-alloc_handle_t *AllocInit(void);
-char *Alloc(long);
-alloc_handle_t *AllocSetPool(alloc_handle_t *);
-void AllocReset(void);
-void AllocFreePool(void);
+struct Mempool * mempool_create();
+void mempool_delete(struct Mempool *mempool);
+void mempool_reset(struct Mempool *mempool);
+char * mempool_alloc(struct Mempool *mempool, size_t size);
#endif
\ No newline at end of file
diff --git a/src/quality.c b/src/quality.c
index a7286db..1a70beb 100644
--- a/src/quality.c
+++ b/src/quality.c
@@ -29,9 +29,9 @@ AUTHOR: L. Rossman
closequal() -- called from ENcloseQ() in EPANET.C
Calls are made to:
- AllocInit()
- Alloc()
- AllocFree()
+ mempool_create()
+ mempool_alloc()
+ mempool_delete()
in MEMPOOL.C to utilize a memory pool to prevent excessive malloc'ing
when constantly creating and destroying pipe sub-segments during
the water quality transport calculations.
@@ -89,7 +89,7 @@ int openqual(EN_Project *pr)
/* Allocate memory pool for WQ segments */
qu->OutOfMemory = FALSE;
- qu->SegPool = AllocInit();
+ qu->SegPool = mempool_create();
if (qu->SegPool == NULL) {
errcode = 101;
}
@@ -193,8 +193,7 @@ void initqual(EN_Project *pr)
/* Reset memory pool */
qu->FreeSeg = NULL;
- AllocSetPool(qu->SegPool);
- AllocReset();
+ mempool_reset(qu->SegPool);
}
/* Initialize avg. reaction rates */
@@ -438,8 +437,7 @@ int closequal(EN_Project *pr)
/* Free memory pool */
if (qu->SegPool)
{
- AllocSetPool(qu->SegPool);
- AllocFreePool();
+ mempool_delete(qu->SegPool);
}
free(qu->FirstSeg);
@@ -573,7 +571,6 @@ void transport(EN_Project *pr, long tstep)
/* Repeat until elapsed time equals hydraulic time step */
- AllocSetPool(qu->SegPool);
qtime = 0;
while (!qu->OutOfMemory && qtime < tstep) { /* Qstep is quality time step */
dt = MIN(qu->Qstep, tstep - qtime); /* Current time step */
@@ -793,7 +790,7 @@ void addseg(EN_Project *pr, int k, double v, double c)
seg = qu->FreeSeg;
qu->FreeSeg = seg->prev;
} else {
- seg = (struct Sseg *)Alloc(sizeof(struct Sseg));
+ seg = (struct Sseg *) mempool_alloc(qu->SegPool, sizeof(struct Sseg));
if (seg == NULL) {
qu->OutOfMemory = TRUE;
return;
diff --git a/src/types.h b/src/types.h
index d887c96..0a2d26c 100755
--- a/src/types.h
+++ b/src/types.h
@@ -22,7 +22,6 @@ AUTHOR: L. Rossman
#include "epanet2.h"
#include "hash.h"
-#include "mempool.h"
#include "util/errormanager.h"
@@ -538,6 +537,8 @@ typedef struct s_ActItem /* Action list item */
} ActItem;
+// Forward declaration of the Mempool structure defined in mempool.h
+struct Mempool;
typedef struct {
char
@@ -578,7 +579,7 @@ typedef struct {
Qtime; /// Current quality time (sec)
char OutOfMemory; /* Out of memory indicator */
- alloc_handle_t *SegPool; // Memory pool for water quality segments
+ struct Mempool *SegPool; // Memory pool for water quality segments
Pseg FreeSeg; /* Pointer to unused segment */
Pseg *FirstSeg, /* First (downstream) segment in each pipe */
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index be6dc5a..38e5328 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -16,9 +16,18 @@ enable_testing()
# Sets for output directory for executables and libraries.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+if(UNIX)
+ set(CMAKE_CXX_FLAGS "-std=c++11")
+endif(UNIX)
#Prep ourselves for compiling boost
-find_package(Boost REQUIRED)
+if(MSVC)
+ set(Boost_DEBUG OFF)
+ set(Boost_DETAILED_FAILURE_MSG OFF)
+ set(Boost_USE_STATIC_LIBS ON)
+endif(MSVC)
+set(Boost_THREAD_FOUND OFF)
+find_package(Boost COMPONENTS thread)
include_directories (${Boost_INCLUDE_DIRS})
diff --git a/tests/data/example_0.inp b/tests/data/example_0.inp
new file mode 100644
index 0000000..3be1e96
--- /dev/null
+++ b/tests/data/example_0.inp
@@ -0,0 +1,481 @@
+[TITLE]
+EPANET Example Network 3
+Example showing how the percent of Lake water in a dual-source
+system changes over time.
+
+[JUNCTIONS]
+;ID Elev Demand Pattern
+ 10 147 0 ;
+ 15 32 1 3 ;
+ 20 129 0 ;
+ 35 12.5 1 4 ;
+ 40 131.9 0 ;
+ 50 116.5 0 ;
+ 60 0 0 ;
+ 601 0 0 ;
+ 61 0 0 ;
+ 101 42 189.95 ;
+ 103 43 133.2 ;
+ 105 28.5 135.37 ;
+ 107 22 54.64 ;
+ 109 20.3 231.4 ;
+ 111 10 141.94 ;
+ 113 2 20.01 ;
+ 115 14 52.1 ;
+ 117 13.6 117.71 ;
+ 119 2 176.13 ;
+ 120 0 0 ;
+ 121 -2 41.63 ;
+ 123 11 1 2 ;
+ 125 11 45.6 ;
+ 127 56 17.66 ;
+ 129 51 0 ;
+ 131 6 42.75 ;
+ 139 31 5.89 ;
+ 141 4 9.85 ;
+ 143 -4.5 6.2 ;
+ 145 1 27.63 ;
+ 147 18.5 8.55 ;
+ 149 16 27.07 ;
+ 151 33.5 144.48 ;
+ 153 66.2 44.17 ;
+ 157 13.1 51.79 ;
+ 159 6 41.32 ;
+ 161 4 15.8 ;
+ 163 5 9.42 ;
+ 164 5 0 ;
+ 166 -2 2.6 ;
+ 167 -5 14.56 ;
+ 169 -5 0 ;
+ 171 -4 39.34 ;
+ 173 -4 0 ;
+ 177 8 58.17 ;
+ 179 8 0 ;
+ 181 8 0 ;
+ 183 11 0 ;
+ 184 16 0 ;
+ 185 16 25.65 ;
+ 187 12.5 0 ;
+ 189 4 107.92 ;
+ 191 25 81.9 ;
+ 193 18 71.31 ;
+ 195 15.5 0 ;
+ 197 23 17.04 ;
+ 199 -2 119.32 ;
+ 201 0.1 44.61 ;
+ 203 2 1 5 ;
+ 204 21 0 ;
+ 205 21 65.36 ;
+ 206 1 0 ;
+ 207 9 69.39 ;
+ 208 16 0 ;
+ 209 -2 0.87 ;
+ 211 7 8.67 ;
+ 213 7 13.94 ;
+ 215 7 92.19 ;
+ 217 6 24.22 ;
+ 219 4 41.32 ;
+ 225 8 22.8 ;
+ 229 10.5 64.18 ;
+ 231 5 16.48 ;
+ 237 14 15.61 ;
+ 239 13 44.61 ;
+ 241 13 0 ;
+ 243 14 4.34 ;
+ 247 18 70.38 ;
+ 249 18 0 ;
+ 251 30 24.16 ;
+ 253 36 54.52 ;
+ 255 27 40.39 ;
+ 257 17 0 ;
+ 259 25 0 ;
+ 261 0 0 ;
+ 263 0 0 ;
+ 265 0 0 ;
+ 267 21 0 ;
+ 269 0 0 ;
+ 271 6 0 ;
+ 273 8 0 ;
+ 275 10 0 ;
+
+[RESERVOIRS]
+;ID Head Pattern
+ River 220.0 ;
+ Lake 167.0 ;
+
+[TANKS]
+;ID Elevation InitLevel MinLevel MaxLevel Diameter MinVol VolCurve
+ 1 131.9 13.1 .1 32.1 85 0 ;
+ 2 116.5 23.5 6.5 40.3 50 0 ;
+ 3 129.0 29.0 4.0 35.5 164 0 ;
+
+[PIPES]
+;ID Node1 Node2 Length Diameter Roughness MinorLoss Status
+ 20 3 20 99 99 199 0 Open ;
+ 40 1 40 99 99 199 0 Open ;
+ 50 2 50 99 99 199 0 Open ;
+ 60 River 60 1231 24 140 0 Open ;
+ 101 10 101 14200 18 110 0 Open ;
+ 103 101 103 1350 16 130 0 Open ;
+ 105 101 105 2540 12 130 0 Open ;
+ 107 105 107 1470 12 130 0 Open ;
+ 109 103 109 3940 16 130 0 Open ;
+ 111 109 111 2000 12 130 0 Open ;
+ 112 115 111 1160 12 130 0 Open ;
+ 113 111 113 1680 12 130 0 Open ;
+ 114 115 113 2000 8 130 0 Open ;
+ 115 107 115 1950 8 130 0 Open ;
+ 116 113 193 1660 12 130 0 Open ;
+ 117 263 105 2725 12 130 0 Open ;
+ 119 115 117 2180 12 130 0 Open ;
+ 120 119 120 730 12 130 0 Open ;
+ 121 120 117 1870 12 130 0 Open ;
+ 122 121 120 2050 8 130 0 Open ;
+ 123 121 119 2000 30 141 0 Open ;
+ 125 123 121 1500 30 141 0 Open ;
+ 129 121 125 930 24 130 0 Open ;
+ 131 125 127 3240 24 130 0 Open ;
+ 133 20 127 785 20 130 0 Open ;
+ 135 127 129 900 24 130 0 Open ;
+ 137 129 131 6480 16 130 0 Open ;
+ 145 129 139 2750 8 130 0 Open ;
+ 147 139 141 2050 8 130 0 Open ;
+ 149 143 141 1400 8 130 0 Open ;
+ 151 15 143 1650 8 130 0 Open ;
+ 153 145 141 3510 12 130 0 Open ;
+ 155 147 145 2200 12 130 0 Open ;
+ 159 147 149 880 12 130 0 Open ;
+ 161 149 151 1020 8 130 0 Open ;
+ 163 151 153 1170 12 130 0 Open ;
+ 169 125 153 4560 8 130 0 Open ;
+ 171 119 151 3460 12 130 0 Open ;
+ 173 119 157 2080 30 141 0 Open ;
+ 175 157 159 2910 30 141 0 Open ;
+ 177 159 161 2000 30 141 0 Open ;
+ 179 161 163 430 30 141 0 Open ;
+ 180 163 164 150 14 130 0 Open ;
+ 181 164 166 490 14 130 0 Open ;
+ 183 265 169 590 30 141 0 Open ;
+ 185 167 169 60 8 130 0 Open ;
+ 186 187 204 99.9 8 130 0 Open ;
+ 187 169 171 1270 30 141 0 Open ;
+ 189 171 173 50 30 141 0 Open ;
+ 191 271 171 760 24 130 0 Open ;
+ 193 35 181 30 24 130 0 Open ;
+ 195 181 177 30 12 130 0 Open ;
+ 197 177 179 30 12 130 0 Open ;
+ 199 179 183 210 12 130 0 Open ;
+ 201 40 179 1190 12 130 0 Open ;
+ 202 185 184 99.9 8 130 0 Open ;
+ 203 183 185 510 8 130 0 Open ;
+ 204 184 205 4530. 12 130 0 Open ;
+ 205 204 185 1325. 12 130 0 Open ;
+ 207 189 183 1350 12 130 0 Open ;
+ 209 189 187 500 8 130 0 Open ;
+ 211 169 269 646 12 130 0 Open ;
+ 213 191 187 2560 12 130 0 Open ;
+ 215 267 189 1230 12 130 0 Open ;
+ 217 191 193 520 12 130 0 Open ;
+ 219 193 195 360 12 130 0 Open ;
+ 221 161 195 2300 8 130 0 Open ;
+ 223 197 191 1150 12 130 0 Open ;
+ 225 111 197 2790 12 130 0 Open ;
+ 229 173 199 4000 24 141 0 Open ;
+ 231 199 201 630 24 141 0 Open ;
+ 233 201 203 120 24 130 0 Open ;
+ 235 199 273 725 12 130 0 Open ;
+ 237 205 207 1200 12 130 0 Open ;
+ 238 207 206 450 12 130 0 Open ;
+ 239 275 207 1430 12 130 0 Open ;
+ 240 206 208 510 12 130 0 Open ;
+ 241 208 209 885 12 130 0 Open ;
+ 243 209 211 1210 16 130 0 Open ;
+ 245 211 213 990 16 130 0 Open ;
+ 247 213 215 4285 16 130 0 Open ;
+ 249 215 217 1660 16 130 0 Open ;
+ 251 217 219 2050 14 130 0 Open ;
+ 257 217 225 1560 12 130 0 Open ;
+ 261 213 229 2200 8 130 0 Open ;
+ 263 229 231 1960 12 130 0 Open ;
+ 269 211 237 2080 12 130 0 Open ;
+ 271 237 229 790 8 130 0 Open ;
+ 273 237 239 510 12 130 0 Open ;
+ 275 239 241 35 12 130 0 Open ;
+ 277 241 243 2200 12 130 0 Open ;
+ 281 241 247 445 10 130 0 Open ;
+ 283 239 249 430 12 130 0 Open ;
+ 285 247 249 10 12 130 0 Open ;
+ 287 247 255 1390 10 130 0 Open ;
+ 289 50 255 925 10 130 0 Open ;
+ 291 255 253 1100 10 130 0 Open ;
+ 293 255 251 1100 8 130 0 Open ;
+ 295 249 251 1450 12 130 0 Open ;
+ 297 120 257 645 8 130 0 Open ;
+ 299 257 259 350 8 130 0 Open ;
+ 301 259 263 1400 8 130 0 Open ;
+ 303 257 261 1400 8 130 0 Open ;
+ 305 117 261 645 12 130 0 Open ;
+ 307 261 263 350 12 130 0 Open ;
+ 309 265 267 1580 8 130 0 Open ;
+ 311 193 267 1170 12 130 0 Open ;
+ 313 269 189 646 12 130 0 Open ;
+ 315 181 271 260 24 130 0 Open ;
+ 317 273 275 2230 8 130 0 Open ;
+ 319 273 205 645 12 130 0 Open ;
+ 321 163 265 1200 30 141 0 Open ;
+ 323 201 275 300 12 130 0 Open ;
+ 325 269 271 1290 8 130 0 Open ;
+ 329 61 123 45500 30 140 0 Open ;
+ 330 60 601 1 30 140 0 Closed ;
+ 333 601 61 1 30 140 0 Open ;
+
+[PUMPS]
+;ID Node1 Node2 Parameters
+ 10 Lake 10 HEAD 1 ;
+ 335 60 61 HEAD 2 ;
+
+[VALVES]
+;ID Node1 Node2 Diameter Type Setting MinorLoss
+
+[TAGS]
+
+[DEMANDS]
+;Junction Demand Pattern Category
+
+[STATUS]
+;ID Status/Setting
+ 10 Closed
+
+[PATTERNS]
+;ID Multipliers
+;General Default Demand Pattern
+ 1 1.34 1.94 1.46 1.44 .76 .92
+ 1 .85 1.07 .96 1.1 1.08 1.19
+ 1 1.16 1.08 .96 .83 .79 .74
+ 1 .64 .64 .85 .96 1.24 1.67
+;Demand Pattern for Node 123
+ 2 0 0 0 0 0 1219
+ 2 0 0 0 1866 1836 1818
+ 2 1818 1822 1822 1817 1824 1816
+ 2 1833 1817 1830 1814 1840 1859
+;Demand Pattern for Node 15
+ 3 620 620 620 620 620 360
+ 3 360 0 0 0 0 360
+ 3 360 360 360 360 0 0
+ 3 0 0 0 0 360 360
+;Demand Pattern for Node 35
+ 4 1637 1706 1719 1719 1791 1819
+ 4 1777 1842 1815 1825 1856 1801
+ 4 1819 1733 1664 1620 1613 1620
+ 4 1616 1647 1627 1627 1671 1668
+;Demand Pattern for Node 203
+ 5 4439 4531 4511 4582 4531 4582
+ 5 4572 4613 4643 4643 4592 4613
+ 5 4531 4521 4449 4439 4449 4460
+ 5 4439 4419 4368 4399 4470 4480
+
+[CURVES]
+;ID X-Value Y-Value
+;PUMP: Pump Curve for Pump 10 (Lake Source)
+ 1 0 104.
+ 1 2000. 92.
+ 1 4000. 63.
+;PUMP: Pump Curve for Pump 335 (River Source)
+ 2 0 200.
+ 2 8000. 138.
+ 2 14000. 86.
+
+[CONTROLS]
+;Lake source operates only part of the day
+Link 10 OPEN AT TIME 1
+Link 10 CLOSED AT TIME 15
+
+;Pump 335 controlled by level in Tank 1
+;When pump is closed, bypass pipe is opened
+Link 335 OPEN IF Node 1 BELOW 17.1
+Link 335 CLOSED IF Node 1 ABOVE 19.1
+Link 330 CLOSED IF Node 1 BELOW 17.1
+Link 330 OPEN IF Node 1 ABOVE 19.1
+
+
+[RULES]
+
+[ENERGY]
+ Global Efficiency 75
+ Global Price 0.0
+ Demand Charge 0.0
+
+[EMITTERS]
+;Junction Coefficient
+
+[QUALITY]
+;Node InitQual
+
+[SOURCES]
+;Node Type Quality Pattern
+
+[REACTIONS]
+;Type Pipe/Tank Coefficient
+
+
+[REACTIONS]
+ Order Bulk 1
+ Order Tank 1
+ Order Wall 1
+ Global Bulk 0.0
+ Global Wall 0.0
+ Limiting Potential 0.0
+ Roughness Correlation 0.0
+
+[MIXING]
+;Tank Model
+
+[TIMES]
+ Duration 24:00
+ Hydraulic Timestep 1:00
+ Quality Timestep 0:05
+ Pattern Timestep 1:00
+ Pattern Start 0:00
+ Report Timestep 1:00
+ Report Start 0:00
+ Start ClockTime 12 am
+ Statistic None
+
+[REPORT]
+ Status Yes
+ Summary No
+ Page 0
+
+[OPTIONS]
+ Units GPM
+ Headloss H-W
+ Specific Gravity 1.0
+ Viscosity 1.0
+ Trials 40
+ Accuracy 0.001
+ CHECKFREQ 2
+ MAXCHECK 10
+ DAMPLIMIT 0
+ Unbalanced Continue 10
+ Pattern 1
+ Demand Multiplier 1.0
+ Emitter Exponent 0.5
+ Quality Trace Lake
+ Diffusivity 1.0
+ Tolerance 0.01
+
+[COORDINATES]
+;Node X-Coord Y-Coord
+ 10 9.00 27.85
+ 15 38.68 23.76
+ 20 29.44 26.91
+ 35 25.46 10.52
+ 40 27.02 9.81
+ 50 33.01 3.01
+ 60 23.90 29.94
+ 601 23.00 29.49
+ 61 23.71 29.03
+ 101 13.81 22.94
+ 103 12.96 21.31
+ 105 16.97 21.28
+ 107 18.45 20.46
+ 109 17.64 18.92
+ 111 20.21 17.53
+ 113 22.04 16.61
+ 115 20.98 19.18
+ 117 21.69 21.28
+ 119 23.70 22.76
+ 120 22.08 23.10
+ 121 23.54 25.50
+ 123 23.37 27.31
+ 125 24.59 25.64
+ 127 29.29 26.40
+ 129 30.32 26.39
+ 131 37.89 29.55
+ 139 33.28 24.54
+ 141 35.68 23.08
+ 143 37.47 21.97
+ 145 33.02 19.29
+ 147 30.24 20.38
+ 149 29.62 20.74
+ 151 28.29 21.39
+ 153 28.13 22.63
+ 157 24.85 20.16
+ 159 23.12 17.50
+ 161 25.10 15.28
+ 163 25.39 14.98
+ 164 25.98 15.14
+ 166 26.48 15.13
+ 167 25.88 12.98
+ 169 25.68 12.74
+ 171 26.65 11.80
+ 173 26.87 11.59
+ 177 25.92 10.59
+ 179 25.71 10.40
+ 181 25.72 10.74
+ 183 25.45 10.18
+ 184 25.15 9.52
+ 185 25.01 9.67
+ 187 23.64 11.04
+ 189 24.15 11.37
+ 191 22.10 14.07
+ 193 22.88 14.35
+ 195 23.18 14.72
+ 197 20.97 15.18
+ 199 29.42 8.44
+ 201 30.89 8.57
+ 203 31.14 8.89
+ 204 23.80 10.90
+ 205 29.20 6.46
+ 206 31.66 6.64
+ 207 31.00 6.61
+ 208 32.54 6.81
+ 209 33.76 6.59
+ 211 34.20 5.54
+ 213 35.26 6.16
+ 215 39.95 8.73
+ 217 42.11 8.67
+ 219 44.86 9.32
+ 225 43.53 7.38
+ 229 36.16 3.49
+ 231 38.38 2.54
+ 237 35.37 3.08
+ 239 35.76 2.31
+ 241 35.87 2.11
+ 243 37.04 0.00
+ 247 35.02 2.05
+ 249 35.02 1.81
+ 251 34.15 1.10
+ 253 32.17 1.88
+ 255 33.51 2.45
+ 257 21.17 23.32
+ 259 20.80 23.40
+ 261 20.79 21.45
+ 263 20.32 21.57
+ 265 25.39 13.60
+ 267 23.38 12.95
+ 269 25.03 12.14
+ 271 25.97 11.00
+ 273 29.16 7.38
+ 275 31.07 8.29
+ River 24.15 31.06
+ Lake 8.00 27.53
+ 1 27.46 9.84
+ 2 32.99 3.45
+ 3 29.41 27.27
+
+[VERTICES]
+;Link X-Coord Y-Coord
+
+[LABELS]
+;X-Coord Y-Coord Label & Anchor Node
+ 8.00 29.42 "LAKE"
+ 25.00 31.10 "RIVER"
+
+[BACKDROP]
+ DIMENSIONS 6.16 -1.55 46.70 32.61
+ UNITS None
+ FILE
+ OFFSET 0.00 0.00
+
+[END]
diff --git a/tests/data/example_1.inp b/tests/data/example_1.inp
new file mode 100644
index 0000000..4df5bbf
--- /dev/null
+++ b/tests/data/example_1.inp
@@ -0,0 +1,178 @@
+[TITLE]
+ EPANET Example Network 1
+A simple example of modeling chlorine decay. Both bulk and
+wall reactions are included.
+
+[JUNCTIONS]
+;ID Elev Demand Pattern
+ 10 710 0 ;
+ 11 710 150 ;
+ 12 700 150 ;
+ 13 695 100 ;
+ 21 700 150 ;
+ 22 695 200 ;
+ 23 690 150 ;
+ 31 700 100 ;
+ 32 710 100 ;
+
+[RESERVOIRS]
+;ID Head Pattern
+ 9 800 ;
+
+[TANKS]
+;ID Elevation InitLevel MinLevel MaxLevel Diameter MinVol VolCurve
+ 2 850 120 100 150 50.5 0 ;
+
+[PIPES]
+;ID Node1 Node2 Length Diameter Roughness MinorLoss Status
+ 10 10 11 10530 18 100 0 Open ;
+ 11 11 12 5280 14 100 0 Open ;
+ 12 12 13 5280 10 100 0 Open ;
+ 21 21 22 5280 10 100 0 Open ;
+ 22 22 23 5280 12 100 0 Open ;
+ 31 31 32 5280 6 100 0 Open ;
+ 110 2 12 200 18 100 0 Open ;
+ 111 11 21 5280 10 100 0 Open ;
+ 112 12 22 5280 12 100 0 Open ;
+ 113 13 23 5280 8 100 0 Open ;
+ 121 21 31 5280 8 100 0 Open ;
+ 122 22 32 5280 6 100 0 Open ;
+
+[PUMPS]
+;ID Node1 Node2 Parameters
+ 9 9 10 HEAD 1 ;
+
+[VALVES]
+;ID Node1 Node2 Diameter Type Setting MinorLoss
+
+[TAGS]
+
+[DEMANDS]
+;Junction Demand Pattern Category
+
+[STATUS]
+;ID Status/Setting
+
+[PATTERNS]
+;ID Multipliers
+;Demand Pattern
+ 1 1.0 1.2 1.4 1.6 1.4 1.2
+ 1 1.0 0.8 0.6 0.4 0.6 0.8
+
+[CURVES]
+;ID X-Value Y-Value
+;PUMP: Pump Curve for Pump 9
+ 1 1500 250
+
+[CONTROLS]
+ LINK 9 OPEN IF NODE 2 BELOW 110
+ LINK 9 CLOSED IF NODE 2 ABOVE 140
+
+
+[RULES]
+
+[ENERGY]
+ Global Efficiency 75
+ Global Price 0.0
+ Demand Charge 0.0
+
+[EMITTERS]
+;Junction Coefficient
+
+[QUALITY]
+;Node InitQual
+ 10 0.5
+ 11 0.5
+ 12 0.5
+ 13 0.5
+ 21 0.5
+ 22 0.5
+ 23 0.5
+ 31 0.5
+ 32 0.5
+ 9 1.0
+ 2 1.0
+
+[SOURCES]
+;Node Type Quality Pattern
+
+[REACTIONS]
+;Type Pipe/Tank Coefficient
+
+
+[REACTIONS]
+ Order Bulk 1
+ Order Tank 1
+ Order Wall 1
+ Global Bulk -.5
+ Global Wall -1
+ Limiting Potential 0.0
+ Roughness Correlation 0.0
+
+[MIXING]
+;Tank Model
+
+[TIMES]
+ Duration 24:00
+ Hydraulic Timestep 1:00
+ Quality Timestep 0:05
+ Pattern Timestep 2:00
+ Pattern Start 0:00
+ Report Timestep 1:00
+ Report Start 0:00
+ Start ClockTime 12 am
+ Statistic None
+
+[REPORT]
+ Status Yes
+ Summary No
+ Page 0
+
+[OPTIONS]
+ Units GPM
+ Headloss H-W
+ Specific Gravity 1.0
+ Viscosity 1.0
+ Trials 40
+ Accuracy 0.001
+ CHECKFREQ 2
+ MAXCHECK 10
+ DAMPLIMIT 0
+ Unbalanced Continue 10
+ Pattern 1
+ Demand Multiplier 1.0
+ Emitter Exponent 0.5
+ Quality Chlorine mg/L
+ Diffusivity 1.0
+ Tolerance 0.01
+
+[COORDINATES]
+;Node X-Coord Y-Coord
+ 10 20.00 70.00
+ 11 30.00 70.00
+ 12 50.00 70.00
+ 13 70.00 70.00
+ 21 30.00 40.00
+ 22 50.00 40.00
+ 23 70.00 40.00
+ 31 30.00 10.00
+ 32 50.00 10.00
+ 9 10.00 70.00
+ 2 50.00 90.00
+
+[VERTICES]
+;Link X-Coord Y-Coord
+
+[LABELS]
+;X-Coord Y-Coord Label & Anchor Node
+ 6.99 73.63 "Source"
+ 13.48 68.13 "Pump"
+ 43.85 91.21 "Tank"
+
+[BACKDROP]
+ DIMENSIONS 7.00 6.00 73.00 94.00
+ UNITS None
+ FILE
+ OFFSET 0.00 0.00
+
+[END]
diff --git a/tests/test_reent.cpp b/tests/test_reent.cpp
new file mode 100644
index 0000000..4ac1d3c
--- /dev/null
+++ b/tests/test_reent.cpp
@@ -0,0 +1,65 @@
+/*
+ * test_reent.cpp
+ *
+ * Created: 8/30/2018
+ * Author: Michael E. Tryby
+ * US EPA - ORD/NRMRL
+ *
+ * Multi-threading / reentrancy test for EPANET Toolkit API.
+*/
+
+#include
+#include
+#include
+
+#include
+
+#include "epanet2.h"
+
+#define NUM_THREADS 2
+
+using namespace std;
+
+void epanet_thread(long i)
+{
+ int errorcode = 0;
+ EN_ProjectHandle ph;
+
+ string prefix = "example_";
+ string suffix = ".inp";
+ string input = prefix + to_string(static_cast(i)) + suffix;
+
+ suffix = ".rpt";
+ string report = prefix + to_string(static_cast(i)) + suffix;
+
+ suffix = ".out";
+ string output = prefix + to_string(static_cast(i)) + suffix;
+
+ printf("Thread #%ld starting EPANET ...\n", i);
+
+ EN_createproject(&ph);
+ errorcode = EN_runproject(ph, input.c_str(), report.c_str(), output.c_str(), NULL);
+ EN_deleteproject(&ph);
+
+ printf("Thread #%ld EPANET done. Status = %d\n", i, errorcode);
+}
+
+int main(int argc, char *argv[])
+{
+ long i;
+ boost::thread *threads[NUM_THREADS];
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ threads[i] = new boost::thread(epanet_thread, i);
+ printf("Main: creating thread %ld.\n", i);
+ }
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ threads[i]->join();
+ printf("Main: joining thread %ld.\n", i);
+ delete threads[i];
+ }
+
+ printf("Main: program completed. Exiting.\n");
+ return(0);
+}